From 67735cf213009c57e617f2dee7e1d7b0b8924366 Mon Sep 17 00:00:00 2001 From: kobewi Date: Sat, 15 Nov 2025 13:31:08 +0100 Subject: [PATCH] Make bottom panel into available dock slot --- doc/classes/EditorDock.xml | 35 +- doc/classes/EditorPlugin.xml | 9 +- editor/debugger/editor_debugger_node.cpp | 50 +-- editor/debugger/editor_debugger_node.h | 1 + editor/docks/dock_constants.h | 55 +++ editor/docks/editor_dock.cpp | 63 ++- editor/docks/editor_dock.h | 38 +- editor/docks/editor_dock_manager.cpp | 402 +++++++++--------- editor/docks/editor_dock_manager.h | 54 ++- editor/docks/filesystem_dock.cpp | 23 +- editor/docks/filesystem_dock.h | 4 - editor/docks/history_dock.cpp | 2 +- editor/docks/import_dock.cpp | 2 +- editor/docks/inspector_dock.cpp | 2 +- editor/docks/node_dock.cpp | 2 +- editor/docks/scene_tree_dock.cpp | 2 +- editor/editor_log.cpp | 9 +- editor/editor_node.cpp | 64 +-- editor/gui/editor_bottom_panel.cpp | 114 ++--- editor/gui/editor_bottom_panel.h | 9 +- editor/plugins/editor_plugin.cpp | 11 +- editor/plugins/editor_plugin.h | 26 +- editor/scene/gui/theme_editor_plugin.cpp | 12 +- editor/scene/gui/theme_editor_preview.cpp | 6 +- editor/script/find_in_files.cpp | 13 + editor/script/find_in_files.h | 1 + editor/script/script_editor_plugin.cpp | 1 - .../version_control_editor_plugin.cpp | 2 +- .../gridmap/editor/grid_map_editor_plugin.cpp | 3 +- scene/main/canvas_item.cpp | 1 - 30 files changed, 594 insertions(+), 422 deletions(-) create mode 100644 editor/docks/dock_constants.h diff --git a/doc/classes/EditorDock.xml b/doc/classes/EditorDock.xml index 5067e79ae8a..ac11bc3c358 100644 --- a/doc/classes/EditorDock.xml +++ b/doc/classes/EditorDock.xml @@ -61,11 +61,22 @@ [/codeblock] + + + + Closes the dock, making its tab hidden. + + + + + + Opens the dock. It will appear in the last used dock slot. If the dock has no default slot, it will be opened floating. + + - - The available layouts for this dock, as a bitmask. - If you want to make all layouts available, use [code]available_layouts = DOCK_LAYOUT_VERTICAL | DOCK_LAYOUT_HORIZONTAL[/code]. + + The available layouts for this dock, as a bitmask. By default, the dock allows vertical and floating layouts. @@ -78,6 +89,9 @@ The shortcut used to open the dock. This property can only be set before this dock is added via [method EditorPlugin.add_dock]. + + If [code]true[/code], the dock appears in the [b]Editor > Editor Docks[/b] menu and can be closed. Non-global docks can still be closed using [method close]. + 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]. @@ -87,14 +101,25 @@ The title of the dock's tab. If empty, the dock's [member Node.name] will be used. If the name is auto-generated (contains [code]@[/code]), the first child's name will be used instead. + + The color of the dock tab's title. If its alpha is [code]0.0[/code], the default font color will be used. + + + 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. + Allows placing the dock in the vertical dock slots on either side of the editor. - [b]Note:[/b] Currently this flag has no effect because the bottom panel is not a proper dock slot. This means that the dock can always be vertical. - Allows placing the dock in the editor's bottom panel. Implement [method _update_layout] to handle changing layouts. + Allows placing the dock in the editor's bottom panel. + + + Allows making the dock floating (opened as a separate window). + + + Allows placing the dock in all available slots. diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml index 2bc6cf5b379..69b29aafaaa 100644 --- a/doc/classes/EditorPlugin.xml +++ b/doc/classes/EditorPlugin.xml @@ -424,7 +424,7 @@ [b]Note:[/b] A plugin instance can belong only to a single context menu slot. - + @@ -662,7 +662,7 @@ Removes the specified context menu plugin. - + @@ -910,7 +910,10 @@ Dock slot, right side, bottom-right (empty in default layout). - + + Bottom panel. + + Represents the size of the [enum DockSlot] enum. diff --git a/editor/debugger/editor_debugger_node.cpp b/editor/debugger/editor_debugger_node.cpp index b162347edfc..8f934c2ffd7 100644 --- a/editor/debugger/editor_debugger_node.cpp +++ b/editor/debugger/editor_debugger_node.cpp @@ -65,12 +65,6 @@ EditorDebuggerNode::EditorDebuggerNode() { singleton = this; } - Ref bottom_panel_margins = EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles)); - add_theme_constant_override("margin_top", -bottom_panel_margins->get_margin(SIDE_TOP)); - add_theme_constant_override("margin_left", -bottom_panel_margins->get_margin(SIDE_LEFT)); - add_theme_constant_override("margin_right", -bottom_panel_margins->get_margin(SIDE_RIGHT)); - add_theme_constant_override("margin_bottom", -bottom_panel_margins->get_margin(SIDE_BOTTOM)); - tabs = memnew(TabContainer); tabs->set_tabs_visible(false); tabs->connect("tab_changed", callable_mp(this, &EditorDebuggerNode::_debugger_changed)); @@ -332,17 +326,19 @@ void EditorDebuggerNode::_notification(int p_what) { if (tabs->get_tab_count() > 1) { tabs->add_theme_style_override(SceneStringName(panel), EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("DebuggerPanel"), EditorStringName(EditorStyles))); } - - Ref bottom_panel_margins = EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles)); - add_theme_constant_override("margin_top", -bottom_panel_margins->get_margin(SIDE_TOP)); - add_theme_constant_override("margin_left", -bottom_panel_margins->get_margin(SIDE_LEFT)); - add_theme_constant_override("margin_right", -bottom_panel_margins->get_margin(SIDE_RIGHT)); - add_theme_constant_override("margin_bottom", -bottom_panel_margins->get_margin(SIDE_BOTTOM)); + _update_margins(); remote_scene_tree->update_icon_max_width(); } break; case NOTIFICATION_READY: { + // TODO: Replace this hack once EditorDebuggerNode is converted to a dock. It should be in the constructor. + EditorDock *parent = Object::cast_to(get_parent()); + if (parent) { + parent->set_clip_contents(false); + _update_margins(); + } + _update_debug_options(); initializing = false; } break; @@ -444,35 +440,41 @@ void EditorDebuggerNode::_update_errors() { last_error_count = error_count; last_warning_count = warning_count; - // TODO: Replace logic when EditorDock class is merged to be more flexible. - TabContainer *parent = Object::cast_to(get_parent()); + // TODO: Replace this hack once EditorDebuggerNode is converted to a dock. + EditorDock *parent = Object::cast_to(get_parent()); if (!parent) { return; } - int idx = parent->get_tab_idx_from_control(this); - if (error_count == 0 && warning_count == 0) { set_name(TTR("Debugger")); - parent->set_tab_icon(idx, Ref()); - parent->get_tab_bar()->set_font_color_override_all(idx, Color(0, 0, 0, 0)); + parent->set_dock_icon(Ref()); + parent->set_title_color(Color(0, 0, 0, 0)); } else { set_name(TTR("Debugger") + " (" + itos(error_count + warning_count) + ")"); if (error_count >= 1 && warning_count >= 1) { - parent->set_tab_icon(idx, get_editor_theme_icon(SNAME("ErrorWarning"))); + parent->set_dock_icon(get_editor_theme_icon(SNAME("ErrorWarning"))); // Use error color to represent the highest level of severity reported. - parent->get_tab_bar()->set_font_color_override_all(idx, get_theme_color(SNAME("error_color"), EditorStringName(Editor))); + parent->set_title_color(get_theme_color(SNAME("error_color"), EditorStringName(Editor))); } else if (error_count >= 1) { - parent->set_tab_icon(idx, get_editor_theme_icon(SNAME("Error"))); - parent->get_tab_bar()->set_font_color_override_all(idx, get_theme_color(SNAME("error_color"), EditorStringName(Editor))); + parent->set_dock_icon(get_editor_theme_icon(SNAME("Error"))); + parent->set_title_color(get_theme_color(SNAME("error_color"), EditorStringName(Editor))); } else { - parent->set_tab_icon(idx, get_editor_theme_icon(SNAME("Warning"))); - parent->get_tab_bar()->set_font_color_override_all(idx, get_theme_color(SNAME("warning_color"), EditorStringName(Editor))); + parent->set_dock_icon(get_editor_theme_icon(SNAME("Warning"))); + parent->set_title_color(get_theme_color(SNAME("warning_color"), EditorStringName(Editor))); } } } } +void EditorDebuggerNode::_update_margins() { + Ref bottom_panel_margins = EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles)); + add_theme_constant_override("margin_top", -bottom_panel_margins->get_margin(SIDE_TOP)); + add_theme_constant_override("margin_left", -bottom_panel_margins->get_margin(SIDE_LEFT)); + add_theme_constant_override("margin_right", -bottom_panel_margins->get_margin(SIDE_RIGHT)); + add_theme_constant_override("margin_bottom", -bottom_panel_margins->get_margin(SIDE_BOTTOM)); +} + void EditorDebuggerNode::_debugger_stopped(int p_id) { ScriptEditorDebugger *dbg = get_debugger(p_id); ERR_FAIL_NULL(dbg); diff --git a/editor/debugger/editor_debugger_node.h b/editor/debugger/editor_debugger_node.h index a2b9ab2d84b..6123aa86737 100644 --- a/editor/debugger/editor_debugger_node.h +++ b/editor/debugger/editor_debugger_node.h @@ -121,6 +121,7 @@ private: ScriptEditorDebugger *_add_debugger(); void _update_errors(); + void _update_margins(); friend class DebuggerEditorPlugin; friend class DebugAdapterParser; diff --git a/editor/docks/dock_constants.h b/editor/docks/dock_constants.h new file mode 100644 index 00000000000..3b379fb6998 --- /dev/null +++ b/editor/docks/dock_constants.h @@ -0,0 +1,55 @@ +/**************************************************************************/ +/* dock_constants.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#pragma once + +namespace DockConstants { + +enum DockSlot { + DOCK_SLOT_NONE = -1, + DOCK_SLOT_LEFT_UL, + DOCK_SLOT_LEFT_BL, + DOCK_SLOT_LEFT_UR, + DOCK_SLOT_LEFT_BR, + DOCK_SLOT_RIGHT_UL, + DOCK_SLOT_RIGHT_BL, + DOCK_SLOT_RIGHT_UR, + DOCK_SLOT_RIGHT_BR, + DOCK_SLOT_BOTTOM, + DOCK_SLOT_MAX +}; + +enum DockLayout { + DOCK_LAYOUT_VERTICAL = 1, + DOCK_LAYOUT_HORIZONTAL = 2, + DOCK_LAYOUT_FLOATING = 4, +}; + +}; //namespace DockConstants diff --git a/editor/docks/editor_dock.cpp b/editor/docks/editor_dock.cpp index d231c7639ac..feaa3601081 100644 --- a/editor/docks/editor_dock.cpp +++ b/editor/docks/editor_dock.cpp @@ -32,13 +32,17 @@ #include "core/input/shortcut.h" #include "core/io/config_file.h" +#include "editor/docks/editor_dock_manager.h" void EditorDock::_set_default_slot_bind(EditorPlugin::DockSlot p_slot) { ERR_FAIL_COND(p_slot < EditorPlugin::DOCK_SLOT_NONE || p_slot >= EditorPlugin::DOCK_SLOT_MAX); - default_slot = (EditorDockManager::DockSlot)p_slot; + default_slot = (DockConstants::DockSlot)p_slot; } void EditorDock::_bind_methods() { + ClassDB::bind_method(D_METHOD("open"), &EditorDock::open); + ClassDB::bind_method(D_METHOD("close"), &EditorDock::close); + ClassDB::bind_method(D_METHOD("set_title", "title"), &EditorDock::set_title); ClassDB::bind_method(D_METHOD("get_title"), &EditorDock::get_title); ADD_PROPERTY(PropertyInfo(Variant::STRING, "title"), "set_title", "get_title"); @@ -47,6 +51,14 @@ void EditorDock::_bind_methods() { ClassDB::bind_method(D_METHOD("get_layout_key"), &EditorDock::get_layout_key); ADD_PROPERTY(PropertyInfo(Variant::STRING, "layout_key"), "set_layout_key", "get_layout_key"); + ClassDB::bind_method(D_METHOD("set_global", "global"), &EditorDock::set_global); + ClassDB::bind_method(D_METHOD("is_global"), &EditorDock::is_global); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "global"), "set_global", "is_global"); + + ClassDB::bind_method(D_METHOD("set_transient", "transient"), &EditorDock::set_transient); + ClassDB::bind_method(D_METHOD("is_transient"), &EditorDock::is_transient); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "transient"), "set_transient", "is_transient"); + 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); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "icon_name"), "set_icon_name", "get_icon_name"); @@ -55,6 +67,10 @@ void EditorDock::_bind_methods() { ClassDB::bind_method(D_METHOD("get_dock_icon"), &EditorDock::get_dock_icon); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "dock_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_dock_icon", "get_dock_icon"); + ClassDB::bind_method(D_METHOD("set_title_color", "color"), &EditorDock::set_title_color); + ClassDB::bind_method(D_METHOD("get_title_color"), &EditorDock::get_title_color); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "title_color"), "set_title_color", "get_title_color"); + ClassDB::bind_method(D_METHOD("set_dock_shortcut", "shortcut"), &EditorDock::set_dock_shortcut); ClassDB::bind_method(D_METHOD("get_dock_shortcut"), &EditorDock::get_dock_shortcut); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "dock_shortcut", PROPERTY_HINT_RESOURCE_TYPE, "ShortCut"), "set_dock_shortcut", "get_dock_shortcut"); @@ -65,10 +81,12 @@ void EditorDock::_bind_methods() { ClassDB::bind_method(D_METHOD("set_available_layouts", "layouts"), &EditorDock::set_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"), "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"); BIND_BITFIELD_FLAG(DOCK_LAYOUT_VERTICAL); BIND_BITFIELD_FLAG(DOCK_LAYOUT_HORIZONTAL); + BIND_BITFIELD_FLAG(DOCK_LAYOUT_FLOATING); + BIND_BITFIELD_FLAG(DOCK_LAYOUT_ALL); GDVIRTUAL_BIND(_update_layout, "layout"); GDVIRTUAL_BIND(_save_layout_to_config, "config", "section"); @@ -80,6 +98,18 @@ EditorDock::EditorDock() { add_user_signal(MethodInfo("tab_style_changed")); } +void EditorDock::open() { + if (!is_open) { + EditorDockManager::get_singleton()->open_dock(this); + } +} + +void EditorDock::close() { + if (is_open) { + EditorDockManager::get_singleton()->close_dock(this); + } +} + void EditorDock::set_title(const String &p_title) { if (title == p_title) { return; @@ -88,6 +118,16 @@ void EditorDock::set_title(const String &p_title) { emit_signal("tab_style_changed"); } +void EditorDock::set_global(bool p_global) { + if (global == p_global) { + return; + } + global = p_global; + if (is_inside_tree()) { + EditorDockManager::get_singleton()->update_docks_menu(); + } +} + void EditorDock::set_icon_name(const StringName &p_name) { if (icon_name == p_name) { return; @@ -104,8 +144,23 @@ void EditorDock::set_dock_icon(const Ref &p_icon) { emit_signal("tab_style_changed"); } -void EditorDock::set_default_slot(EditorDockManager::DockSlot p_slot) { - ERR_FAIL_INDEX(p_slot, EditorDockManager::DOCK_SLOT_MAX); +void EditorDock::set_title_color(const Color &p_color) { + if (title_color == p_color) { + return; + } + title_color = p_color; + emit_signal("tab_style_changed"); +} + +void EditorDock::set_dock_shortcut(const Ref &p_shortcut) { + shortcut = p_shortcut; + if (global && is_inside_tree()) { + EditorDockManager::get_singleton()->update_docks_menu(); + } +} + +void EditorDock::set_default_slot(DockConstants::DockSlot p_slot) { + ERR_FAIL_INDEX(p_slot, DockConstants::DOCK_SLOT_MAX); default_slot = p_slot; } diff --git a/editor/docks/editor_dock.h b/editor/docks/editor_dock.h index 6bd58669d58..e4ad6b6a2ff 100644 --- a/editor/docks/editor_dock.h +++ b/editor/docks/editor_dock.h @@ -43,30 +43,34 @@ class EditorDock : public MarginContainer { public: enum DockLayout { - DOCK_LAYOUT_VERTICAL = 1, - DOCK_LAYOUT_HORIZONTAL = 2, + DOCK_LAYOUT_VERTICAL = DockConstants::DOCK_LAYOUT_VERTICAL, + DOCK_LAYOUT_HORIZONTAL = DockConstants::DOCK_LAYOUT_HORIZONTAL, + DOCK_LAYOUT_FLOATING = DockConstants::DOCK_LAYOUT_FLOATING, + DOCK_LAYOUT_ALL = DOCK_LAYOUT_VERTICAL | DOCK_LAYOUT_HORIZONTAL | DOCK_LAYOUT_FLOATING, }; private: friend class EditorDockManager; friend class DockContextPopup; + friend class DockShortcutHandler; String title; String layout_key; StringName icon_name; Ref dock_icon; + Color title_color = Color(0, 0, 0, 0); Ref shortcut; - EditorDockManager::DockSlot default_slot = EditorDockManager::DOCK_SLOT_NONE; + DockConstants::DockSlot default_slot = DockConstants::DOCK_SLOT_NONE; + bool global = true; + bool transient = false; - BitField available_layouts = DOCK_LAYOUT_VERTICAL; + BitField available_layouts = DOCK_LAYOUT_VERTICAL | DOCK_LAYOUT_FLOATING; - bool open = false; + bool is_open = false; bool enabled = true; - bool at_bottom = false; int previous_tab_index = -1; - bool previous_at_bottom = false; WindowWrapper *dock_window = nullptr; - int dock_slot_index = EditorDockManager::DOCK_SLOT_NONE; + int dock_slot_index = DockConstants::DOCK_SLOT_NONE; void _set_default_slot_bind(EditorPlugin::DockSlot p_slot); EditorPlugin::DockSlot _get_default_slot_bind() const { return (EditorPlugin::DockSlot)default_slot; } @@ -81,23 +85,35 @@ protected: public: EditorDock(); + void open(); + void close(); + void set_title(const String &p_title); String get_title() const { return title; } void set_layout_key(const String &p_key) { layout_key = p_key; } String get_layout_key() const { return layout_key; } + void set_global(bool p_global); + bool is_global() const { return global; } + + void set_transient(bool p_transient) { transient = p_transient; } + bool is_transient() const { return transient; } + void set_icon_name(const StringName &p_name); StringName get_icon_name() const { return icon_name; } void set_dock_icon(const Ref &p_icon); Ref get_dock_icon() const { return dock_icon; } - void set_dock_shortcut(const Ref &p_shortcut) { shortcut = p_shortcut; } + void set_title_color(const Color &p_color); + Color get_title_color() const { return title_color; } + + void set_dock_shortcut(const Ref &p_shortcut); Ref get_dock_shortcut() const { return shortcut; } - void set_default_slot(EditorDockManager::DockSlot p_slot); - EditorDockManager::DockSlot get_default_slot() const { return default_slot; } + void set_default_slot(DockConstants::DockSlot p_slot); + DockConstants::DockSlot get_default_slot() const { return default_slot; } void set_available_layouts(BitField p_layouts) { available_layouts = p_layouts; } BitField get_available_layouts() const { return available_layouts; } diff --git a/editor/docks/editor_dock_manager.cpp b/editor/docks/editor_dock_manager.cpp index 5c99e8fb615..f81c8f668df 100644 --- a/editor/docks/editor_dock_manager.cpp +++ b/editor/docks/editor_dock_manager.cpp @@ -69,7 +69,7 @@ void EditorDockDragHint::drop_data(const Point2 &p_point, const Variant &p_data) if (drop_tabbar->get_rect().has_point(p_point)) { drop_tabbar->_handle_drop_data("tab_container_tab", p_point, p_data, callable_mp(this, &EditorDockDragHint::_drag_move_tab), callable_mp(this, &EditorDockDragHint::_drag_move_tab_from)); } else { - dock_manager->_move_dock(dock_manager->_get_dock_tab_dragged(), dock_manager->dock_slot[occupied_slot], drop_tabbar->get_tab_count()); + dock_manager->_move_dock(dock_manager->_get_dock_tab_dragged(), dock_manager->dock_slots[occupied_slot].container, drop_tabbar->get_tab_count()); } } @@ -78,7 +78,7 @@ void EditorDockDragHint::_drag_move_tab(int p_from_index, int p_to_index) { } void EditorDockDragHint::_drag_move_tab_from(TabBar *p_from_tabbar, int p_from_index, int p_to_index) { - dock_manager->_move_dock(dock_manager->_get_dock_tab_dragged(), dock_manager->dock_slot[occupied_slot], p_to_index); + dock_manager->_move_dock(dock_manager->_get_dock_tab_dragged(), dock_manager->dock_slots[occupied_slot].container, p_to_index); } void EditorDockDragHint::gui_input(const Ref &p_event) { @@ -96,9 +96,9 @@ void EditorDockDragHint::gui_input(const Ref &p_event) { } } -void EditorDockDragHint::set_slot(EditorDockManager::DockSlot p_slot) { +void EditorDockDragHint::set_slot(DockConstants::DockSlot p_slot) { occupied_slot = p_slot; - drop_tabbar = dock_manager->dock_slot[occupied_slot]->get_tab_bar(); + drop_tabbar = dock_manager->dock_slots[occupied_slot].container->get_tab_bar(); } void EditorDockDragHint::_notification(int p_what) { @@ -123,12 +123,12 @@ void EditorDockDragHint::_notification(int p_what) { } break; case NOTIFICATION_DRAG_BEGIN: { - Control *dragged_dock = dock_manager->_get_dock_tab_dragged(); + EditorDock *dragged_dock = dock_manager->_get_dock_tab_dragged(); if (!dragged_dock) { return; } - can_drop_dock = true; + can_drop_dock = dragged_dock->get_available_layouts() & (EditorDock::DockLayout)EditorDockManager::get_singleton()->dock_slots[occupied_slot].layout; dock_drop_highlight->set_border_color(valid_drop_color); dock_drop_highlight->set_bg_color(valid_drop_color * Color(1, 1, 1, 0.1)); @@ -141,7 +141,7 @@ void EditorDockDragHint::_notification(int p_what) { } break; case NOTIFICATION_DRAW: { - if (!mouse_inside) { + if (!mouse_inside || !can_drop_dock) { return; } @@ -151,6 +151,7 @@ void EditorDockDragHint::_notification(int p_what) { // Only display tabbar hint if the mouse is over the tabbar. if (drop_tabbar->get_global_rect().has_point(get_global_mouse_position())) { + draw_set_transform(drop_tabbar->get_position()); // The TabBar isn't always on top. drop_tabbar->_draw_tab_drop(get_canvas_item()); } } break; @@ -245,7 +246,7 @@ EditorDock *EditorDockManager::_get_dock_tab_dragged() { return dock_tab_dragged; } - Dictionary dock_drop_data = dock_slot[DOCK_SLOT_LEFT_BL]->get_viewport()->gui_get_drag_data(); + Dictionary dock_drop_data = dock_slots[DockConstants::DOCK_SLOT_LEFT_BL].container->get_viewport()->gui_get_drag_data(); // Check if we are dragging a dock. if (dock_drop_data.get("type", "").operator String() != "tab") { @@ -269,10 +270,10 @@ EditorDock *EditorDockManager::_get_dock_tab_dragged() { return nullptr; } - for (int i = 0; i < DOCK_SLOT_MAX; i++) { - if (dock_slot[i]->is_visible_in_tree()) { - dock_drag_rects[i]->set_rect(dock_slot[i]->get_global_rect()); - dock_drag_rects[i]->show(); + for (int i = 0; i < DockConstants::DOCK_SLOT_MAX; i++) { + if (dock_slots[i].container->is_visible_in_tree()) { + dock_slots[i].drag_hint->set_rect(dock_slots[i].container->get_global_rect()); + dock_slots[i].drag_hint->show(); } } @@ -334,7 +335,7 @@ void EditorDockManager::update_docks_menu() { int id = 0; const Callable icon_fetch = callable_mp((Window *)docks_menu, &Window::get_editor_theme_native_menu_icon).bind(global_menu, dark_mode); for (EditorDock *dock : all_docks) { - if (!dock->enabled) { + if (!dock->enabled || !dock->global) { continue; } if (dock->shortcut.is_valid()) { @@ -347,7 +348,7 @@ void EditorDockManager::update_docks_menu() { const Ref icon = _get_dock_icon(dock, icon_fetch); docks_menu->set_item_icon(id, icon.is_valid() ? icon : default_icon); - if (!dock->open) { + if (!dock->is_open) { docks_menu->set_item_icon_modulate(id, closed_icon_color_mod); docks_menu->set_item_tooltip(id, vformat(TTR("Open the %s dock."), dock->get_display_title())); } else { @@ -362,7 +363,7 @@ void EditorDockManager::_docks_menu_option(int p_id) { EditorDock *dock = docks_menu_docks[p_id]; ERR_FAIL_NULL(dock); ERR_FAIL_COND_MSG(!all_docks.has(dock), vformat("Menu option for unknown dock '%s'.", dock->get_display_title())); - if (dock->enabled && dock->open) { + if (dock->enabled && dock->is_open) { PopupMenu *parent_menu = Object::cast_to(docks_menu->get_parent()); ERR_FAIL_NULL(parent_menu); parent_menu->hide(); @@ -375,8 +376,8 @@ void EditorDockManager::_window_close_request(WindowWrapper *p_wrapper) { EditorDock *dock = _close_window(p_wrapper); ERR_FAIL_COND(!all_docks.has(dock)); - if (dock->previous_at_bottom || dock->dock_slot_index != DOCK_SLOT_NONE) { - dock->open = false; + if (dock->dock_slot_index != DockConstants::DOCK_SLOT_NONE) { + dock->is_open = false; open_dock(dock); focus_dock(dock); } else { @@ -411,10 +412,11 @@ void EditorDockManager::_open_dock_in_window(EditorDock *p_dock, bool p_show_win EditorNode::get_singleton()->get_gui_base()->add_child(wrapper); _move_dock(p_dock, nullptr); + p_dock->update_layout(EditorDock::DOCK_LAYOUT_FLOATING); wrapper->set_wrapped_control(p_dock); p_dock->dock_window = wrapper; - p_dock->open = true; + p_dock->is_open = true; p_dock->show(); wrapper->connect("window_close_requested", callable_mp(this, &EditorDockManager::_window_close_request).bind(wrapper)); @@ -444,31 +446,6 @@ void EditorDockManager::_restore_dock_to_saved_window(EditorDock *p_dock, const p_window_dump.get("window_screen_rect", Rect2i())); } -void EditorDockManager::_dock_move_to_bottom(EditorDock *p_dock, bool p_visible) { - _move_dock(p_dock, nullptr); - - p_dock->at_bottom = true; - p_dock->previous_at_bottom = false; - p_dock->update_layout(EditorDock::DOCK_LAYOUT_HORIZONTAL); - - // Force docks moved to the bottom to appear first in the list, and give them their associated shortcut to toggle their bottom panel. - EditorNode::get_bottom_panel()->add_item(p_dock->get_display_title(), p_dock, p_dock->shortcut, true); - EditorNode::get_bottom_panel()->make_item_visible(p_dock, p_visible); -} - -void EditorDockManager::_dock_remove_from_bottom(EditorDock *p_dock) { - p_dock->at_bottom = false; - p_dock->previous_at_bottom = true; - - EditorNode::get_bottom_panel()->remove_item(p_dock); - p_dock->update_layout(EditorDock::DOCK_LAYOUT_VERTICAL); -} - -bool EditorDockManager::_is_dock_at_bottom(EditorDock *p_dock) { - ERR_FAIL_COND_V(!all_docks.has(p_dock), false); - return p_dock->at_bottom; -} - void EditorDockManager::_move_dock_tab_index(EditorDock *p_dock, int p_tab_index, bool p_set_current) { TabContainer *dock_tab_container = Object::cast_to(p_dock->get_parent()); if (!dock_tab_container) { @@ -492,7 +469,7 @@ void EditorDockManager::_move_dock(EditorDock *p_dock, Control *p_target, int p_ Node *parent = p_dock->get_parent(); if (parent == p_target) { - if (p_tab_index >= 0 && parent) { + if (parent && p_tab_index >= 0) { // Only change the tab index. _move_dock_tab_index(p_dock, p_tab_index, p_set_current); } @@ -503,10 +480,7 @@ void EditorDockManager::_move_dock(EditorDock *p_dock, Control *p_target, int p_ if (parent) { if (p_dock->dock_window) { _close_window(p_dock->dock_window); - } else if (p_dock->at_bottom) { - _dock_remove_from_bottom(p_dock); } else { - p_dock->previous_at_bottom = false; TabContainer *parent_tabs = Object::cast_to(parent); if (parent_tabs) { p_dock->previous_tab_index = parent_tabs->get_tab_idx_from_control(p_dock); @@ -520,13 +494,20 @@ void EditorDockManager::_move_dock(EditorDock *p_dock, Control *p_target, int p_ } } - // Add dock to its new parent, at the given tab index. if (!p_target) { return; } + + if (p_target != closed_dock_parent) { + p_dock->update_layout(p_target->get_meta("dock_layout")); + p_dock->dock_slot_index = p_target->get_meta("dock_slot"); + } + + // Add dock to its new parent, at the given tab index. p_target->set_block_signals(true); p_target->add_child(p_dock); p_target->set_block_signals(false); + TabContainer *dock_tab_container = Object::cast_to(p_target); if (dock_tab_container) { if (dock_tab_container->is_inside_tree()) { @@ -540,11 +521,11 @@ void EditorDockManager::_move_dock(EditorDock *p_dock, Control *p_target, int p_ } void EditorDockManager::_update_tab_style(EditorDock *p_dock) { - if (!p_dock->enabled || !p_dock->open) { + if (!p_dock->enabled || !p_dock->is_open) { return; // Disabled by feature profile or manually closed by user. } - if (p_dock->dock_window || p_dock->at_bottom) { - return; // Floating or sent to bottom. + if (p_dock->dock_window) { + return; // Floating. } TabContainer *tab_container = get_dock_tab_container(p_dock); @@ -553,6 +534,12 @@ void EditorDockManager::_update_tab_style(EditorDock *p_dock) { int index = tab_container->get_tab_idx_from_control(p_dock); ERR_FAIL_COND(index == -1); + if (p_dock->global) { + update_docks_menu(); + } + + tab_container->get_tab_bar()->set_font_color_override_all(index, p_dock->title_color); + const TabStyle style = (TabStyle)EDITOR_GET("interface/editor/dock_tab_style").operator int(); const Ref icon = _get_dock_icon(p_dock, callable_mp((Control *)tab_container, &Control::get_editor_theme_icon)); switch (style) { @@ -576,35 +563,34 @@ void EditorDockManager::_update_tab_style(EditorDock *p_dock) { void EditorDockManager::save_docks_to_config(Ref p_layout, const String &p_section) const { // Save docks by dock slot. - for (int i = 0; i < DOCK_SLOT_MAX; i++) { - String names; - for (int j = 0; j < dock_slot[i]->get_tab_count(); j++) { - String name = Object::cast_to(dock_slot[i]->get_tab_control(j))->get_effective_layout_key(); - if (!names.is_empty()) { - names += ","; - } - names += name; + for (int i = 0; i < DockConstants::DOCK_SLOT_MAX; i++) { + const DockSlot &dock_slot = dock_slots[i]; + + PackedStringArray names; + names.reserve_exact(dock_slot.container->get_tab_count()); + for (int j = 0; j < dock_slot.container->get_tab_count(); j++) { + const String name = Object::cast_to(dock_slot.container->get_tab_control(j))->get_effective_layout_key(); + names.append(name); } - String config_key = "dock_" + itos(i + 1); - + const String config_key = "dock_" + itos(i + 1); if (p_layout->has_section_key(p_section, config_key)) { p_layout->erase_section_key(p_section, config_key); } if (!names.is_empty()) { - p_layout->set_value(p_section, config_key, names); + p_layout->set_value(p_section, config_key, String(",").join(names)); } - int selected_tab_idx = dock_slot[i]->get_current_tab(); + const String tab_key = config_key + "_selected_tab_idx"; + + int selected_tab_idx = dock_slots[i].container->get_current_tab(); if (selected_tab_idx >= 0) { - p_layout->set_value(p_section, "dock_" + itos(i + 1) + "_selected_tab_idx", selected_tab_idx); + p_layout->set_value(p_section, tab_key, selected_tab_idx); + } else if (p_layout->has_section_key(p_section, tab_key)) { + p_layout->erase_section_key(p_section, tab_key); } } - if (p_layout->has_section_key(p_section, "dock_0")) { - // Clear the keys where the dock has no slot so it is overridden. - p_layout->erase_section_key(p_section, "dock_0"); - } // Save docks in windows. Dictionary floating_docks_dump; @@ -635,22 +621,18 @@ void EditorDockManager::save_docks_to_config(Ref p_layout, const Str } p_layout->set_value(p_section, "dock_floating", floating_docks_dump); - // Save closed and bottom docks. - Array bottom_docks_dump; + // Save closed docks. Array closed_docks_dump; for (const EditorDock *dock : all_docks) { const String section_name = p_section + "/" + dock->get_effective_layout_key(); dock->save_layout_to_config(p_layout, section_name); - if (!dock->at_bottom && dock->open && (!dock->previous_at_bottom || !dock->dock_window)) { + if (dock->is_open) { continue; } const String name = dock->get_effective_layout_key(); - if (dock->at_bottom || (dock->previous_at_bottom && dock->dock_window)) { - bottom_docks_dump.push_back(name); - } - if (!dock->open) { + if (!dock->is_open && !dock->transient) { closed_docks_dump.push_back(name); } @@ -665,7 +647,6 @@ void EditorDockManager::save_docks_to_config(Ref p_layout, const Str } p_layout->set_value(p_section, config_key, names); } - p_layout->set_value(p_section, "dock_bottom", bottom_docks_dump); p_layout->set_value(p_section, "dock_closed", closed_docks_dump); // Save SplitContainer offsets. @@ -682,7 +663,6 @@ void EditorDockManager::save_docks_to_config(Ref p_layout, const Str void EditorDockManager::load_docks_from_config(Ref p_layout, const String &p_section, bool p_first_load) { Dictionary floating_docks_dump = p_layout->get_value(p_section, "dock_floating", Dictionary()); - Array dock_bottom = p_layout->get_value(p_section, "dock_bottom", Array()); Array closed_docks = p_layout->get_value(p_section, "dock_closed", Array()); bool allow_floating_docks = EditorNode::get_singleton()->is_multi_window_enabled() && (!p_first_load || EDITOR_GET("interface/multi_window/restore_windows_on_load")); @@ -694,7 +674,7 @@ void EditorDockManager::load_docks_from_config(Ref p_layout, const S } // Load docks by slot. Index -1 is for docks that have no slot. - for (int i = -1; i < DOCK_SLOT_MAX; i++) { + for (int i = -1; i < DockConstants::DOCK_SLOT_MAX; i++) { if (!p_layout->has_section_key(p_section, "dock_" + itos(i + 1))) { continue; } @@ -711,32 +691,28 @@ void EditorDockManager::load_docks_from_config(Ref p_layout, const S EditorDock *dock = dock_map[name]; if (!dock->enabled) { - // Don't open disabled docks. + // Don't is_open disabled docks. dock->load_layout_from_config(p_layout, section_name); continue; } - bool at_bottom = false; if (allow_floating_docks && floating_docks_dump.has(name)) { - dock->previous_at_bottom = dock_bottom.has(name); _restore_dock_to_saved_window(dock, floating_docks_dump[name]); - } else if (dock_bottom.has(name)) { - _dock_move_to_bottom(dock, false); - at_bottom = true; } else if (i >= 0) { - _move_dock(dock, dock_slot[i], 0); + if (dock->transient && !dock->is_open) { + dock->dock_slot_index = i; + } else { + _move_dock(dock, dock_slots[i].container, 0); + } } dock->load_layout_from_config(p_layout, section_name); - if (closed_docks.has(name)) { - _move_dock(dock, closed_dock_parent); - dock->open = false; - dock->hide(); - } else { - // Make sure it is open. - dock->open = true; - // It's important to not update the visibility of bottom panels. - // Visibility of bottom panels are managed in EditorBottomPanel. - if (!at_bottom) { + if (!dock->transient) { + if (closed_docks.has(name)) { + _move_dock(dock, closed_dock_parent); + dock->is_open = false; + dock->hide(); + } else { + dock->is_open = true; dock->show(); } } @@ -747,16 +723,24 @@ void EditorDockManager::load_docks_from_config(Ref p_layout, const S } // Set the selected tabs. - for (int i = 0; i < DOCK_SLOT_MAX; i++) { - if (dock_slot[i]->get_tab_count() == 0 || !p_layout->has_section_key(p_section, "dock_" + itos(i + 1) + "_selected_tab_idx")) { + for (int i = 0; i < DockConstants::DOCK_SLOT_MAX; i++) { + const DockSlot &dock_slot = dock_slots[i]; + + int selected_tab_idx = p_layout->get_value(p_section, "dock_" + itos(i + 1) + "_selected_tab_idx", -1); + if (selected_tab_idx <= 0 || selected_tab_idx >= dock_slot.container->get_tab_count()) { + if (i == DockConstants::DOCK_SLOT_BOTTOM) { + dock_slot.container->set_current_tab(-1); + } continue; } - int selected_tab_idx = p_layout->get_value(p_section, "dock_" + itos(i + 1) + "_selected_tab_idx"); - if (selected_tab_idx >= 0 && selected_tab_idx < dock_slot[i]->get_tab_count()) { - dock_slot[i]->set_block_signals(true); - dock_slot[i]->set_current_tab(selected_tab_idx); - dock_slot[i]->set_block_signals(false); + + EditorDock *selected_dock = Object::cast_to(dock_slot.container->get_tab_control(selected_tab_idx)); + if (!selected_dock) { + continue; } + dock_slot.container->set_block_signals(true); + dock_slot.container->set_current_tab(selected_tab_idx); + dock_slot.container->set_block_signals(false); } // Load SplitContainer offsets. @@ -778,22 +762,6 @@ void EditorDockManager::load_docks_from_config(Ref p_layout, const S update_docks_menu(); } -void EditorDockManager::bottom_dock_show_placement_popup(const Rect2i &p_position, EditorDock *p_dock) { - ERR_FAIL_COND(!all_docks.has(p_dock)); - - dock_context_popup->set_dock(p_dock); - - Vector2 popup_pos = p_position.position; - popup_pos.y += p_position.size.height; - - if (!EditorNode::get_singleton()->get_gui_base()->is_layout_rtl()) { - popup_pos.x -= dock_context_popup->get_size().width; - popup_pos.x += p_position.size.width; - } - dock_context_popup->set_position(popup_pos); - dock_context_popup->popup(); -} - void EditorDockManager::set_dock_enabled(EditorDock *p_dock, bool p_enabled) { ERR_FAIL_NULL(p_dock); ERR_FAIL_COND_MSG(!all_docks.has(p_dock), vformat("Cannot set enabled unknown dock '%s'.", p_dock->get_display_title())); @@ -814,13 +782,17 @@ void EditorDockManager::close_dock(EditorDock *p_dock) { ERR_FAIL_NULL(p_dock); ERR_FAIL_COND_MSG(!all_docks.has(p_dock), vformat("Cannot close unknown dock '%s'.", p_dock->get_display_title())); - if (!p_dock->open) { + if (!p_dock->is_open) { return; } + EditorBottomPanel *bottom_panel = EditorNode::get_bottom_panel(); + if (get_dock_tab_container(p_dock) == bottom_panel && bottom_panel->get_current_tab_control() == p_dock) { + bottom_panel->hide_bottom_panel(); + } _move_dock(p_dock, closed_dock_parent); - p_dock->open = false; + p_dock->is_open = false; p_dock->hide(); _update_layout(); @@ -830,18 +802,16 @@ void EditorDockManager::open_dock(EditorDock *p_dock, bool p_set_current) { ERR_FAIL_NULL(p_dock); ERR_FAIL_COND_MSG(!all_docks.has(p_dock), vformat("Cannot open unknown dock '%s'.", p_dock->get_display_title())); - if (p_dock->open) { + if (p_dock->is_open) { return; } - p_dock->open = true; + p_dock->is_open = true; p_dock->show(); // Open dock to its previous location. - if (p_dock->previous_at_bottom) { - _dock_move_to_bottom(p_dock, true); - } else if (p_dock->dock_slot_index != DOCK_SLOT_NONE) { - TabContainer *slot = dock_slot[p_dock->dock_slot_index]; + if (p_dock->dock_slot_index != DockConstants::DOCK_SLOT_NONE) { + TabContainer *slot = dock_slots[p_dock->dock_slot_index].container; int tab_index = p_dock->previous_tab_index; if (tab_index < 0) { tab_index = slot->get_tab_count(); @@ -867,7 +837,7 @@ void EditorDockManager::focus_dock(EditorDock *p_dock) { return; } - if (!p_dock->open) { + if (!p_dock->is_open) { open_dock(p_dock); } @@ -876,11 +846,6 @@ void EditorDockManager::focus_dock(EditorDock *p_dock) { return; } - if (p_dock->at_bottom) { - EditorNode::get_bottom_panel()->make_item_visible(p_dock, true, true); - return; - } - if (!docks_visible) { return; } @@ -903,7 +868,7 @@ void EditorDockManager::add_dock(EditorDock *p_dock) { p_dock->connect("tab_style_changed", callable_mp(this, &EditorDockManager::_update_tab_style).bind(p_dock)); p_dock->connect("renamed", callable_mp(this, &EditorDockManager::_update_tab_style).bind(p_dock)); - if (p_dock->default_slot != DOCK_SLOT_NONE) { + if (p_dock->default_slot != DockConstants::DOCK_SLOT_NONE) { open_dock(p_dock, false); } else { closed_dock_parent->add_child(p_dock); @@ -929,8 +894,8 @@ void EditorDockManager::set_docks_visible(bool p_show) { return; } docks_visible = p_show; - for (int i = 0; i < DOCK_SLOT_MAX; i++) { - dock_slot[i]->set_visible(docks_visible && dock_slot[i]->get_tab_count() > 0); + for (int i = 0; i < DockConstants::DOCK_SLOT_MAX; i++) { + dock_slots[i].container->set_visible(docks_visible && dock_slots[i].container->get_tab_count() > 0); } _update_layout(); } @@ -946,8 +911,8 @@ void EditorDockManager::update_tab_styles() { } void EditorDockManager::set_tab_icon_max_width(int p_max_width) { - for (int i = 0; i < DOCK_SLOT_MAX; i++) { - TabContainer *tab_container = dock_slot[i]; + for (int i = 0; i < DockConstants::DOCK_SLOT_MAX; i++) { + TabContainer *tab_container = dock_slots[i].container; tab_container->add_theme_constant_override(SNAME("icon_max_width"), p_max_width); } } @@ -962,14 +927,14 @@ void EditorDockManager::add_hsplit(DockSplitContainer *p_split) { p_split->connect("dragged", callable_mp(this, &EditorDockManager::_dock_split_dragged)); } -void EditorDockManager::register_dock_slot(DockSlot p_dock_slot, TabContainer *p_tab_container) { +void EditorDockManager::register_dock_slot(DockConstants::DockSlot p_dock_slot, TabContainer *p_tab_container, DockConstants::DockLayout p_layout) { ERR_FAIL_NULL(p_tab_container); - ERR_FAIL_INDEX(p_dock_slot, DOCK_SLOT_MAX); + ERR_FAIL_INDEX(p_dock_slot, DockConstants::DOCK_SLOT_MAX); - dock_slot[p_dock_slot] = p_tab_container; + DockSlot slot; + slot.layout = p_layout; - p_tab_container->set_custom_minimum_size(Size2(170, 0) * EDSCALE); - p_tab_container->set_v_size_flags(Control::SIZE_EXPAND_FILL); + slot.container = p_tab_container; p_tab_container->set_popup(dock_context_popup); p_tab_container->connect("pre_popup_pressed", callable_mp(dock_context_popup, &DockContextPopup::select_current_dock_in_dock_slot).bind(p_dock_slot)); p_tab_container->get_tab_bar()->connect("tab_rmb_clicked", callable_mp(this, &EditorDockManager::_dock_container_popup).bind(p_tab_container)); @@ -978,14 +943,23 @@ void EditorDockManager::register_dock_slot(DockSlot p_dock_slot, TabContainer *p p_tab_container->connect("tab_changed", callable_mp(this, &EditorDockManager::_update_layout).unbind(1)); p_tab_container->connect("active_tab_rearranged", callable_mp(this, &EditorDockManager::_update_layout).unbind(1)); p_tab_container->connect("child_order_changed", callable_mp(this, &EditorDockManager::_dock_container_update_visibility).bind(p_tab_container)); - p_tab_container->set_use_hidden_tabs_for_min_size(true); p_tab_container->hide(); + p_tab_container->set_meta("dock_slot", p_dock_slot); + p_tab_container->set_meta("dock_layout", p_layout); + + if (p_layout == DockConstants::DOCK_LAYOUT_VERTICAL) { + p_tab_container->set_custom_minimum_size(Size2(170, 0) * EDSCALE); + p_tab_container->set_v_size_flags(Control::SIZE_EXPAND_FILL); + p_tab_container->set_use_hidden_tabs_for_min_size(true); + } // Create dock dragging hint. - dock_drag_rects[p_dock_slot] = memnew(EditorDockDragHint); - dock_drag_rects[p_dock_slot]->set_slot(p_dock_slot); - dock_drag_rects[p_dock_slot]->hide(); - EditorNode::get_singleton()->get_gui_base()->add_child(dock_drag_rects[p_dock_slot]); + slot.drag_hint = memnew(EditorDockDragHint); + slot.drag_hint->hide(); + EditorNode::get_singleton()->get_gui_base()->add_child(slot.drag_hint); + + dock_slots[p_dock_slot] = slot; + slot.drag_hint->set_slot(p_dock_slot); } int EditorDockManager::get_hsplit_count() const { @@ -1007,6 +981,7 @@ EditorDockManager::EditorDockManager() { dock_context_popup = memnew(DockContextPopup); EditorNode::get_singleton()->get_gui_base()->add_child(dock_context_popup); + EditorNode::get_singleton()->add_child(memnew(DockShortcutHandler)); docks_menu = memnew(PopupMenu); docks_menu->set_hide_on_item_selection(false); @@ -1036,7 +1011,6 @@ void DockContextPopup::_notification(int p_what) { tab_move_left_button->set_tooltip_text(TTR("Move this dock left one tab.")); tab_move_right_button->set_tooltip_text(TTR("Move this dock right one tab.")); } - dock_to_bottom_button->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignBottomWide"))); close_button->set_button_icon(get_editor_theme_icon(SNAME("Close"))); } break; } @@ -1074,10 +1048,8 @@ void DockContextPopup::_float_dock() { dock_manager->_open_dock_in_window(context_dock); } -void DockContextPopup::_move_dock_to_bottom() { - hide(); - dock_manager->_dock_move_to_bottom(context_dock, true); - dock_manager->_update_layout(); +bool DockContextPopup::_is_slot_available(int p_slot) const { + return context_dock->available_layouts & (EditorDock::DockLayout)EditorDockManager::get_singleton()->dock_slots[p_slot].layout; } void DockContextPopup::_dock_select_input(const Ref &p_input) { @@ -1087,7 +1059,7 @@ void DockContextPopup::_dock_select_input(const Ref &p_input) { Vector2 point = me->get_position(); int over_dock_slot = -1; - for (int i = 0; i < EditorDockManager::DOCK_SLOT_MAX; i++) { + for (int i = 0; i < DockConstants::DOCK_SLOT_MAX; i++) { if (dock_select_rects[i].has_point(point)) { over_dock_slot = i; break; @@ -1104,12 +1076,11 @@ void DockContextPopup::_dock_select_input(const Ref &p_input) { } Ref mb = me; - TabContainer *target_tab_container = dock_manager->dock_slot[over_dock_slot]; + TabContainer *target_tab_container = dock_manager->dock_slots[over_dock_slot].container; if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) { - if (dock_manager->get_dock_tab_container(context_dock) != target_tab_container) { + if (dock_manager->get_dock_tab_container(context_dock) != target_tab_container && _is_slot_available(over_dock_slot)) { dock_manager->_move_dock(context_dock, target_tab_container, target_tab_container->get_tab_count()); - context_dock->dock_slot_index = over_dock_slot; dock_manager->_update_layout(); hide(); } @@ -1137,30 +1108,30 @@ void DockContextPopup::_dock_select_draw() { dock_size.x /= 6.0; dock_size.y /= 2.0; - Size2 center_panel_size = dock_size * 2.0; - Rect2 center_panel_rect(center_panel_size.x, 0, center_panel_size.x, center_panel_size.y); + real_t center_panel_width = dock_size.x * 2.0; + Rect2 center_panel_rect(center_panel_width, 0, center_panel_width, dock_size.y); if (dock_select->is_layout_rtl()) { - dock_select_rects[EditorDockManager::DOCK_SLOT_RIGHT_UR] = Rect2(Point2(), dock_size); - dock_select_rects[EditorDockManager::DOCK_SLOT_RIGHT_BR] = Rect2(Point2(0, dock_size.y), dock_size); - dock_select_rects[EditorDockManager::DOCK_SLOT_RIGHT_UL] = Rect2(Point2(dock_size.x, 0), dock_size); - dock_select_rects[EditorDockManager::DOCK_SLOT_RIGHT_BL] = Rect2(dock_size, dock_size); - dock_select_rects[EditorDockManager::DOCK_SLOT_LEFT_UR] = Rect2(Point2(dock_size.x * 4, 0), dock_size); - dock_select_rects[EditorDockManager::DOCK_SLOT_LEFT_BR] = Rect2(Point2(dock_size.x * 4, dock_size.y), dock_size); - dock_select_rects[EditorDockManager::DOCK_SLOT_LEFT_UL] = Rect2(Point2(dock_size.x * 5, 0), dock_size); - dock_select_rects[EditorDockManager::DOCK_SLOT_LEFT_BL] = Rect2(Point2(dock_size.x * 5, dock_size.y), dock_size); + dock_select_rects[DockConstants::DOCK_SLOT_RIGHT_UR] = Rect2(Point2(), dock_size); + dock_select_rects[DockConstants::DOCK_SLOT_RIGHT_BR] = Rect2(Point2(0, dock_size.y), dock_size); + dock_select_rects[DockConstants::DOCK_SLOT_RIGHT_UL] = Rect2(Point2(dock_size.x, 0), dock_size); + dock_select_rects[DockConstants::DOCK_SLOT_RIGHT_BL] = Rect2(dock_size, dock_size); + dock_select_rects[DockConstants::DOCK_SLOT_LEFT_UR] = Rect2(Point2(dock_size.x * 4, 0), dock_size); + dock_select_rects[DockConstants::DOCK_SLOT_LEFT_BR] = Rect2(Point2(dock_size.x * 4, dock_size.y), dock_size); + dock_select_rects[DockConstants::DOCK_SLOT_LEFT_UL] = Rect2(Point2(dock_size.x * 5, 0), dock_size); + dock_select_rects[DockConstants::DOCK_SLOT_LEFT_BL] = Rect2(Point2(dock_size.x * 5, dock_size.y), dock_size); } else { - dock_select_rects[EditorDockManager::DOCK_SLOT_LEFT_UL] = Rect2(Point2(), dock_size); - dock_select_rects[EditorDockManager::DOCK_SLOT_LEFT_BL] = Rect2(Point2(0, dock_size.y), dock_size); - dock_select_rects[EditorDockManager::DOCK_SLOT_LEFT_UR] = Rect2(Point2(dock_size.x, 0), dock_size); - dock_select_rects[EditorDockManager::DOCK_SLOT_LEFT_BR] = Rect2(dock_size, dock_size); - dock_select_rects[EditorDockManager::DOCK_SLOT_RIGHT_UL] = Rect2(Point2(dock_size.x * 4, 0), dock_size); - dock_select_rects[EditorDockManager::DOCK_SLOT_RIGHT_BL] = Rect2(Point2(dock_size.x * 4, dock_size.y), dock_size); - dock_select_rects[EditorDockManager::DOCK_SLOT_RIGHT_UR] = Rect2(Point2(dock_size.x * 5, 0), dock_size); - dock_select_rects[EditorDockManager::DOCK_SLOT_RIGHT_BR] = Rect2(Point2(dock_size.x * 5, dock_size.y), dock_size); + dock_select_rects[DockConstants::DOCK_SLOT_LEFT_UL] = Rect2(Point2(), dock_size); + dock_select_rects[DockConstants::DOCK_SLOT_LEFT_BL] = Rect2(Point2(0, dock_size.y), dock_size); + dock_select_rects[DockConstants::DOCK_SLOT_LEFT_UR] = Rect2(Point2(dock_size.x, 0), dock_size); + dock_select_rects[DockConstants::DOCK_SLOT_LEFT_BR] = Rect2(dock_size, dock_size); + dock_select_rects[DockConstants::DOCK_SLOT_RIGHT_UL] = Rect2(Point2(dock_size.x * 4, 0), dock_size); + dock_select_rects[DockConstants::DOCK_SLOT_RIGHT_BL] = Rect2(Point2(dock_size.x * 4, dock_size.y), dock_size); + dock_select_rects[DockConstants::DOCK_SLOT_RIGHT_UR] = Rect2(Point2(dock_size.x * 5, 0), dock_size); + dock_select_rects[DockConstants::DOCK_SLOT_RIGHT_BR] = Rect2(Point2(dock_size.x * 5, dock_size.y), dock_size); } + dock_select_rects[DockConstants::DOCK_SLOT_BOTTOM] = Rect2(center_panel_width, dock_size.y, center_panel_width, dock_size.y); - int max_tabs = 3; int rtl_dir = dock_select->is_layout_rtl() ? -1 : 1; real_t tab_height = 3.0 * EDSCALE; real_t tab_spacing = 1.0 * EDSCALE; @@ -1178,17 +1149,30 @@ void DockContextPopup::_dock_select_draw() { dock_select->draw_rect(center_panel_draw_rect, unusable_dock_color); // Draw all dock slots. - for (int i = 0; i < EditorDockManager::DOCK_SLOT_MAX; i++) { + for (int i = 0; i < DockConstants::DOCK_SLOT_MAX; i++) { + int max_tabs = (i == DockConstants::DOCK_SLOT_BOTTOM) ? 6 : 3; + const EditorDockManager::DockSlot &dock_slot = dock_manager->dock_slots[i]; + Rect2 dock_slot_draw_rect = dock_select_rects[i].grow_individual(-dock_spacing, -dock_top_spacing, -dock_spacing, -dock_spacing); real_t tab_width = Math::round(dock_slot_draw_rect.size.width / max_tabs); Rect2 tab_draw_rect = Rect2(dock_slot_draw_rect.position.x, dock_select_rects[i].position.y, tab_width - tab_spacing, tab_height); + + real_t max_width = tab_width * max_tabs; + // Tabs may not fit perfectly, so they need to be re-centered. + if (max_width > dock_slot_draw_rect.size.x) { + tab_draw_rect.position.x -= int(max_width - dock_slot_draw_rect.size.x) / 2 * rtl_dir; + } if (dock_select->is_layout_rtl()) { tab_draw_rect.position.x += dock_slot_draw_rect.size.x - tab_draw_rect.size.x; } - bool is_context_dock = context_tab_container == dock_manager->dock_slot[i]; - int tabs_to_draw = MIN(max_tabs, dock_manager->dock_slot[i]->get_tab_count()); - if (i == dock_select_rect_over_idx) { + int tabs_to_draw = MIN(max_tabs, dock_slot.container->get_tab_count()); + bool is_context_dock = context_tab_container == dock_slot.container; + if (i == context_dock->dock_slot_index) { + dock_select->draw_rect(dock_slot_draw_rect, tab_selected_color); + } else if (!_is_slot_available(i)) { + dock_select->draw_rect(dock_slot_draw_rect, unusable_dock_color); + } else if (i == dock_select_rect_over_idx) { dock_select->draw_rect(dock_slot_draw_rect, hovered_dock_color); } else if (tabs_to_draw == 0) { dock_select->draw_rect(dock_slot_draw_rect, unused_dock_color); @@ -1211,23 +1195,36 @@ void DockContextPopup::_dock_select_draw() { void DockContextPopup::_update_buttons() { TabContainer *context_tab_container = dock_manager->get_dock_tab_container(context_dock); - bool dock_at_bottom = dock_manager->_is_dock_at_bottom(context_dock); + if (context_dock->global) { + close_button->set_tooltip_text(TTRC("Close this dock.")); + close_button->set_disabled(false); + } else { + close_button->set_tooltip_text(TTRC("This dock can't be closed.")); + close_button->set_disabled(true); + } + if (EditorNode::get_singleton()->is_multi_window_enabled()) { + if (!(context_dock->available_layouts & EditorDock::DOCK_LAYOUT_FLOATING)) { + make_float_button->set_tooltip_text(TTRC("This dock does not support floating.")); + make_float_button->set_disabled(true); + } else { + make_float_button->set_tooltip_text(TTRC("Make this dock floating.")); + make_float_button->set_disabled(false); + } + } // Update tab move buttons. tab_move_left_button->set_disabled(true); tab_move_right_button->set_disabled(true); - if (!dock_at_bottom && context_tab_container && context_tab_container->get_tab_count() > 0) { + if (context_tab_container && context_tab_container->get_tab_count() > 0) { int context_tab_index = context_tab_container->get_tab_idx_from_control(context_dock); tab_move_left_button->set_disabled(context_tab_index == 0); tab_move_right_button->set_disabled(context_tab_index >= context_tab_container->get_tab_count() - 1); } - - dock_to_bottom_button->set_visible(!dock_at_bottom && bool(context_dock->available_layouts & EditorDock::DOCK_LAYOUT_HORIZONTAL)); reset_size(); } void DockContextPopup::select_current_dock_in_dock_slot(int p_dock_slot) { - context_dock = Object::cast_to(dock_manager->dock_slot[p_dock_slot]->get_current_tab_control()); + context_dock = Object::cast_to(dock_manager->dock_slots[p_dock_slot].container->get_current_tab_control()); _update_buttons(); } @@ -1262,7 +1259,7 @@ DockContextPopup::DockContextPopup() { header_hb->add_child(tab_move_left_button); Label *position_label = memnew(Label); - position_label->set_text(TTR("Dock Position")); + position_label->set_text(TTRC("Dock Position")); position_label->set_h_size_flags(Control::SIZE_EXPAND_FILL); position_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); header_hb->add_child(position_label); @@ -1285,32 +1282,39 @@ DockContextPopup::DockContextPopup() { dock_select_popup_vb->add_child(dock_select); make_float_button = memnew(Button); - make_float_button->set_text(TTR("Make Floating")); + make_float_button->set_text(TTRC("Make Floating")); if (!EditorNode::get_singleton()->is_multi_window_enabled()) { make_float_button->set_disabled(true); make_float_button->set_tooltip_text(EditorNode::get_singleton()->get_multiwindow_support_tooltip_text()); - } else { - make_float_button->set_tooltip_text(TTR("Make this dock floating.")); } make_float_button->set_focus_mode(Control::FOCUS_ACCESSIBILITY); make_float_button->set_h_size_flags(Control::SIZE_EXPAND_FILL); make_float_button->connect(SceneStringName(pressed), callable_mp(this, &DockContextPopup::_float_dock)); dock_select_popup_vb->add_child(make_float_button); - dock_to_bottom_button = memnew(Button); - dock_to_bottom_button->set_text(TTR("Move to Bottom")); - dock_to_bottom_button->set_tooltip_text(TTR("Move this dock to the bottom panel.")); - dock_to_bottom_button->set_focus_mode(Control::FOCUS_ACCESSIBILITY); - dock_to_bottom_button->set_h_size_flags(Control::SIZE_EXPAND_FILL); - dock_to_bottom_button->connect(SceneStringName(pressed), callable_mp(this, &DockContextPopup::_move_dock_to_bottom)); - dock_to_bottom_button->hide(); - dock_select_popup_vb->add_child(dock_to_bottom_button); - close_button = memnew(Button); - close_button->set_text(TTR("Close")); - close_button->set_tooltip_text(TTR("Close this dock.")); + close_button->set_text(TTRC("Close")); close_button->set_focus_mode(Control::FOCUS_ACCESSIBILITY); close_button->set_h_size_flags(Control::SIZE_EXPAND_FILL); close_button->connect(SceneStringName(pressed), callable_mp(this, &DockContextPopup::_close_dock)); dock_select_popup_vb->add_child(close_button); } + +void DockShortcutHandler::shortcut_input(const Ref &p_event) { + if (p_event.is_null() || !p_event->is_pressed() || p_event->is_echo()) { + return; + } + + for (EditorDock *dock : EditorDockManager::get_singleton()->all_docks) { + const Ref &dock_shortcut = dock->get_dock_shortcut(); + if (dock_shortcut.is_valid() && dock_shortcut->matches_event(p_event)) { + if (dock->is_visible() && dock->get_parent() == EditorNode::get_bottom_panel()) { + EditorNode::get_bottom_panel()->hide_bottom_panel(); + } else if (!dock->transient || dock->is_open) { + EditorDockManager::get_singleton()->focus_dock(dock); + } + get_viewport()->set_input_as_handled(); + break; + } + } +} diff --git a/editor/docks/editor_dock_manager.h b/editor/docks/editor_dock_manager.h index e426a64e61e..ffa04ae9e96 100644 --- a/editor/docks/editor_dock_manager.h +++ b/editor/docks/editor_dock_manager.h @@ -30,6 +30,7 @@ #pragma once +#include "editor/docks/dock_constants.h" #include "scene/gui/popup.h" #include "scene/gui/split_container.h" @@ -60,30 +61,26 @@ public: DockSplitContainer(); }; +class DockShortcutHandler : public Node { + GDCLASS(DockShortcutHandler, Node); + +protected: + virtual void shortcut_input(const Ref &p_event) override; + +public: + DockShortcutHandler() { set_process_shortcut_input(true); } +}; + class DockContextPopup; class EditorDockDragHint; class EditorDockManager : public Object { GDCLASS(EditorDockManager, Object); -public: - enum DockSlot { - DOCK_SLOT_NONE = -1, - DOCK_SLOT_LEFT_UL, - DOCK_SLOT_LEFT_BL, - DOCK_SLOT_LEFT_UR, - DOCK_SLOT_LEFT_BR, - DOCK_SLOT_RIGHT_UL, - DOCK_SLOT_RIGHT_BL, - DOCK_SLOT_RIGHT_UR, - DOCK_SLOT_RIGHT_BR, - DOCK_SLOT_MAX - }; - private: friend class DockContextPopup; friend class EditorDockDragHint; - friend class EditorBottomPanel; // TODO: Temporary until DOCK_SLOT_BOTTOM registered. Used to connect signals. + friend class DockShortcutHandler; static inline EditorDockManager *singleton = nullptr; @@ -91,10 +88,16 @@ private: Vector vsplits; Vector hsplits; + struct DockSlot { + TabContainer *container = nullptr; + EditorDockDragHint *drag_hint = nullptr; + DockConstants::DockLayout layout = DockConstants::DOCK_LAYOUT_VERTICAL; + }; + + DockSlot dock_slots[DockConstants::DOCK_SLOT_MAX]; Vector dock_windows; - TabContainer *dock_slot[DOCK_SLOT_MAX]; - EditorDockDragHint *dock_drag_rects[DOCK_SLOT_MAX]; LocalVector all_docks; + EditorDock *dock_tab_dragged = nullptr; bool docks_visible = true; @@ -117,10 +120,6 @@ private: void _open_dock_in_window(EditorDock *p_dock, bool p_show_window = true, bool p_reset_size = false); void _restore_dock_to_saved_window(EditorDock *p_dock, const Dictionary &p_window_dump); - void _dock_move_to_bottom(EditorDock *p_dock, bool p_visible); - void _dock_remove_from_bottom(EditorDock *p_dock); - bool _is_dock_at_bottom(EditorDock *p_dock); - void _move_dock_tab_index(EditorDock *p_dock, int p_tab_index, bool p_set_current); void _move_dock(EditorDock *p_dock, Control *p_target, int p_tab_index = -1, bool p_set_current = true); @@ -135,7 +134,7 @@ public: void add_vsplit(DockSplitContainer *p_split); void add_hsplit(DockSplitContainer *p_split); - void register_dock_slot(DockSlot p_dock_slot, TabContainer *p_tab_container); + void register_dock_slot(DockConstants::DockSlot p_dock_slot, TabContainer *p_tab_container, DockConstants::DockLayout p_layout); int get_hsplit_count() const; int get_vsplit_count() const; PopupMenu *get_docks_menu(); @@ -150,8 +149,6 @@ public: TabContainer *get_dock_tab_container(Control *p_dock) const; - void bottom_dock_show_placement_popup(const Rect2i &p_position, EditorDock *p_dock); - void set_docks_visible(bool p_show); bool are_docks_visible() const; @@ -166,7 +163,7 @@ class EditorDockDragHint : public Control { private: EditorDockManager *dock_manager = nullptr; - EditorDockManager::DockSlot occupied_slot = EditorDockManager::DOCK_SLOT_MAX; + DockConstants::DockSlot occupied_slot = DockConstants::DOCK_SLOT_MAX; TabBar *drop_tabbar = nullptr; Color valid_drop_color; @@ -186,7 +183,7 @@ protected: void drop_data(const Point2 &p_point, const Variant &p_data) override; public: - void set_slot(EditorDockManager::DockSlot p_slot); + void set_slot(DockConstants::DockSlot p_slot); EditorDockDragHint(); }; @@ -201,10 +198,9 @@ private: Button *tab_move_left_button = nullptr; Button *tab_move_right_button = nullptr; Button *close_button = nullptr; - Button *dock_to_bottom_button = nullptr; Control *dock_select = nullptr; - Rect2 dock_select_rects[EditorDockManager::DOCK_SLOT_MAX]; + Rect2 dock_select_rects[DockConstants::DOCK_SLOT_MAX]; int dock_select_rect_over_idx = -1; EditorDock *context_dock = nullptr; @@ -215,7 +211,7 @@ private: void _tab_move_right(); void _close_dock(); void _float_dock(); - void _move_dock_to_bottom(); + bool _is_slot_available(int p_slot) const; void _dock_select_input(const Ref &p_input); void _dock_select_mouse_exited(); diff --git a/editor/docks/filesystem_dock.cpp b/editor/docks/filesystem_dock.cpp index 79c1ae76e48..eaf89536317 100644 --- a/editor/docks/filesystem_dock.cpp +++ b/editor/docks/filesystem_dock.cpp @@ -614,8 +614,6 @@ void FileSystemDock::_notification(int p_what) { file_list_search_box->set_right_icon(get_editor_theme_icon(SNAME("Search"))); file_list_button_sort->set_button_icon(get_editor_theme_icon(SNAME("Sort"))); - button_dock_placement->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); - if (is_layout_rtl()) { button_hist_next->set_button_icon(get_editor_theme_icon(SNAME("Back"))); button_hist_prev->set_button_icon(get_editor_theme_icon(SNAME("Forward"))); @@ -2796,10 +2794,6 @@ void FileSystemDock::_rescan() { EditorFileSystem::get_singleton()->scan(); } -void FileSystemDock::_change_bottom_dock_placement() { - EditorDockManager::get_singleton()->bottom_dock_show_placement_popup(button_dock_placement->get_screen_rect(), this); -} - void FileSystemDock::_change_split_mode() { DisplayMode next_mode = DISPLAY_MODE_TREE_ONLY; if (display_mode == DISPLAY_MODE_VSPLIT) { @@ -4086,10 +4080,11 @@ MenuButton *FileSystemDock::_create_file_menu_button() { } void FileSystemDock::update_layout(EditorDock::DockLayout p_layout) { - horizontal = p_layout == EditorDock::DOCK_LAYOUT_HORIZONTAL; - if (button_dock_placement->is_visible() == horizontal) { + bool new_horizontal = (p_layout == EditorDock::DOCK_LAYOUT_HORIZONTAL); + if (horizontal == new_horizontal) { return; } + horizontal = new_horizontal; if (horizontal) { path_hb->reparent(toolbar_hbc, false); @@ -4119,7 +4114,6 @@ void FileSystemDock::update_layout(EditorDock::DockLayout p_layout) { set_file_list_display_mode(new_file_display_mode); set_custom_minimum_size(Size2(0, 0)); } - button_dock_placement->set_visible(horizontal); } void FileSystemDock::save_layout_to_config(Ref &p_layout, const String &p_section) const { @@ -4211,8 +4205,8 @@ FileSystemDock::FileSystemDock() { set_name(TTRC("FileSystem")); set_icon_name("Folder"); set_dock_shortcut(ED_SHORTCUT_AND_COMMAND("docks/open_filesystem", TTRC("Open FileSystem Dock"), KeyModifierMask::ALT | Key::F)); - set_default_slot(EditorDockManager::DOCK_SLOT_LEFT_BR); - set_available_layouts(DOCK_LAYOUT_VERTICAL | DOCK_LAYOUT_HORIZONTAL); + set_default_slot(DockConstants::DOCK_SLOT_LEFT_BR); + set_available_layouts(DOCK_LAYOUT_ALL); ProjectSettings::get_singleton()->add_hidden_prefix("file_customization/"); @@ -4292,13 +4286,6 @@ FileSystemDock::FileSystemDock() { button_toggle_display_mode->set_theme_type_variation("FlatMenuButton"); toolbar_hbc->add_child(button_toggle_display_mode); - button_dock_placement = memnew(Button); - button_dock_placement->set_theme_type_variation("FlatMenuButton"); - button_dock_placement->set_accessibility_name(TTRC("Dock Placement")); - button_dock_placement->connect(SceneStringName(pressed), callable_mp(this, &FileSystemDock::_change_bottom_dock_placement)); - button_dock_placement->hide(); - toolbar_hbc->add_child(button_dock_placement); - toolbar2_hbc = memnew(HBoxContainer); top_vbc->add_child(toolbar2_hbc); diff --git a/editor/docks/filesystem_dock.h b/editor/docks/filesystem_dock.h index a5e5228844a..1182586b15d 100644 --- a/editor/docks/filesystem_dock.h +++ b/editor/docks/filesystem_dock.h @@ -160,8 +160,6 @@ private: HashSet favorites; - Button *button_dock_placement = nullptr; - Button *button_toggle_display_mode = nullptr; Button *button_file_list_display_mode = nullptr; Button *button_hist_next = nullptr; @@ -379,8 +377,6 @@ private: void _project_settings_changed(); static Vector _remove_self_included_paths(Vector selected_strings); - void _change_bottom_dock_placement(); - private: inline static FileSystemDock *singleton = nullptr; diff --git a/editor/docks/history_dock.cpp b/editor/docks/history_dock.cpp index fafad00aefc..27215d5a6bd 100644 --- a/editor/docks/history_dock.cpp +++ b/editor/docks/history_dock.cpp @@ -239,7 +239,7 @@ HistoryDock::HistoryDock() { set_name(TTRC("History")); set_icon_name("History"); set_dock_shortcut(ED_SHORTCUT_AND_COMMAND("docks/open_history", TTRC("Open History Dock"))); - set_default_slot(EditorDockManager::DOCK_SLOT_RIGHT_UL); + set_default_slot(DockConstants::DOCK_SLOT_RIGHT_UL); ur_manager = EditorUndoRedoManager::get_singleton(); ur_manager->connect("history_changed", callable_mp(this, &HistoryDock::on_history_changed)); diff --git a/editor/docks/import_dock.cpp b/editor/docks/import_dock.cpp index f434f8a2d20..8b3bf1612be 100644 --- a/editor/docks/import_dock.cpp +++ b/editor/docks/import_dock.cpp @@ -747,7 +747,7 @@ ImportDock::ImportDock() { set_name(TTRC("Import")); set_icon_name("FileAccess"); set_dock_shortcut(ED_SHORTCUT_AND_COMMAND("docks/open_import", TTRC("Open Import Dock"))); - set_default_slot(EditorDockManager::DOCK_SLOT_LEFT_UR); + set_default_slot(DockConstants::DOCK_SLOT_LEFT_UR); VBoxContainer *main_vb = memnew(VBoxContainer); add_child(main_vb); diff --git a/editor/docks/inspector_dock.cpp b/editor/docks/inspector_dock.cpp index 947c2b9bf62..4f99d64e99d 100644 --- a/editor/docks/inspector_dock.cpp +++ b/editor/docks/inspector_dock.cpp @@ -719,7 +719,7 @@ InspectorDock::InspectorDock(EditorData &p_editor_data) { set_name(TTRC("Inspector")); set_icon_name("AnimationTrackList"); set_dock_shortcut(ED_SHORTCUT_AND_COMMAND("docks/open_inspector", TTRC("Open Inspector Dock"))); - set_default_slot(EditorDockManager::DOCK_SLOT_RIGHT_UL); + set_default_slot(DockConstants::DOCK_SLOT_RIGHT_UL); VBoxContainer *main_vb = memnew(VBoxContainer); add_child(main_vb); diff --git a/editor/docks/node_dock.cpp b/editor/docks/node_dock.cpp index 70c243f0c74..d70fb701e22 100644 --- a/editor/docks/node_dock.cpp +++ b/editor/docks/node_dock.cpp @@ -93,7 +93,7 @@ NodeDock::NodeDock() { set_name(TTRC("Node")); set_icon_name("Object"); set_dock_shortcut(ED_SHORTCUT_AND_COMMAND("docks/open_node", TTRC("Open Node Dock"))); - set_default_slot(EditorDockManager::DOCK_SLOT_RIGHT_UL); + set_default_slot(DockConstants::DOCK_SLOT_RIGHT_UL); VBoxContainer *main_vb = memnew(VBoxContainer); add_child(main_vb); diff --git a/editor/docks/scene_tree_dock.cpp b/editor/docks/scene_tree_dock.cpp index a9299329074..5c1ec869ad7 100644 --- a/editor/docks/scene_tree_dock.cpp +++ b/editor/docks/scene_tree_dock.cpp @@ -4716,7 +4716,7 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec set_name(TTRC("Scene")); set_icon_name("PackedScene"); set_dock_shortcut(ED_SHORTCUT_AND_COMMAND("docks/open_scene", TTRC("Open Scene Dock"))); - set_default_slot(EditorDockManager::DOCK_SLOT_LEFT_UR); + set_default_slot(DockConstants::DOCK_SLOT_LEFT_UR); singleton = this; editor_data = &p_editor_data; diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp index 4488ed5488e..cc8f7567346 100644 --- a/editor/editor_log.cpp +++ b/editor/editor_log.cpp @@ -33,6 +33,7 @@ #include "core/object/undo_redo.h" #include "core/os/keyboard.h" #include "core/version.h" +#include "editor/docks/editor_dock.h" #include "editor/docks/inspector_dock.h" #include "editor/editor_node.h" #include "editor/editor_string_names.h" @@ -42,7 +43,6 @@ #include "editor/themes/editor_scale.h" #include "modules/regex/regex.h" #include "scene/gui/separator.h" -#include "scene/gui/tab_container.h" #include "scene/main/timer.h" #include "scene/resources/font.h" @@ -286,11 +286,10 @@ void EditorLog::add_message(const String &p_msg, MessageType p_type) { } void EditorLog::_set_dock_tab_icon(Ref p_icon) { - // This is the sole reason to include "tab_container.h" here. - TabContainer *parent = Object::cast_to(get_parent()); + // TODO: Remove this hack once EditorLog is converted to a dock. + EditorDock *parent = Object::cast_to(get_parent()); if (parent) { - int idx = parent->get_tab_idx_from_control(this); - parent->set_tab_icon(idx, p_icon); + parent->set_dock_icon(p_icon); } } diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index d21f346ad7a..f5b1a1e8282 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -928,6 +928,15 @@ void EditorNode::_notification(int p_what) { case NOTIFICATION_READY: { started_timestamp = Time::get_singleton()->get_unix_time_from_system(); + // Store the default order of bottom docks. It can only be determined dynamically. + PackedStringArray bottom_docks; + bottom_docks.reserve_exact(bottom_panel->get_tab_count()); + for (int i = 0; i < bottom_panel->get_tab_count(); i++) { + EditorDock *dock = Object::cast_to(bottom_panel->get_tab_control(i)); + bottom_docks.append(dock->get_effective_layout_key()); + } + default_layout->set_value("docks", "dock_9", String(",").join(bottom_docks)); + RenderingServer::get_singleton()->viewport_set_disable_2d(get_scene_root()->get_viewport_rid(), true); RenderingServer::get_singleton()->viewport_set_environment_mode(get_viewport()->get_viewport_rid(), RenderingServer::VIEWPORT_ENVIRONMENT_DISABLED); DisplayServer::get_singleton()->screen_set_keep_on(EDITOR_GET("interface/editor/keep_screen_on")); @@ -8231,13 +8240,13 @@ EditorNode::EditorNode() { left_l_vsplit->set_vertical(true); left_l_hsplit->add_child(left_l_vsplit); - TabContainer *dock_slot[EditorDockManager::DOCK_SLOT_MAX]; - dock_slot[EditorDockManager::DOCK_SLOT_LEFT_UL] = memnew(TabContainer); - dock_slot[EditorDockManager::DOCK_SLOT_LEFT_UL]->set_name("DockSlotLeftUL"); - left_l_vsplit->add_child(dock_slot[EditorDockManager::DOCK_SLOT_LEFT_UL]); - dock_slot[EditorDockManager::DOCK_SLOT_LEFT_BL] = memnew(TabContainer); - dock_slot[EditorDockManager::DOCK_SLOT_LEFT_BL]->set_name("DockSlotLeftBL"); - left_l_vsplit->add_child(dock_slot[EditorDockManager::DOCK_SLOT_LEFT_BL]); + TabContainer *dock_slot[DockConstants::DOCK_SLOT_MAX]; + dock_slot[DockConstants::DOCK_SLOT_LEFT_UL] = memnew(TabContainer); + dock_slot[DockConstants::DOCK_SLOT_LEFT_UL]->set_name("DockSlotLeftUL"); + left_l_vsplit->add_child(dock_slot[DockConstants::DOCK_SLOT_LEFT_UL]); + dock_slot[DockConstants::DOCK_SLOT_LEFT_BL] = memnew(TabContainer); + dock_slot[DockConstants::DOCK_SLOT_LEFT_BL]->set_name("DockSlotLeftBL"); + left_l_vsplit->add_child(dock_slot[DockConstants::DOCK_SLOT_LEFT_BL]); left_r_hsplit = memnew(DockSplitContainer); left_r_hsplit->set_name("DockHSplitLeftR"); @@ -8246,12 +8255,12 @@ EditorNode::EditorNode() { left_r_vsplit->set_name("DockVSplitLeftR"); left_r_vsplit->set_vertical(true); left_r_hsplit->add_child(left_r_vsplit); - dock_slot[EditorDockManager::DOCK_SLOT_LEFT_UR] = memnew(TabContainer); - dock_slot[EditorDockManager::DOCK_SLOT_LEFT_UR]->set_name("DockSlotLeftUR"); - left_r_vsplit->add_child(dock_slot[EditorDockManager::DOCK_SLOT_LEFT_UR]); - dock_slot[EditorDockManager::DOCK_SLOT_LEFT_BR] = memnew(TabContainer); - dock_slot[EditorDockManager::DOCK_SLOT_LEFT_BR]->set_name("DockSlotLeftBR"); - left_r_vsplit->add_child(dock_slot[EditorDockManager::DOCK_SLOT_LEFT_BR]); + dock_slot[DockConstants::DOCK_SLOT_LEFT_UR] = memnew(TabContainer); + dock_slot[DockConstants::DOCK_SLOT_LEFT_UR]->set_name("DockSlotLeftUR"); + left_r_vsplit->add_child(dock_slot[DockConstants::DOCK_SLOT_LEFT_UR]); + dock_slot[DockConstants::DOCK_SLOT_LEFT_BR] = memnew(TabContainer); + dock_slot[DockConstants::DOCK_SLOT_LEFT_BR]->set_name("DockSlotLeftBR"); + left_r_vsplit->add_child(dock_slot[DockConstants::DOCK_SLOT_LEFT_BR]); main_hsplit = memnew(DockSplitContainer); main_hsplit->set_name("DockHSplitMain"); @@ -8277,23 +8286,23 @@ EditorNode::EditorNode() { right_l_vsplit->set_name("DockVSplitRightL"); right_l_vsplit->set_vertical(true); right_hsplit->add_child(right_l_vsplit); - dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_UL] = memnew(TabContainer); - dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_UL]->set_name("DockSlotRightUL"); - right_l_vsplit->add_child(dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_UL]); - dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_BL] = memnew(TabContainer); - dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_BL]->set_name("DockSlotRightBL"); - right_l_vsplit->add_child(dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_BL]); + dock_slot[DockConstants::DOCK_SLOT_RIGHT_UL] = memnew(TabContainer); + dock_slot[DockConstants::DOCK_SLOT_RIGHT_UL]->set_name("DockSlotRightUL"); + right_l_vsplit->add_child(dock_slot[DockConstants::DOCK_SLOT_RIGHT_UL]); + dock_slot[DockConstants::DOCK_SLOT_RIGHT_BL] = memnew(TabContainer); + dock_slot[DockConstants::DOCK_SLOT_RIGHT_BL]->set_name("DockSlotRightBL"); + right_l_vsplit->add_child(dock_slot[DockConstants::DOCK_SLOT_RIGHT_BL]); right_r_vsplit = memnew(DockSplitContainer); right_r_vsplit->set_name("DockVSplitRightR"); right_r_vsplit->set_vertical(true); right_hsplit->add_child(right_r_vsplit); - dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_UR] = memnew(TabContainer); - dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_UR]->set_name("DockSlotRightUR"); - right_r_vsplit->add_child(dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_UR]); - dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_BR] = memnew(TabContainer); - dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_BR]->set_name("DockSlotRightBR"); - right_r_vsplit->add_child(dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_BR]); + dock_slot[DockConstants::DOCK_SLOT_RIGHT_UR] = memnew(TabContainer); + dock_slot[DockConstants::DOCK_SLOT_RIGHT_UR]->set_name("DockSlotRightUR"); + right_r_vsplit->add_child(dock_slot[DockConstants::DOCK_SLOT_RIGHT_UR]); + dock_slot[DockConstants::DOCK_SLOT_RIGHT_BR] = memnew(TabContainer); + dock_slot[DockConstants::DOCK_SLOT_RIGHT_BR]->set_name("DockSlotRightBR"); + right_r_vsplit->add_child(dock_slot[DockConstants::DOCK_SLOT_RIGHT_BR]); editor_dock_manager = memnew(EditorDockManager); @@ -8308,8 +8317,8 @@ EditorNode::EditorNode() { editor_dock_manager->add_hsplit(main_hsplit); editor_dock_manager->add_hsplit(right_hsplit); - for (int i = 0; i < EditorDockManager::DOCK_SLOT_MAX; i++) { - editor_dock_manager->register_dock_slot((EditorDockManager::DockSlot)i, dock_slot[i]); + for (int i = 0; i < DockConstants::DOCK_SLOT_BOTTOM; i++) { + editor_dock_manager->register_dock_slot((DockConstants::DockSlot)i, dock_slot[i], DockConstants::DOCK_LAYOUT_VERTICAL); } editor_layout_save_delay_timer = memnew(Timer); @@ -8774,6 +8783,7 @@ EditorNode::EditorNode() { // Bottom panels. bottom_panel = memnew(EditorBottomPanel); + editor_dock_manager->register_dock_slot(DockConstants::DOCK_SLOT_BOTTOM, bottom_panel, DockConstants::DOCK_LAYOUT_HORIZONTAL); bottom_panel->set_theme_type_variation("BottomPanel"); center_split->add_child(bottom_panel); center_split->set_dragger_visibility(SplitContainer::DRAGGER_HIDDEN); diff --git a/editor/gui/editor_bottom_panel.cpp b/editor/gui/editor_bottom_panel.cpp index 6a4e3c02ef1..25d238cbcd8 100644 --- a/editor/gui/editor_bottom_panel.cpp +++ b/editor/gui/editor_bottom_panel.cpp @@ -31,6 +31,7 @@ #include "editor_bottom_panel.h" #include "editor/debugger/editor_debugger_node.h" +#include "editor/docks/editor_dock.h" #include "editor/docks/editor_dock_manager.h" #include "editor/editor_node.h" #include "editor/editor_string_names.h" @@ -41,10 +42,15 @@ #include "editor/themes/editor_scale.h" #include "scene/gui/box_container.h" #include "scene/gui/button.h" +#include "scene/gui/separator.h" #include "scene/gui/split_container.h" void EditorBottomPanel::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_READY: { + layout_popup = get_popup(); + } break; + case NOTIFICATION_THEME_CHANGED: { pin_button->set_button_icon(get_editor_theme_icon(SNAME("Pin"))); expand_button->set_button_icon(get_editor_theme_icon(SNAME("ExpandBottomDock"))); @@ -58,6 +64,8 @@ void EditorBottomPanel::_on_tab_changed(int p_idx) { } void EditorBottomPanel::_theme_changed() { + // Add margin to make space for the right side buttons. + icon_spacer->set_custom_minimum_size(Vector2(get_theme_constant("class_icon_size", EditorStringName(Editor)), 0)); Ref bottom_tabbar_style = EditorNode::get_singleton()->get_editor_theme()->get_stylebox("tabbar_background", "BottomPanel")->duplicate(); bottom_tabbar_style->set_content_margin(SIDE_RIGHT, bottom_hbox->get_minimum_size().x + bottom_tabbar_style->get_content_margin(SIDE_LEFT)); add_theme_style_override("tabbar_background", bottom_tabbar_style); @@ -91,9 +99,16 @@ int EditorBottomPanel::get_bottom_panel_offset() { void EditorBottomPanel::_repaint() { bool panel_collapsed = get_current_tab() == -1; - if (!panel_collapsed && (get_previous_tab() != -1)) { + + if (panel_collapsed && get_popup()) { + set_popup(nullptr); + } else if (!panel_collapsed && !get_popup()) { + set_popup(layout_popup); + } + if (!panel_collapsed && (previous_tab != -1)) { return; } + previous_tab = get_current_tab(); DockSplitContainer *center_split = EditorNode::get_center_split(); ERR_FAIL_NULL(center_split); @@ -124,23 +139,6 @@ void EditorBottomPanel::load_layout_from_config(Ref p_config_file, c String key = name.to_snake_case(); dock_offsets[key] = p_config_file->get_value(p_section, "dock_" + key + "_offset", 0); } - - if (p_config_file->has_section_key(p_section, "selected_bottom_panel_item")) { - int stored_current_tab = p_config_file->get_value(p_section, "selected_bottom_panel_item"); - - if (stored_current_tab >= 0 && stored_current_tab < get_tab_bar()->get_tab_count()) { - // Make sure we don't try to open contextual editors which are not enabled in the current context. - if (!get_tab_bar()->is_tab_hidden(stored_current_tab)) { - set_current_tab(stored_current_tab); - - callable_mp(this, &EditorBottomPanel::_update_center_split_offset).call_deferred(); - return; - } - } - } - - // If there is no active tab we need to collapse the panel. - set_current_tab(-1); } void EditorBottomPanel::make_item_visible(Control *p_item, bool p_visible, bool p_ignore_lock) { @@ -149,11 +147,15 @@ void EditorBottomPanel::make_item_visible(Control *p_item, bool p_visible, bool return; } - p_item->set_visible(p_visible); + EditorDock *dock = _get_dock_from_control(p_item); + ERR_FAIL_NULL(dock); + dock->set_visible(p_visible); } void EditorBottomPanel::move_item_to_end(Control *p_item) { - move_child(p_item, -1); + EditorDock *dock = _get_dock_from_control(p_item); + ERR_FAIL_NULL(dock); + move_child(dock, -1); } void EditorBottomPanel::hide_bottom_panel() { @@ -164,19 +166,6 @@ void EditorBottomPanel::toggle_last_opened_bottom_panel() { set_current_tab(get_current_tab() == -1 ? get_previous_tab() : -1); } -void EditorBottomPanel::shortcut_input(const Ref &p_event) { - if (p_event.is_null() || !p_event->is_pressed() || p_event->is_echo()) { - return; - } - - for (uint32_t i = 0; i < dock_shortcuts.size(); i++) { - if (dock_shortcuts[i].is_valid() && dock_shortcuts[i]->matches_event(p_event)) { - bottom_docks[i]->set_visible(!bottom_docks[i]->is_visible()); - break; - } - } -} - void EditorBottomPanel::_pin_button_toggled(bool p_pressed) { lock_panel_switching = p_pressed; } @@ -208,59 +197,74 @@ void EditorBottomPanel::_update_center_split_offset() { center_split->set_split_offset(get_bottom_panel_offset()); } -Button *EditorBottomPanel::add_item(String p_text, Control *p_item, const Ref &p_shortcut, bool p_at_front) { - p_item->set_name(p_text); - add_child(p_item); - if (p_at_front) { - move_child(p_item, 0); - } - bottom_docks.push_back(p_item); - dock_shortcuts.push_back(p_shortcut); +EditorDock *EditorBottomPanel::_get_dock_from_control(Control *p_control) const { + return Object::cast_to(p_control->get_parent()); +} - set_process_shortcut_input(is_processing_shortcut_input() || p_shortcut.is_valid()); +Button *EditorBottomPanel::add_item(String p_text, Control *p_item, const Ref &p_shortcut, bool p_at_front) { + EditorDock *dock = memnew(EditorDock); + dock->add_child(p_item); + dock->set_title(p_text); + dock->set_dock_shortcut(p_shortcut); + dock->set_global(false); + dock->set_transient(true); + dock->set_default_slot(DockConstants::DOCK_SLOT_BOTTOM); + dock->set_available_layouts(EditorDock::DOCK_LAYOUT_HORIZONTAL); + EditorDockManager::get_singleton()->add_dock(dock); + bottom_docks.push_back(dock); + + p_item->show(); // Compatibility in case it was hidden. // Still return a dummy button for compatibility reasons. Button *tb = memnew(Button); tb->set_toggle_mode(true); - tb->connect(SceneStringName(visibility_changed), callable_mp(this, &EditorBottomPanel::_on_button_visibility_changed).bind(tb, p_item)); + tb->connect(SceneStringName(visibility_changed), callable_mp(this, &EditorBottomPanel::_on_button_visibility_changed).bind(tb, dock)); legacy_buttons.push_back(tb); return tb; } void EditorBottomPanel::remove_item(Control *p_item) { - int item_idx = bottom_docks.find(p_item); - ERR_FAIL_COND_MSG(item_idx == -1, vformat("Cannot remove unknown dock \"%s\" from the bottom panel.", p_item->get_name())); + EditorDock *dock = _get_dock_from_control(p_item); + ERR_FAIL_NULL_MSG(dock, vformat("Cannot remove unknown dock \"%s\" from the bottom panel.", p_item->get_name())); + + int item_idx = bottom_docks.find(dock); + ERR_FAIL_COND(item_idx == -1); bottom_docks.remove_at(item_idx); - dock_shortcuts.remove_at(item_idx); legacy_buttons[item_idx]->queue_free(); legacy_buttons.remove_at(item_idx); - remove_child(p_item); + EditorDockManager::get_singleton()->remove_dock(dock); + dock->remove_child(p_item); + dock->queue_free(); } -void EditorBottomPanel::_on_button_visibility_changed(Button *p_button, Control *p_control) { - int tab_index = get_tab_idx_from_control(p_control); - if (tab_index == -1) { - return; +void EditorBottomPanel::_on_button_visibility_changed(Button *p_button, EditorDock *p_dock) { + if (p_button->is_visible()) { + p_dock->open(); + } else { + p_dock->close(); } - - // Ignore the tab if the button is hidden. - get_tab_bar()->set_tab_hidden(tab_index, !p_button->is_visible()); } EditorBottomPanel::EditorBottomPanel() { - get_tab_bar()->connect("tab_rmb_clicked", callable_mp(EditorDockManager::get_singleton(), &EditorDockManager::_dock_container_popup).bind(this)); get_tab_bar()->connect("tab_changed", callable_mp(this, &EditorBottomPanel::_on_tab_changed)); set_tabs_position(TabPosition::POSITION_BOTTOM); set_deselect_enabled(true); bottom_hbox = memnew(HBoxContainer); + bottom_hbox->set_mouse_filter(MOUSE_FILTER_IGNORE); bottom_hbox->set_anchors_and_offsets_preset(Control::PRESET_RIGHT_WIDE); bottom_hbox->set_h_grow_direction(Control::GROW_DIRECTION_END); get_tab_bar()->add_child(bottom_hbox); + icon_spacer = memnew(Control); + icon_spacer->set_mouse_filter(MOUSE_FILTER_IGNORE); + bottom_hbox->add_child(icon_spacer); + + bottom_hbox->add_child(memnew(VSeparator)); + editor_toaster = memnew(EditorToaster); bottom_hbox->add_child(editor_toaster); diff --git a/editor/gui/editor_bottom_panel.h b/editor/gui/editor_bottom_panel.h index 12afc61bf4a..6ca2c88990f 100644 --- a/editor/gui/editor_bottom_panel.h +++ b/editor/gui/editor_bottom_panel.h @@ -34,6 +34,7 @@ class Button; class ConfigFile; +class EditorDock; class EditorToaster; class HBoxContainer; @@ -41,29 +42,31 @@ class EditorBottomPanel : public TabContainer { GDCLASS(EditorBottomPanel, TabContainer); HBoxContainer *bottom_hbox = nullptr; + Control *icon_spacer = nullptr; EditorToaster *editor_toaster = nullptr; Button *pin_button = nullptr; Button *expand_button = nullptr; + Popup *layout_popup = nullptr; + int previous_tab = -1; bool lock_panel_switching = false; LocalVector bottom_docks; LocalVector> dock_shortcuts; HashMap dock_offsets; LocalVector