Merge pull request #113108 from KoBeWi/Thock

Change Theme to EditorDock and add `closable` property
This commit is contained in:
Rémi Verschelde 2025-12-03 09:49:31 +01:00
commit 11ffcb3ef7
No known key found for this signature in database
GPG key ID: C3336907360768E1
7 changed files with 55 additions and 22 deletions

View file

@ -85,6 +85,9 @@
The available layouts for this dock, as a bitmask. By default, the dock allows vertical and floating layouts. The available layouts for this dock, as a bitmask. By default, the dock allows vertical and floating layouts.
</member> </member>
<member name="clip_contents" type="bool" setter="set_clip_contents" getter="is_clipping_contents" overrides="Control" default="true" /> <member name="clip_contents" type="bool" setter="set_clip_contents" getter="is_clipping_contents" overrides="Control" default="true" />
<member name="closable" type="bool" setter="set_closable" getter="is_closable" default="false">
If [code]true[/code], the dock can be closed with the Close button in the context popup. Docks with [member global] enabled are always closable.
</member>
<member name="default_slot" type="int" setter="set_default_slot" getter="get_default_slot" enum="EditorPlugin.DockSlot" default="-1"> <member name="default_slot" type="int" setter="set_default_slot" getter="get_default_slot" enum="EditorPlugin.DockSlot" default="-1">
The default dock slot used when adding the dock with [method EditorPlugin.add_dock]. The default dock slot used when adding the dock with [method EditorPlugin.add_dock].
After the dock is added, it can be moved to a different slot and the editor will automatically remember its position between sessions. If you remove and re-add the dock, it will be reset to default. After the dock is added, it can be moved to a different slot and the editor will automatically remember its position between sessions. If you remove and re-add the dock, it will be reset to default.
@ -99,7 +102,7 @@
If [code]true[/code], the dock will always display an icon, regardless of [member EditorSettings.interface/editor/dock_tab_style] or [member EditorSettings.interface/editor/bottom_dock_tab_style]. If [code]true[/code], the dock will always display an icon, regardless of [member EditorSettings.interface/editor/dock_tab_style] or [member EditorSettings.interface/editor/bottom_dock_tab_style].
</member> </member>
<member name="global" type="bool" setter="set_global" getter="is_global" default="true"> <member name="global" type="bool" setter="set_global" getter="is_global" default="true">
If [code]true[/code], the dock appears in the [b]Editor &gt; Editor Docks[/b] menu and can be closed. Non-global docks can still be closed using [method close]. If [code]true[/code], the dock appears in the [b]Editor &gt; Editor Docks[/b] menu and can be closed. Non-global docks can still be closed using [method close] or when [member closable] is [code]true[/code].
</member> </member>
<member name="icon_name" type="StringName" setter="set_icon_name" getter="get_icon_name" default="&amp;&quot;&quot;"> <member name="icon_name" type="StringName" setter="set_icon_name" getter="get_icon_name" default="&amp;&quot;&quot;">
The icon for the dock, as a name from the [code]EditorIcons[/code] theme type in the editor theme. You can find the list of available icons [url=https://godot-editor-icons.github.io/]here[/url]. The icon for the dock, as a name from the [code]EditorIcons[/code] theme type in the editor theme. You can find the list of available icons [url=https://godot-editor-icons.github.io/]here[/url].
@ -117,6 +120,13 @@
If [code]true[/code], the dock is not automatically opened or closed when loading an editor layout, only moved. It also can't be opened using a shortcut. This is meant for docks that are opened and closed in specific cases, such as when selecting a [TileMap] or [AnimationTree] node. If [code]true[/code], the dock is not automatically opened or closed when loading an editor layout, only moved. It also can't be opened using a shortcut. This is meant for docks that are opened and closed in specific cases, such as when selecting a [TileMap] or [AnimationTree] node.
</member> </member>
</members> </members>
<signals>
<signal name="closed">
<description>
Emitted when the dock is closed with the Close button in the context popup, before it's removed from its parent. See [member closable].
</description>
</signal>
</signals>
<constants> <constants>
<constant name="DOCK_LAYOUT_VERTICAL" value="1" enum="DockLayout" is_bitfield="true"> <constant name="DOCK_LAYOUT_VERTICAL" value="1" enum="DockLayout" is_bitfield="true">
Allows placing the dock in the vertical dock slots on either side of the editor. Allows placing the dock in the vertical dock slots on either side of the editor.

View file

@ -60,6 +60,10 @@ void EditorDock::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_transient"), &EditorDock::is_transient); ClassDB::bind_method(D_METHOD("is_transient"), &EditorDock::is_transient);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "transient"), "set_transient", "is_transient"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "transient"), "set_transient", "is_transient");
ClassDB::bind_method(D_METHOD("set_closable", "closable"), &EditorDock::set_closable);
ClassDB::bind_method(D_METHOD("is_closable"), &EditorDock::is_closable);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "closable"), "set_closable", "is_closable");
ClassDB::bind_method(D_METHOD("set_icon_name", "icon_name"), &EditorDock::set_icon_name); ClassDB::bind_method(D_METHOD("set_icon_name", "icon_name"), &EditorDock::set_icon_name);
ClassDB::bind_method(D_METHOD("get_icon_name"), &EditorDock::get_icon_name); ClassDB::bind_method(D_METHOD("get_icon_name"), &EditorDock::get_icon_name);
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "icon_name"), "set_icon_name", "get_icon_name"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "icon_name"), "set_icon_name", "get_icon_name");
@ -88,6 +92,8 @@ void EditorDock::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_available_layouts"), &EditorDock::get_available_layouts); ClassDB::bind_method(D_METHOD("get_available_layouts"), &EditorDock::get_available_layouts);
ADD_PROPERTY(PropertyInfo(Variant::INT, "available_layouts", PROPERTY_HINT_FLAGS, "Vertical:1,Horizontal:2,Floating:3"), "set_available_layouts", "get_available_layouts"); ADD_PROPERTY(PropertyInfo(Variant::INT, "available_layouts", PROPERTY_HINT_FLAGS, "Vertical:1,Horizontal:2,Floating:3"), "set_available_layouts", "get_available_layouts");
ADD_SIGNAL(MethodInfo("closed"));
BIND_BITFIELD_FLAG(DOCK_LAYOUT_VERTICAL); BIND_BITFIELD_FLAG(DOCK_LAYOUT_VERTICAL);
BIND_BITFIELD_FLAG(DOCK_LAYOUT_HORIZONTAL); BIND_BITFIELD_FLAG(DOCK_LAYOUT_HORIZONTAL);
BIND_BITFIELD_FLAG(DOCK_LAYOUT_FLOATING); BIND_BITFIELD_FLAG(DOCK_LAYOUT_FLOATING);

View file

@ -64,6 +64,7 @@ private:
DockConstants::DockSlot default_slot = DockConstants::DOCK_SLOT_NONE; DockConstants::DockSlot default_slot = DockConstants::DOCK_SLOT_NONE;
bool global = true; bool global = true;
bool transient = false; bool transient = false;
bool closable = false;
BitField<DockLayout> available_layouts = DOCK_LAYOUT_VERTICAL | DOCK_LAYOUT_FLOATING; BitField<DockLayout> available_layouts = DOCK_LAYOUT_VERTICAL | DOCK_LAYOUT_FLOATING;
@ -102,6 +103,9 @@ public:
void set_transient(bool p_transient) { transient = p_transient; } void set_transient(bool p_transient) { transient = p_transient; }
bool is_transient() const { return transient; } bool is_transient() const { return transient; }
void set_closable(bool p_closable) { closable = p_closable; }
bool is_closable() const { return closable; }
void set_icon_name(const StringName &p_name); void set_icon_name(const StringName &p_name);
StringName get_icon_name() const { return icon_name; } StringName get_icon_name() const { return icon_name; }

View file

@ -1060,6 +1060,7 @@ void DockContextPopup::_tab_move_right() {
void DockContextPopup::_close_dock() { void DockContextPopup::_close_dock() {
hide(); hide();
context_dock->emit_signal("closed");
dock_manager->close_dock(context_dock); dock_manager->close_dock(context_dock);
} }
@ -1215,7 +1216,7 @@ void DockContextPopup::_dock_select_draw() {
void DockContextPopup::_update_buttons() { void DockContextPopup::_update_buttons() {
TabContainer *context_tab_container = dock_manager->get_dock_tab_container(context_dock); TabContainer *context_tab_container = dock_manager->get_dock_tab_container(context_dock);
if (context_dock->global) { if (context_dock->global || context_dock->closable) {
close_button->set_tooltip_text(TTRC("Close this dock.")); close_button->set_tooltip_text(TTRC("Close this dock."));
close_button->set_disabled(false); close_button->set_disabled(false);
} else { } else {

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="m8 1c-.75 1.305-1.654 2.427-2.5 3.5-1 1.208-1.865 2.349-2.346 3.5-.24.57-.404 1.148-.404 1.75s.126 1.2.322 1.75c.723 2.035 2.646 3.5 4.928 3.5s4.205-1.465 4.928-3.5c.196-.55.322-1.148.322-1.75s-.164-1.18-.404-1.75c-.481-1.151-1.346-2.292-2.346-3.5-.846-1.073-1.747-2.195-2.5-3.5z"/></svg>

After

Width:  |  Height:  |  Size: 376 B

View file

@ -31,6 +31,7 @@
#include "theme_editor_plugin.h" #include "theme_editor_plugin.h"
#include "editor/doc/editor_help.h" #include "editor/doc/editor_help.h"
#include "editor/docks/editor_dock_manager.h"
#include "editor/docks/filesystem_dock.h" #include "editor/docks/filesystem_dock.h"
#include "editor/docks/inspector_dock.h" #include "editor/docks/inspector_dock.h"
#include "editor/editor_node.h" #include "editor/editor_node.h"
@ -3725,13 +3726,15 @@ void ThemeEditor::_theme_edit_button_cbk() {
} }
void ThemeEditor::_theme_close_button_cbk() { void ThemeEditor::_theme_close_button_cbk() {
plugin->make_visible(false); // Enables auto hide. close();
_dock_closed_cbk();
}
void ThemeEditor::_dock_closed_cbk() {
if (theme.is_valid() && InspectorDock::get_inspector_singleton()->get_edited_object() == theme.ptr()) { if (theme.is_valid() && InspectorDock::get_inspector_singleton()->get_edited_object() == theme.ptr()) {
EditorNode::get_singleton()->push_item(nullptr); EditorNode::get_singleton()->push_item(nullptr);
} else {
theme = Ref<Theme>();
EditorNode::get_singleton()->hide_unused_editors(plugin);
} }
theme = Ref<Theme>();
} }
void ThemeEditor::_scene_closed(const String &p_path) { void ThemeEditor::_scene_closed(const String &p_path) {
@ -3919,6 +3922,7 @@ void ThemeEditor::_preview_tabs_resized() {
void ThemeEditor::_notification(int p_what) { void ThemeEditor::_notification(int p_what) {
switch (p_what) { switch (p_what) {
case NOTIFICATION_READY: { case NOTIFICATION_READY: {
connect("closed", callable_mp(this, &ThemeEditor::_dock_closed_cbk));
EditorNode::get_singleton()->connect("scene_closed", callable_mp(this, &ThemeEditor::_scene_closed)); EditorNode::get_singleton()->connect("scene_closed", callable_mp(this, &ThemeEditor::_scene_closed));
EditorNode::get_singleton()->connect("resource_saved", callable_mp(this, &ThemeEditor::_resource_saved)); EditorNode::get_singleton()->connect("resource_saved", callable_mp(this, &ThemeEditor::_resource_saved));
FileSystemDock::get_singleton()->connect("files_moved", callable_mp(this, &ThemeEditor::_files_moved)); FileSystemDock::get_singleton()->connect("files_moved", callable_mp(this, &ThemeEditor::_files_moved));
@ -3947,8 +3951,21 @@ void ThemeEditor::_notification(int p_what) {
} }
ThemeEditor::ThemeEditor() { ThemeEditor::ThemeEditor() {
set_name(TTRC("Theme"));
set_icon_name("ThemeDock");
set_dock_shortcut(ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_theme_bottom_panel", TTRC("Toggle Theme Dock")));
set_default_slot(DockConstants::DOCK_SLOT_BOTTOM);
set_available_layouts(EditorDock::DOCK_LAYOUT_HORIZONTAL | EditorDock::DOCK_LAYOUT_FLOATING);
set_global(false);
set_transient(true);
set_closable(true);
set_custom_minimum_size(Size2(0, 200 * EDSCALE));
VBoxContainer *content_vb = memnew(VBoxContainer);
add_child(content_vb);
HBoxContainer *top_menu = memnew(HBoxContainer); HBoxContainer *top_menu = memnew(HBoxContainer);
add_child(top_menu); content_vb->add_child(top_menu);
Label *theme_label = memnew(Label); Label *theme_label = memnew(Label);
theme_label->set_text(TTRC("Theme:")); theme_label->set_text(TTRC("Theme:"));
@ -3999,7 +4016,7 @@ ThemeEditor::ThemeEditor() {
HSplitContainer *main_hs = memnew(HSplitContainer); HSplitContainer *main_hs = memnew(HSplitContainer);
main_hs->set_v_size_flags(SIZE_EXPAND_FILL); main_hs->set_v_size_flags(SIZE_EXPAND_FILL);
add_child(main_hs); content_vb->add_child(main_hs);
main_hs->set_split_offset(520 * EDSCALE); main_hs->set_split_offset(520 * EDSCALE);
@ -4067,14 +4084,9 @@ bool ThemeEditorPlugin::handles(Object *p_object) const {
void ThemeEditorPlugin::make_visible(bool p_visible) { void ThemeEditorPlugin::make_visible(bool p_visible) {
if (p_visible) { if (p_visible) {
button->show(); theme_editor->make_visible();
EditorNode::get_bottom_panel()->make_item_visible(theme_editor);
} else { } else {
if (theme_editor->is_visible_in_tree()) { theme_editor->close();
EditorNode::get_bottom_panel()->hide_bottom_panel();
}
button->hide();
} }
} }
@ -4085,8 +4097,6 @@ bool ThemeEditorPlugin::can_auto_hide() const {
ThemeEditorPlugin::ThemeEditorPlugin() { ThemeEditorPlugin::ThemeEditorPlugin() {
theme_editor = memnew(ThemeEditor); theme_editor = memnew(ThemeEditor);
theme_editor->plugin = this; theme_editor->plugin = this;
theme_editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE); EditorDockManager::get_singleton()->add_dock(theme_editor);
theme_editor->close();
button = EditorNode::get_bottom_panel()->add_item(TTRC("Theme"), theme_editor, ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_theme_bottom_panel", TTRC("Toggle Theme Bottom Panel")));
button->hide();
} }

View file

@ -30,6 +30,7 @@
#pragma once #pragma once
#include "editor/docks/editor_dock.h"
#include "editor/plugins/editor_plugin.h" #include "editor/plugins/editor_plugin.h"
#include "editor/scene/gui/theme_editor_preview.h" #include "editor/scene/gui/theme_editor_preview.h"
#include "scene/gui/dialogs.h" #include "scene/gui/dialogs.h"
@ -432,8 +433,8 @@ public:
ThemeTypeEditor(); ThemeTypeEditor();
}; };
class ThemeEditor : public VBoxContainer { class ThemeEditor : public EditorDock {
GDCLASS(ThemeEditor, VBoxContainer); GDCLASS(ThemeEditor, EditorDock);
friend class ThemeEditorPlugin; friend class ThemeEditorPlugin;
ThemeEditorPlugin *plugin = nullptr; ThemeEditorPlugin *plugin = nullptr;
@ -457,6 +458,7 @@ class ThemeEditor : public VBoxContainer {
void _theme_save_button_cbk(bool p_save_as); void _theme_save_button_cbk(bool p_save_as);
void _theme_edit_button_cbk(); void _theme_edit_button_cbk();
void _theme_close_button_cbk(); void _theme_close_button_cbk();
void _dock_closed_cbk();
void _scene_closed(const String &p_path); void _scene_closed(const String &p_path);
void _resource_saved(const Ref<Resource> &p_resource); void _resource_saved(const Ref<Resource> &p_resource);
void _files_moved(const String &p_old_path, const String &p_new_path); void _files_moved(const String &p_old_path, const String &p_new_path);
@ -489,7 +491,6 @@ class ThemeEditorPlugin : public EditorPlugin {
GDCLASS(ThemeEditorPlugin, EditorPlugin); GDCLASS(ThemeEditorPlugin, EditorPlugin);
ThemeEditor *theme_editor = nullptr; ThemeEditor *theme_editor = nullptr;
Button *button = nullptr;
public: public:
virtual String get_plugin_name() const override { return "Theme"; } virtual String get_plugin_name() const override { return "Theme"; }