From 4274f8b543b1680d90000278d7c984da718bb7f4 Mon Sep 17 00:00:00 2001 From: kobewi Date: Wed, 26 Nov 2025 16:43:49 +0100 Subject: [PATCH] Replace DockSlot with DockTabContainer --- editor/docks/dock_tab_container.cpp | 270 +++++++++++++++ editor/docks/dock_tab_container.h | 120 +++++++ editor/docks/editor_dock.cpp | 72 ++++ editor/docks/editor_dock.h | 8 + editor/docks/editor_dock_manager.cpp | 447 ++++--------------------- editor/docks/editor_dock_manager.h | 50 +-- editor/editor_node.cpp | 81 +++-- editor/gui/editor_bottom_panel.cpp | 40 ++- editor/gui/editor_bottom_panel.h | 14 +- editor/shader/shader_editor_plugin.cpp | 2 +- 10 files changed, 646 insertions(+), 458 deletions(-) create mode 100644 editor/docks/dock_tab_container.cpp create mode 100644 editor/docks/dock_tab_container.h diff --git a/editor/docks/dock_tab_container.cpp b/editor/docks/dock_tab_container.cpp new file mode 100644 index 00000000000..705060a9221 --- /dev/null +++ b/editor/docks/dock_tab_container.cpp @@ -0,0 +1,270 @@ +/**************************************************************************/ +/* dock_tab_container.cpp */ +/**************************************************************************/ +/* 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. */ +/**************************************************************************/ + +#include "dock_tab_container.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" +#include "editor/settings/editor_settings.h" +#include "editor/themes/editor_scale.h" +#include "scene/resources/style_box_flat.h" + +bool EditorDockDragHint::can_drop_data(const Point2 &p_point, const Variant &p_data) const { + return can_drop_dock; +} + +void EditorDockDragHint::drop_data(const Point2 &p_point, const Variant &p_data) { + // Drop dock into last spot if not over tabbar. + 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 { + EditorDockManager *dock_manager = EditorDockManager::get_singleton(); + dock_manager->_move_dock(dock_manager->_get_dock_tab_dragged(), dock_container, drop_tabbar->get_tab_count()); + } +} + +void EditorDockDragHint::_drag_move_tab(int p_from_index, int p_to_index) { + dock_container->get_dock(p_from_index)->set_tab_index(p_to_index, true); +} + +void EditorDockDragHint::_drag_move_tab_from(TabBar *p_from_tabbar, int p_from_index, int p_to_index) { + EditorDockManager *dock_manager = EditorDockManager::get_singleton(); + dock_manager->_move_dock(dock_manager->_get_dock_tab_dragged(), dock_container, p_to_index); +} + +void EditorDockDragHint::gui_input(const Ref &p_event) { + ERR_FAIL_COND(p_event.is_null()); + + Ref mm = p_event; + if (mm.is_valid()) { + Point2 pos = mm->get_position(); + + // Redraw when inside the tabbar and just exited. + if (mouse_inside_tabbar) { + queue_redraw(); + } + mouse_inside_tabbar = drop_tabbar->get_rect().has_point(pos); + } +} + +void EditorDockDragHint::set_slot(DockTabContainer *p_slot) { + dock_container = p_slot; + drop_tabbar = p_slot->get_tab_bar(); +} + +void EditorDockDragHint::_notification(int p_what) { + switch (p_what) { + case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { + if (EditorSettings::get_singleton()->check_changed_settings_in_group("interface/theme")) { + dock_drop_highlight->set_corner_radius_all(EDSCALE * EDITOR_GET("interface/theme/corner_radius").operator int()); + if (mouse_inside) { + queue_redraw(); + } + } + } break; + + case NOTIFICATION_THEME_CHANGED: { + valid_drop_color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor)); + } break; + + case NOTIFICATION_MOUSE_ENTER: + case NOTIFICATION_MOUSE_EXIT: { + mouse_inside = p_what == NOTIFICATION_MOUSE_ENTER; + queue_redraw(); + } break; + + case NOTIFICATION_DRAG_BEGIN: { + EditorDock *dragged_dock = EditorDockManager::get_singleton()->_get_dock_tab_dragged(); + if (!dragged_dock) { + return; + } + + can_drop_dock = dragged_dock->get_available_layouts() & dock_container->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)); + } break; + case NOTIFICATION_DRAG_END: { + EditorDockManager::get_singleton()->_dock_drag_stopped(); + can_drop_dock = false; + mouse_inside = false; + hide(); + } break; + + case NOTIFICATION_DRAW: { + if (!mouse_inside || !can_drop_dock) { + return; + } + + // Draw highlights around docks that can be dropped. + Rect2 dock_rect = Rect2(Point2(), get_size()).grow(2 * EDSCALE); + draw_style_box(dock_drop_highlight, dock_rect); + + // 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; + } +} + +EditorDockDragHint::EditorDockDragHint() { + set_as_top_level(true); + hide(); + + dock_drop_highlight.instantiate(); + dock_drop_highlight->set_corner_radius_all(EDSCALE * EDITOR_GET("interface/theme/corner_radius").operator int()); + dock_drop_highlight->set_border_width_all(Math::round(2 * EDSCALE)); +} + +void DockTabContainer::_pre_popup() { + dock_context_popup->set_dock(get_dock(get_current_tab())); +} + +void DockTabContainer::_tab_rmb_clicked(int p_tab_idx) { + EditorDock *hovered_dock = get_dock(p_tab_idx); + if (!hovered_dock) { + return; + } + + // Right click context menu. + dock_context_popup->set_dock(hovered_dock); + dock_context_popup->set_position(get_tab_bar()->get_screen_position() + get_tab_bar()->get_local_mouse_position()); + dock_context_popup->popup(); +} + +void DockTabContainer::_notification(int p_what) { + if (p_what == NOTIFICATION_POSTINITIALIZE) { + connect("pre_popup_pressed", callable_mp(this, &DockTabContainer::_pre_popup)); + connect("child_order_changed", callable_mp(this, &DockTabContainer::update_visibility)); + } +} + +void DockTabContainer::update_visibility() { + // Hide the dock container if there are no tabs. + set_visible(EditorDockManager::get_singleton()->are_docks_visible() && get_tab_count() > 0); +} + +DockTabContainer::TabStyle DockTabContainer::get_tab_style() const { + return (TabStyle)EDITOR_GET("interface/editor/dock_tab_style").operator int(); +} + +bool DockTabContainer::can_switch_dock() const { + return EditorDockManager::get_singleton()->are_docks_visible(); +} + +void DockTabContainer::save_docks_to_config(Ref p_layout, const String &p_section) { + PackedStringArray names; + names.reserve_exact(get_tab_count()); + for (int i = 0; i < get_tab_count(); i++) { + const String name = get_dock(i)->get_effective_layout_key(); + names.append(name); + } + + const String config_key = DockTabContainer::get_config_key(dock_slot); + if (!names.is_empty()) { + p_layout->set_value(p_section, config_key, String(",").join(names)); + } else if (p_layout->has_section_key(p_section, config_key)) { + p_layout->erase_section_key(p_section, config_key); + } + + const String tab_key = config_key + "_selected_tab_idx"; + int selected_tab_idx = get_current_tab(); + if (selected_tab_idx >= 0) { + 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); + } +} + +void DockTabContainer::load_selected_tab(int p_idx) { + EditorDock *selected_dock = get_dock(p_idx); + if (!selected_dock) { + return; + } + set_block_signals(true); + set_current_tab(p_idx); + set_block_signals(false); +} + +void DockTabContainer::set_dock_context_popup(DockContextPopup *p_popup) { + dock_context_popup = p_popup; + set_popup(dock_context_popup); +} + +void DockTabContainer::move_dock_index(EditorDock *p_dock, int p_to_index, bool p_set_current) { + set_block_signals(true); + int target_index = CLAMP(p_to_index, 0, get_tab_count() - 1); + move_child(p_dock, get_dock(target_index)->get_index(false)); + + if (p_set_current) { + set_current_tab(target_index); + } + set_block_signals(false); +} + +EditorDock *DockTabContainer::get_dock(int p_idx) const { + return Object::cast_to(get_tab_control(p_idx)); +} + +void DockTabContainer::show_drag_hint() { + if (!is_visible_in_tree()) { + return; + } + drag_hint->set_rect(get_global_rect()); + drag_hint->show(); +} + +DockTabContainer::DockTabContainer(EditorDock::DockSlot p_slot) { + ERR_FAIL_INDEX(p_slot, EditorDock::DOCK_SLOT_MAX); + dock_slot = p_slot; + + set_drag_to_rearrange_enabled(true); + set_tabs_rearrange_group(1); + hide(); + + drag_hint = memnew(EditorDockDragHint); + drag_hint->set_slot(this); + drag_hint->hide(); + EditorNode::get_singleton()->get_gui_base()->add_child(drag_hint); + + get_tab_bar()->set_switch_on_release(true); + get_tab_bar()->connect("tab_rmb_clicked", callable_mp(this, &DockTabContainer::_tab_rmb_clicked)); +} + +SideDockTabContainer::SideDockTabContainer(EditorDock::DockSlot p_slot) : + DockTabContainer(p_slot) { + set_custom_minimum_size(Size2(170 * EDSCALE, 0)); + set_v_size_flags(Control::SIZE_EXPAND_FILL); + set_use_hidden_tabs_for_min_size(true); +} diff --git a/editor/docks/dock_tab_container.h b/editor/docks/dock_tab_container.h new file mode 100644 index 00000000000..5132beebe05 --- /dev/null +++ b/editor/docks/dock_tab_container.h @@ -0,0 +1,120 @@ +/**************************************************************************/ +/* dock_tab_container.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 + +#include "editor/docks/editor_dock.h" +#include "scene/gui/tab_container.h" + +class ConfigFile; +class DockContextPopup; +class DockTabContainer; +class EditorDockManager; +class StyleBoxFlat; + +class EditorDockDragHint : public Control { + GDCLASS(EditorDockDragHint, Control); + + DockTabContainer *dock_container = nullptr; + TabBar *drop_tabbar = nullptr; + + Color valid_drop_color; + Ref dock_drop_highlight; + bool can_drop_dock = false; + bool mouse_inside = false; + bool mouse_inside_tabbar = false; + + void _drag_move_tab(int p_from_index, int p_to_index); + void _drag_move_tab_from(TabBar *p_from_tabbar, int p_from_index, int p_to_index); + +protected: + virtual void gui_input(const Ref &p_event) override; + + void _notification(int p_what); + virtual bool can_drop_data(const Point2 &p_point, const Variant &p_data) const override; + virtual void drop_data(const Point2 &p_point, const Variant &p_data) override; + +public: + void set_slot(DockTabContainer *p_slot); + + EditorDockDragHint(); +}; + +class DockTabContainer : public TabContainer { + GDCLASS(DockTabContainer, TabContainer); + + EditorDockDragHint *drag_hint = nullptr; + + void _pre_popup(); + void _tab_rmb_clicked(int p_tab_idx); + +protected: + DockContextPopup *dock_context_popup = nullptr; + + void _notification(int p_what); + +public: + enum class TabStyle { + TEXT_ONLY, + ICON_ONLY, + TEXT_AND_ICON, + }; + + EditorDock::DockSlot dock_slot = EditorDock::DOCK_SLOT_NONE; + EditorDock::DockLayout layout = EditorDock::DOCK_LAYOUT_VERTICAL; + + static String get_config_key(int p_idx) { return "dock_" + itos(p_idx + 1); } + + virtual void dock_closed(EditorDock *p_dock) {} + virtual void dock_focused(EditorDock *p_dock, bool p_was_visible) {} + virtual void update_visibility(); + virtual TabStyle get_tab_style() const; + virtual bool can_switch_dock() const; + + // There is no equivalent load method, because loading needs to handle floating and closing. + void save_docks_to_config(Ref p_layout, const String &p_section); + virtual void load_selected_tab(int p_idx); + + // This method should only be called by EditorDock. + void move_dock_index(EditorDock *p_dock, int p_to_index, bool p_set_current); + + void set_dock_context_popup(DockContextPopup *p_popup); + EditorDock *get_dock(int p_idx) const; + void show_drag_hint(); + + DockTabContainer(EditorDock::DockSlot p_slot); +}; + +class SideDockTabContainer : public DockTabContainer { + GDCLASS(SideDockTabContainer, DockTabContainer); + +public: + SideDockTabContainer(EditorDock::DockSlot p_slot); +}; diff --git a/editor/docks/editor_dock.cpp b/editor/docks/editor_dock.cpp index 5a074587788..49ba0c227c1 100644 --- a/editor/docks/editor_dock.cpp +++ b/editor/docks/editor_dock.cpp @@ -32,6 +32,7 @@ #include "core/input/shortcut.h" #include "core/io/config_file.h" +#include "editor/docks/dock_tab_container.h" #include "editor/docks/editor_dock_manager.h" void EditorDock::_set_default_slot_bind(DockSlot p_slot) { @@ -49,6 +50,14 @@ void EditorDock::_notification(int p_what) { set_accessibility_region(true); set_accessibility_name(get_display_title()); } break; + + case NOTIFICATION_PARENTED: { + parent_dock_container = Object::cast_to(get_parent()); + } break; + + case NOTIFICATION_UNPARENTED: { + parent_dock_container = nullptr; + } break; } } @@ -140,6 +149,10 @@ void EditorDock::make_visible() { EditorDockManager::get_singleton()->open_dock(this, true); } +void EditorDock::make_floating() { + EditorDockManager::get_singleton()->make_dock_floating(this); +} + void EditorDock::close() { if (is_open) { EditorDockManager::get_singleton()->close_dock(this); @@ -242,3 +255,62 @@ String EditorDock::get_display_title() const { String EditorDock::get_effective_layout_key() const { return layout_key.is_empty() ? get_display_title() : layout_key; } + +void EditorDock::set_tab_index(int p_index, bool p_set_current) { + parent_dock_container->move_dock_index(this, p_index, p_set_current); + previous_tab_index = parent_dock_container->get_tab_idx_from_control(this); +} + +void EditorDock::update_tab_style() { + if (!enabled || !is_open) { + return; // Disabled by feature profile or manually closed by user. + } + if (dock_window) { + return; // Floating. + } + + ERR_FAIL_NULL(parent_dock_container); + + int index = parent_dock_container->get_tab_idx_from_control(this); + ERR_FAIL_COND(index == -1); + + parent_dock_container->get_tab_bar()->set_font_color_override_all(index, title_color); + + const Ref icon = get_effective_icon(callable_mp((Control *)this, &Control::get_editor_theme_icon)); + bool assign_icon = force_show_icon; + String tooltip; + switch (parent_dock_container->get_tab_style()) { + case DockTabContainer::TabStyle::TEXT_ONLY: { + parent_dock_container->set_tab_title(index, get_display_title()); + } break; + case DockTabContainer::TabStyle::ICON_ONLY: { + parent_dock_container->set_tab_title(index, icon.is_valid() ? String() : get_display_title()); + tooltip = TTR(get_display_title()); + assign_icon = true; + } break; + case DockTabContainer::TabStyle::TEXT_AND_ICON: { + parent_dock_container->set_tab_title(index, get_display_title()); + parent_dock_container->set_tab_tooltip(index, String()); + assign_icon = true; + } break; + } + + if (shortcut.is_valid() && shortcut->has_valid_event()) { + tooltip += (tooltip.is_empty() ? "" : "\n") + TTR(shortcut->get_name()) + " (" + shortcut->get_as_text() + ")"; + } + parent_dock_container->set_tab_tooltip(index, tooltip); + + if (assign_icon) { + parent_dock_container->set_tab_icon(index, icon); + } else { + parent_dock_container->set_tab_icon(index, Ref()); + } +} + +Ref EditorDock::get_effective_icon(const Callable &p_icon_fetch) { + Ref icon = dock_icon; + if (icon.is_null() && !icon_name.is_empty()) { + icon = p_icon_fetch.call(icon_name); + } + return icon; +} diff --git a/editor/docks/editor_dock.h b/editor/docks/editor_dock.h index 10d022393e9..a69211436c7 100644 --- a/editor/docks/editor_dock.h +++ b/editor/docks/editor_dock.h @@ -33,6 +33,7 @@ #include "core/io/config_file.h" #include "scene/gui/margin_container.h" +class DockTabContainer; class Shortcut; class WindowWrapper; @@ -85,6 +86,7 @@ private: bool enabled = true; int previous_tab_index = -1; WindowWrapper *dock_window = nullptr; + DockTabContainer *parent_dock_container = nullptr; int dock_slot_index = DOCK_SLOT_NONE; void _set_default_slot_bind(DockSlot p_slot); @@ -103,6 +105,7 @@ protected: public: void open(); void make_visible(); + void make_floating(); void close(); void set_title(const String &p_title); @@ -144,6 +147,11 @@ public: String get_display_title() const; String get_effective_layout_key() const; + DockTabContainer *get_parent_container() const { return parent_dock_container; } + void set_tab_index(int p_index, bool p_set_current); + void update_tab_style(); + Ref get_effective_icon(const Callable &p_icon_fetch); + virtual void update_layout(DockLayout p_layout) { GDVIRTUAL_CALL(_update_layout, p_layout); } DockLayout get_current_layout() const { return current_layout; } diff --git a/editor/docks/editor_dock_manager.cpp b/editor/docks/editor_dock_manager.cpp index b7a7a025a0f..2446b8a7e0f 100644 --- a/editor/docks/editor_dock_manager.cpp +++ b/editor/docks/editor_dock_manager.cpp @@ -37,135 +37,13 @@ #include "scene/gui/tab_container.h" #include "scene/main/window.h" +#include "editor/docks/dock_tab_container.h" #include "editor/docks/editor_dock.h" #include "editor/editor_node.h" #include "editor/editor_string_names.h" -#include "editor/gui/editor_bottom_panel.h" #include "editor/gui/window_wrapper.h" #include "editor/settings/editor_settings.h" #include "editor/themes/editor_scale.h" -#include "scene/resources/style_box_flat.h" - -enum class TabStyle { - TEXT_ONLY, - ICON_ONLY, - TEXT_AND_ICON, -}; - -static inline Ref _get_dock_icon(const EditorDock *p_dock, const Callable &p_icon_fetch) { - Ref icon = p_dock->get_dock_icon(); - if (icon.is_null() && !p_dock->get_icon_name().is_empty()) { - icon = p_icon_fetch.call(p_dock->get_icon_name()); - } - return icon; -} - -bool EditorDockDragHint::can_drop_data(const Point2 &p_point, const Variant &p_data) const { - return can_drop_dock; -} - -void EditorDockDragHint::drop_data(const Point2 &p_point, const Variant &p_data) { - // Drop dock into last spot if not over tabbar. - 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_slots[occupied_slot].container, drop_tabbar->get_tab_count()); - } -} - -void EditorDockDragHint::_drag_move_tab(int p_from_index, int p_to_index) { - dock_manager->_move_dock_tab_index(dock_manager->_get_dock_tab_dragged(), p_to_index, true); -} - -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_slots[occupied_slot].container, p_to_index); -} - -void EditorDockDragHint::gui_input(const Ref &p_event) { - ERR_FAIL_COND(p_event.is_null()); - - Ref mm = p_event; - if (mm.is_valid()) { - Point2 pos = mm->get_position(); - - // Redraw when inside the tabbar and just exited. - if (mouse_inside_tabbar) { - queue_redraw(); - } - mouse_inside_tabbar = drop_tabbar->get_rect().has_point(pos); - } -} - -void EditorDockDragHint::set_slot(EditorDock::DockSlot p_slot) { - occupied_slot = p_slot; - drop_tabbar = dock_manager->dock_slots[occupied_slot].container->get_tab_bar(); -} - -void EditorDockDragHint::_notification(int p_what) { - switch (p_what) { - case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { - if (EditorSettings::get_singleton()->check_changed_settings_in_group("interface/theme")) { - dock_drop_highlight->set_corner_radius_all(EDSCALE * EDITOR_GET("interface/theme/corner_radius").operator int()); - if (mouse_inside) { - queue_redraw(); - } - } - } break; - - case NOTIFICATION_THEME_CHANGED: { - valid_drop_color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor)); - } break; - - case NOTIFICATION_MOUSE_ENTER: - case NOTIFICATION_MOUSE_EXIT: { - mouse_inside = p_what == NOTIFICATION_MOUSE_ENTER; - queue_redraw(); - } break; - - case NOTIFICATION_DRAG_BEGIN: { - EditorDock *dragged_dock = dock_manager->_get_dock_tab_dragged(); - if (!dragged_dock) { - return; - } - - can_drop_dock = dragged_dock->get_available_layouts() & 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)); - } break; - case NOTIFICATION_DRAG_END: { - dock_manager->_dock_drag_stopped(); - can_drop_dock = false; - mouse_inside = false; - hide(); - } break; - - case NOTIFICATION_DRAW: { - if (!mouse_inside || !can_drop_dock) { - return; - } - - // Draw highlights around docks that can be dropped. - Rect2 dock_rect = Rect2(Point2(), get_size()).grow(2 * EDSCALE); - draw_style_box(dock_drop_highlight, dock_rect); - - // 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; - } -} - -EditorDockDragHint::EditorDockDragHint() { - dock_manager = EditorDockManager::get_singleton(); - - set_as_top_level(true); - dock_drop_highlight.instantiate(); - dock_drop_highlight->set_corner_radius_all(EDSCALE * EDITOR_GET("interface/theme/corner_radius").operator int()); - dock_drop_highlight->set_border_width_all(Math::round(2 * EDSCALE)); -} //////////////////////////////////////////////// //////////////////////////////////////////////// @@ -246,7 +124,7 @@ EditorDock *EditorDockManager::_get_dock_tab_dragged() { return dock_tab_dragged; } - Dictionary dock_drop_data = dock_slots[EditorDock::DOCK_SLOT_LEFT_BL].container->get_viewport()->gui_get_drag_data(); + Dictionary dock_drop_data = EditorNode::get_singleton()->get_viewport()->gui_get_drag_data(); // Check if we are dragging a dock. if (dock_drop_data.get("type", "").operator String() != "tab") { @@ -260,21 +138,18 @@ EditorDock *EditorDockManager::_get_dock_tab_dragged() { return nullptr; } - TabContainer *source_tab_container = Object::cast_to(source_tab_bar->get_parent()); + DockTabContainer *source_tab_container = Object::cast_to(source_tab_bar->get_parent()); if (!source_tab_container) { return nullptr; } - dock_tab_dragged = Object::cast_to(source_tab_container->get_tab_control(dock_drop_data["tab_index"])); + dock_tab_dragged = source_tab_container->get_dock(dock_drop_data["tab_index"]); if (!dock_tab_dragged) { return nullptr; } for (int i = 0; i < EditorDock::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(); - } + dock_slots[i]->show_drag_hint(); } return dock_tab_dragged; @@ -290,26 +165,6 @@ void EditorDockManager::_dock_split_dragged(int p_offset) { EditorNode::get_singleton()->save_editor_layout_delayed(); } -void EditorDockManager::_dock_container_popup(int p_tab_idx, TabContainer *p_dock_container) { - EditorDock *hovered_dock = Object::cast_to(p_dock_container->get_tab_control(p_tab_idx)); - if (hovered_dock == nullptr) { - return; - } - - // Right click context menu. - dock_context_popup->set_dock(hovered_dock); - dock_context_popup->set_position(p_dock_container->get_tab_bar()->get_screen_position() + p_dock_container->get_tab_bar()->get_local_mouse_position()); - dock_context_popup->popup(); -} - -void EditorDockManager::_dock_container_update_visibility(TabContainer *p_dock_container) { - if (!docks_visible) { - return; - } - // Hide the dock container if there are no tabs. - p_dock_container->set_visible(p_dock_container->get_tab_count() > 0); -} - void EditorDockManager::_update_layout() { if (!dock_context_popup->is_inside_tree() || EditorNode::get_singleton()->is_exiting()) { return; @@ -346,7 +201,7 @@ void EditorDockManager::update_docks_menu() { } docks_menu->set_item_icon_max_width(id, icon_max_width); - const Ref icon = _get_dock_icon(dock, icon_fetch); + const Ref icon = dock->get_effective_icon(icon_fetch); docks_menu->set_item_icon(id, icon.is_valid() ? icon : default_icon); if (!dock->is_open) { docks_menu->set_item_icon_modulate(id, closed_icon_color_mod); @@ -378,7 +233,6 @@ void EditorDockManager::_window_close_request(WindowWrapper *p_wrapper) { if (dock->dock_slot_index != EditorDock::DOCK_SLOT_NONE) { dock->is_open = false; - open_dock(dock); focus_dock(dock); } else { close_dock(dock); @@ -447,23 +301,6 @@ void EditorDockManager::_restore_dock_to_saved_window(EditorDock *p_dock, const p_window_dump.get("window_screen_rect", Rect2i())); } -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) { - return; - } - - dock_tab_container->set_block_signals(true); - int target_index = CLAMP(p_tab_index, 0, dock_tab_container->get_tab_count() - 1); - dock_tab_container->move_child(p_dock, dock_tab_container->get_tab_control(target_index)->get_index(false)); - p_dock->previous_tab_index = target_index; - - if (p_set_current) { - dock_tab_container->set_current_tab(target_index); - } - dock_tab_container->set_block_signals(false); -} - void EditorDockManager::_move_dock(EditorDock *p_dock, Control *p_target, int p_tab_index, bool p_set_current) { ERR_FAIL_NULL(p_dock); ERR_FAIL_COND_MSG(!all_docks.has(p_dock), vformat("Cannot move unknown dock '%s'.", p_dock->get_display_title())); @@ -472,7 +309,7 @@ void EditorDockManager::_move_dock(EditorDock *p_dock, Control *p_target, int p_ if (parent == p_target) { if (parent && p_tab_index >= 0) { // Only change the tab index. - _move_dock_tab_index(p_dock, p_tab_index, p_set_current); + p_dock->set_tab_index(p_tab_index, p_set_current); } return; } @@ -482,7 +319,7 @@ void EditorDockManager::_move_dock(EditorDock *p_dock, Control *p_target, int p_ if (p_dock->dock_window) { _close_window(p_dock->dock_window); } else { - TabContainer *parent_tabs = Object::cast_to(parent); + DockTabContainer *parent_tabs = Object::cast_to(parent); if (parent_tabs) { p_dock->previous_tab_index = parent_tabs->get_tab_idx_from_control(p_dock); } @@ -490,7 +327,7 @@ void EditorDockManager::_move_dock(EditorDock *p_dock, Control *p_target, int p_ parent->remove_child(p_dock); parent->set_block_signals(false); if (parent_tabs) { - _dock_container_update_visibility(parent_tabs); + parent_tabs->update_visibility(); } } } @@ -499,13 +336,13 @@ void EditorDockManager::_move_dock(EditorDock *p_dock, Control *p_target, int p_ return; } + DockTabContainer *dock_tab_container = Object::cast_to(p_target); if (p_target != closed_dock_parent) { - EditorDock::DockLayout layout = p_target->get_meta("dock_layout"); - if (layout != p_dock->current_layout) { - p_dock->update_layout(layout); - p_dock->current_layout = layout; + if (dock_tab_container->layout != p_dock->current_layout) { + p_dock->update_layout(dock_tab_container->layout); + p_dock->current_layout = dock_tab_container->layout; } - p_dock->dock_slot_index = p_target->get_meta("dock_slot"); + p_dock->dock_slot_index = dock_tab_container->dock_slot; } // Add dock to its new parent, at the given tab index. @@ -513,15 +350,14 @@ void EditorDockManager::_move_dock(EditorDock *p_dock, Control *p_target, int p_ 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()) { - _update_tab_style(p_dock); + p_dock->update_tab_style(); } if (p_tab_index >= 0) { - _move_dock_tab_index(p_dock, p_tab_index, p_set_current); + p_dock->set_tab_index(p_tab_index, p_set_current); } - _dock_container_update_visibility(dock_tab_container); + dock_tab_container->update_visibility(); } } @@ -536,7 +372,7 @@ void EditorDockManager::_update_dirty_dock_tabs() { bool update_menu = false; for (EditorDock *dock : dirty_docks) { update_menu = update_menu || dock->global; - _update_tab_style(dock); + dock->update_tab_style(); } dirty_docks.clear(); @@ -545,85 +381,10 @@ void EditorDockManager::_update_dirty_dock_tabs() { } } -void EditorDockManager::_update_tab_style(EditorDock *p_dock) { - if (!p_dock->enabled || !p_dock->is_open) { - return; // Disabled by feature profile or manually closed by user. - } - if (p_dock->dock_window) { - return; // Floating. - } - - TabContainer *tab_container = get_dock_tab_container(p_dock); - ERR_FAIL_NULL(tab_container); - - int index = tab_container->get_tab_idx_from_control(p_dock); - ERR_FAIL_COND(index == -1); - - tab_container->get_tab_bar()->set_font_color_override_all(index, p_dock->title_color); - - const TabStyle style = (tab_container == EditorNode::get_bottom_panel()) - ? (TabStyle)EDITOR_GET("interface/editor/bottom_dock_tab_style").operator int() - : (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)); - bool assign_icon = p_dock->force_show_icon; - String tooltip; - switch (style) { - case TabStyle::TEXT_ONLY: { - tab_container->set_tab_title(index, p_dock->get_display_title()); - } break; - case TabStyle::ICON_ONLY: { - tab_container->set_tab_title(index, icon.is_valid() ? String() : p_dock->get_display_title()); - tooltip = TTR(p_dock->get_display_title()); - assign_icon = true; - } break; - case TabStyle::TEXT_AND_ICON: { - tab_container->set_tab_title(index, p_dock->get_display_title()); - assign_icon = true; - } break; - } - - if (p_dock->shortcut.is_valid() && p_dock->shortcut->has_valid_event()) { - tooltip += (tooltip.is_empty() ? "" : "\n") + TTR(p_dock->shortcut->get_name()) + " (" + p_dock->shortcut->get_as_text() + ")"; - } - tab_container->set_tab_tooltip(index, tooltip); - - if (assign_icon) { - tab_container->set_tab_icon(index, icon); - } else { - tab_container->set_tab_icon(index, Ref()); - } -} - void EditorDockManager::save_docks_to_config(Ref p_layout, const String &p_section) const { // Save docks by dock slot. for (int i = 0; i < EditorDock::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); - } - - 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, String(",").join(names)); - } - - 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, 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); - } + dock_slots[i]->save_docks_to_config(p_layout, p_section); } // Clear the special dock slot for docks without default slots (index -1 = dock_0). @@ -652,7 +413,7 @@ void EditorDockManager::save_docks_to_config(Ref p_layout, const Str // Append to regular dock section so we know where to restore it to. int dock_slot_id = dock->dock_slot_index; - String config_key = "dock_" + itos(dock_slot_id + 1); + String config_key = DockTabContainer::get_config_key(dock_slot_id); String names = p_layout->get_value(p_section, config_key, ""); if (names.is_empty()) { @@ -664,7 +425,6 @@ void EditorDockManager::save_docks_to_config(Ref p_layout, const Str } p_layout->set_value(p_section, "dock_floating", floating_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(); @@ -674,13 +434,14 @@ void EditorDockManager::save_docks_to_config(Ref p_layout, const Str continue; } + // Save closed docks. const String name = dock->get_effective_layout_key(); - if (!dock->is_open && !dock->transient) { + if (!dock->transient) { closed_docks_dump.push_back(name); } int dock_slot_id = dock->dock_slot_index; - String config_key = "dock_" + itos(dock_slot_id + 1); + String config_key = DockTabContainer::get_config_key(dock_slot_id); String names = p_layout->get_value(p_section, config_key, ""); if (names.is_empty()) { @@ -725,12 +486,12 @@ 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 < EditorDock::DOCK_SLOT_MAX; i++) { - if (!p_layout->has_section_key(p_section, "dock_" + itos(i + 1))) { + const String key = DockTabContainer::get_config_key(i); + if (!p_layout->has_section_key(p_section, key)) { continue; } - Vector names = String(p_layout->get_value(p_section, "dock_" + itos(i + 1))).split(","); - + Vector names = String(p_layout->get_value(p_section, key)).split(","); for (int j = names.size() - 1; j >= 0; j--) { const String &name = names[j]; const String section_name = p_section + "/" + name; @@ -741,7 +502,7 @@ void EditorDockManager::load_docks_from_config(Ref p_layout, const S EditorDock *dock = dock_map[name]; if (!dock->enabled) { - // Don't is_open disabled docks. + // Don't open disabled docks. dock->load_layout_from_config(p_layout, section_name); continue; } @@ -756,7 +517,7 @@ void EditorDockManager::load_docks_from_config(Ref p_layout, const S _move_dock(dock, closed_dock_parent); } else { dock->is_open = true; - _move_dock(dock, dock_slots[i].container, 0); + _move_dock(dock, dock_slots[i], 0); } } dock->load_layout_from_config(p_layout, section_name); @@ -768,23 +529,8 @@ void EditorDockManager::load_docks_from_config(Ref p_layout, const S // Set the selected tabs. for (int i = 0; i < EditorDock::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 == EditorDock::DOCK_SLOT_BOTTOM) { - dock_slot.container->set_current_tab(-1); - } - continue; - } - - 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); + int selected_tab_idx = p_layout->get_value(p_section, DockTabContainer::get_config_key(i) + "_selected_tab_idx", -1); + dock_slots[i]->load_selected_tab(selected_tab_idx); } // Load SplitContainer offsets. @@ -832,11 +578,8 @@ void EditorDockManager::close_dock(EditorDock *p_dock) { } p_dock->is_open = false; + p_dock->get_parent_container()->dock_closed(p_dock); - 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(); - } // Hide before moving to remove inconsistent signals. p_dock->hide(); _move_dock(p_dock, closed_dock_parent); @@ -860,7 +603,7 @@ void EditorDockManager::open_dock(EditorDock *p_dock, bool p_set_current) { // Open dock to its previous location. if (p_dock->dock_slot_index != EditorDock::DOCK_SLOT_NONE) { - TabContainer *slot = dock_slots[p_dock->dock_slot_index].container; + TabContainer *slot = dock_slots[p_dock->dock_slot_index]; int tab_index = p_dock->previous_tab_index; if (tab_index < 0) { tab_index = slot->get_tab_count(); @@ -883,10 +626,6 @@ void EditorDockManager::make_dock_floating(EditorDock *p_dock) { } } -TabContainer *EditorDockManager::get_dock_tab_container(Control *p_dock) const { - return Object::cast_to(p_dock->get_parent()); -} - void EditorDockManager::_make_dock_visible(EditorDock *p_dock, bool p_grab_focus) { if (p_dock->dock_window) { if (p_grab_focus) { @@ -895,23 +634,17 @@ void EditorDockManager::_make_dock_visible(EditorDock *p_dock, bool p_grab_focus return; } - if (p_dock->get_parent() == EditorNode::get_bottom_panel()) { - if (EditorNode::get_bottom_panel()->is_locked()) { - return; - } - } else if (!docks_visible) { + DockTabContainer *tab_container = p_dock->get_parent_container(); + if (!tab_container || !tab_container->can_switch_dock()) { return; } - TabContainer *tab_container = get_dock_tab_container(p_dock); - if (tab_container) { - if (p_grab_focus) { - tab_container->get_tab_bar()->grab_focus(); - } - - int tab_index = tab_container->get_tab_idx_from_control(p_dock); - tab_container->set_current_tab(tab_index); + if (p_grab_focus) { + tab_container->get_tab_bar()->grab_focus(); } + + int tab_index = tab_container->get_tab_idx_from_control(p_dock); + tab_container->set_current_tab(tab_index); } void EditorDockManager::focus_dock(EditorDock *p_dock) { @@ -964,10 +697,9 @@ void EditorDockManager::set_docks_visible(bool p_show) { return; } docks_visible = p_show; - for (int i = 0; i < EditorDock::DOCK_SLOT_BOTTOM; i++) { + for (int i = 0; i < EditorDock::DOCK_SLOT_MAX; i++) { // Show and hide in reverse order due to the SplitContainer prioritizing the last split offset. - TabContainer *container = dock_slots[docks_visible ? i : EditorDock::DOCK_SLOT_BOTTOM - i - 1].container; - container->set_visible(docks_visible && container->get_tab_count() > 0); + dock_slots[docks_visible ? i : EditorDock::DOCK_SLOT_MAX - i - 1]->update_visibility(); } _update_layout(); } @@ -978,14 +710,13 @@ bool EditorDockManager::are_docks_visible() const { void EditorDockManager::update_tab_styles() { for (EditorDock *dock : all_docks) { - _update_tab_style(dock); + dock->update_tab_style(); } } void EditorDockManager::set_tab_icon_max_width(int p_max_width) { for (int i = 0; i < EditorDock::DOCK_SLOT_MAX; i++) { - TabContainer *tab_container = dock_slots[i].container; - tab_container->add_theme_constant_override(SNAME("icon_max_width"), p_max_width); + dock_slots[i]->add_theme_constant_override(SNAME("icon_max_width"), p_max_width); } } @@ -999,40 +730,13 @@ void EditorDockManager::set_hsplit(DockSplitContainer *p_split) { p_split->connect("dragged", callable_mp(this, &EditorDockManager::_dock_split_dragged)); } -void EditorDockManager::register_dock_slot(EditorDock::DockSlot p_dock_slot, TabContainer *p_tab_container, EditorDock::DockLayout p_layout) { +void EditorDockManager::register_dock_slot(DockTabContainer *p_tab_container) { ERR_FAIL_NULL(p_tab_container); - ERR_FAIL_INDEX(p_dock_slot, EditorDock::DOCK_SLOT_MAX); + dock_slots[p_tab_container->dock_slot] = p_tab_container; - DockSlot slot; - slot.layout = p_layout; - - 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()->set_switch_on_release(true); - p_tab_container->get_tab_bar()->connect("tab_rmb_clicked", callable_mp(this, &EditorDockManager::_dock_container_popup).bind(p_tab_container)); - p_tab_container->set_drag_to_rearrange_enabled(true); - p_tab_container->set_tabs_rearrange_group(1); + p_tab_container->set_dock_context_popup(dock_context_popup); 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->hide(); - p_tab_container->set_meta("dock_slot", p_dock_slot); - p_tab_container->set_meta("dock_layout", p_layout); - - if (p_layout == EditorDock::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. - 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_vsplit_count() const { @@ -1088,23 +792,23 @@ void DockContextPopup::_notification(int p_what) { } void DockContextPopup::_tab_move_left() { - TabContainer *tab_container = dock_manager->get_dock_tab_container(context_dock); + TabContainer *tab_container = context_dock->get_parent_container(); if (!tab_container) { return; } int new_index = tab_container->get_tab_idx_from_control(context_dock) - 1; - dock_manager->_move_dock(context_dock, tab_container, new_index); + context_dock->set_tab_index(new_index, true); dock_manager->_update_layout(); dock_select->queue_redraw(); } void DockContextPopup::_tab_move_right() { - TabContainer *tab_container = dock_manager->get_dock_tab_container(context_dock); + TabContainer *tab_container = context_dock->get_parent_container(); if (!tab_container) { return; } int new_index = tab_container->get_tab_idx_from_control(context_dock) + 1; - dock_manager->_move_dock(context_dock, tab_container, new_index); + context_dock->set_tab_index(new_index, true); dock_manager->_update_layout(); dock_select->queue_redraw(); } @@ -1120,10 +824,6 @@ void DockContextPopup::_float_dock() { dock_manager->_open_dock_in_window(context_dock); } -bool DockContextPopup::_is_slot_available(int p_slot) const { - return context_dock->available_layouts & EditorDockManager::get_singleton()->dock_slots[p_slot].layout; -} - void DockContextPopup::_dock_select_input(const Ref &p_input) { Ref me = p_input; @@ -1148,14 +848,19 @@ void DockContextPopup::_dock_select_input(const Ref &p_input) { } Ref mb = me; - TabContainer *target_tab_container = dock_manager->dock_slots[over_dock_slot].container; + DockTabContainer *target_tab_container = dock_manager->dock_slots[over_dock_slot]; + if (context_dock->get_parent_container() == target_tab_container) { + return; + } + + if (!(context_dock->available_layouts & target_tab_container->layout)) { + return; + } 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 && _is_slot_available(over_dock_slot)) { - dock_manager->_move_dock(context_dock, target_tab_container, target_tab_container->get_tab_count()); - dock_manager->_update_layout(); - hide(); - } + dock_manager->_move_dock(context_dock, target_tab_container, target_tab_container->get_tab_count()); + dock_manager->_update_layout(); + hide(); } } } @@ -1210,7 +915,7 @@ void DockContextPopup::_dock_select_draw() { real_t dock_spacing = 2.0 * EDSCALE; real_t dock_top_spacing = tab_height + dock_spacing; - TabContainer *context_tab_container = dock_manager->get_dock_tab_container(context_dock); + TabContainer *context_tab_container = context_dock->get_parent_container(); int context_tab_index = -1; if (context_tab_container && context_tab_container->get_tab_count() > 0) { context_tab_index = context_tab_container->get_tab_idx_from_control(context_dock); @@ -1223,7 +928,7 @@ void DockContextPopup::_dock_select_draw() { // Draw all dock slots. for (int i = 0; i < EditorDock::DOCK_SLOT_MAX; i++) { int max_tabs = (i == EditorDock::DOCK_SLOT_BOTTOM) ? 6 : 3; - const EditorDockManager::DockSlot &dock_slot = dock_manager->dock_slots[i]; + const DockTabContainer *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); @@ -1238,11 +943,11 @@ void DockContextPopup::_dock_select_draw() { tab_draw_rect.position.x += dock_slot_draw_rect.size.x - tab_draw_rect.size.x; } - int tabs_to_draw = MIN(max_tabs, dock_slot.container->get_tab_count()); - bool is_context_dock = context_tab_container == dock_slot.container; + int tabs_to_draw = MIN(max_tabs, dock_slot->get_tab_count()); + bool is_context_dock = context_tab_container == dock_slot; if (i == context_dock->dock_slot_index) { dock_select->draw_rect(dock_slot_draw_rect, tab_selected_color); - } else if (!_is_slot_available(i)) { + } else if (!(context_dock->available_layouts & dock_slot->layout)) { 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); @@ -1266,7 +971,6 @@ void DockContextPopup::_dock_select_draw() { } void DockContextPopup::_update_buttons() { - TabContainer *context_tab_container = dock_manager->get_dock_tab_container(context_dock); if (context_dock->global || context_dock->closable) { close_button->set_tooltip_text(TTRC("Close this dock.")); close_button->set_disabled(false); @@ -1287,6 +991,7 @@ void DockContextPopup::_update_buttons() { // Update tab move buttons. tab_move_left_button->set_disabled(true); tab_move_right_button->set_disabled(true); + TabContainer *context_tab_container = context_dock->get_parent_container(); 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); @@ -1295,20 +1000,11 @@ void DockContextPopup::_update_buttons() { reset_size(); } -void DockContextPopup::select_current_dock_in_dock_slot(int p_dock_slot) { - context_dock = Object::cast_to(dock_manager->dock_slots[p_dock_slot].container->get_current_tab_control()); - _update_buttons(); -} - void DockContextPopup::set_dock(EditorDock *p_dock) { context_dock = p_dock; _update_buttons(); } -EditorDock *DockContextPopup::get_dock() const { - return context_dock; -} - void DockContextPopup::docks_updated() { if (!is_visible()) { return; @@ -1380,11 +1076,14 @@ void DockShortcutHandler::shortcut_input(const Ref &p_event) { 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) { + bool was_visible = dock->is_visible(); + if (!dock->transient || dock->is_open) { EditorDockManager::get_singleton()->focus_dock(dock); } + DockTabContainer *dock_container = dock->get_parent_container(); + if (dock_container) { + dock_container->dock_focused(dock, was_visible); + } get_viewport()->set_input_as_handled(); break; } diff --git a/editor/docks/editor_dock_manager.h b/editor/docks/editor_dock_manager.h index 02ec8585a54..0561be638cc 100644 --- a/editor/docks/editor_dock_manager.h +++ b/editor/docks/editor_dock_manager.h @@ -73,6 +73,7 @@ public: class DockContextPopup; class EditorDockDragHint; +class DockTabContainer; class EditorDockManager : public Object { GDCLASS(EditorDockManager, Object); @@ -88,13 +89,7 @@ private: Vector vsplits; DockSplitContainer *main_hsplit = nullptr; - struct DockSlot { - TabContainer *container = nullptr; - EditorDockDragHint *drag_hint = nullptr; - EditorDock::DockLayout layout = EditorDock::DOCK_LAYOUT_VERTICAL; - }; - - DockSlot dock_slots[EditorDock::DOCK_SLOT_MAX]; + DockTabContainer *dock_slots[EditorDock::DOCK_SLOT_MAX]; Vector dock_windows; LocalVector all_docks; HashSet dirty_docks; @@ -110,8 +105,6 @@ private: EditorDock *_get_dock_tab_dragged(); void _dock_drag_stopped(); void _dock_split_dragged(int p_offset); - void _dock_container_popup(int p_tab_idx, TabContainer *p_dock_container); - void _dock_container_update_visibility(TabContainer *p_dock_container); void _update_layout(); void _docks_menu_option(int p_id); @@ -122,12 +115,10 @@ private: void _restore_dock_to_saved_window(EditorDock *p_dock, const Dictionary &p_window_dump); void _make_dock_visible(EditorDock *p_dock, bool p_grab_focus); - 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); void _queue_update_tab_style(EditorDock *p_dock); void _update_dirty_dock_tabs(); - void _update_tab_style(EditorDock *p_dock); public: static EditorDockManager *get_singleton() { return singleton; } @@ -138,7 +129,7 @@ public: void add_vsplit(DockSplitContainer *p_split); void set_hsplit(DockSplitContainer *p_split); - void register_dock_slot(EditorDock::DockSlot p_dock_slot, TabContainer *p_tab_container, EditorDock::DockLayout p_layout); + void register_dock_slot(DockTabContainer *p_tab_container); int get_vsplit_count() const; PopupMenu *get_docks_menu(); @@ -151,8 +142,6 @@ public: void focus_dock(EditorDock *p_dock); void make_dock_floating(EditorDock *p_dock); - TabContainer *get_dock_tab_container(Control *p_dock) const; - void set_docks_visible(bool p_show); bool are_docks_visible() const; @@ -162,36 +151,6 @@ public: EditorDockManager(); }; -class EditorDockDragHint : public Control { - GDCLASS(EditorDockDragHint, Control); - -private: - EditorDockManager *dock_manager = nullptr; - EditorDock::DockSlot occupied_slot = EditorDock::DOCK_SLOT_MAX; - TabBar *drop_tabbar = nullptr; - - Color valid_drop_color; - Ref dock_drop_highlight; - bool can_drop_dock = false; - bool mouse_inside = false; - bool mouse_inside_tabbar = false; - - void _drag_move_tab(int p_from_index, int p_to_index); - void _drag_move_tab_from(TabBar *p_from_tabbar, int p_from_index, int p_to_index); - -protected: - virtual void gui_input(const Ref &p_event) override; - - void _notification(int p_what); - bool can_drop_data(const Point2 &p_point, const Variant &p_data) const override; - void drop_data(const Point2 &p_point, const Variant &p_data) override; - -public: - void set_slot(EditorDock::DockSlot p_slot); - - EditorDockDragHint(); -}; - class DockContextPopup : public PopupPanel { GDCLASS(DockContextPopup, PopupPanel); @@ -215,7 +174,6 @@ private: void _tab_move_right(); void _close_dock(); void _float_dock(); - bool _is_slot_available(int p_slot) const; void _dock_select_input(const Ref &p_input); void _dock_select_mouse_exited(); @@ -227,9 +185,7 @@ protected: void _notification(int p_what); public: - void select_current_dock_in_dock_slot(int p_dock_slot); void set_dock(EditorDock *p_dock); - EditorDock *get_dock() const; void docks_updated(); DockContextPopup(); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 757df717f2f..4e20ae4ada2 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -1460,7 +1460,7 @@ void EditorNode::_sources_changed(bool p_exist) { if (SceneTreeDock::get_singleton()->is_visible_in_tree()) { SceneTreeDock::get_singleton()->get_tree_editor()->get_scene_tree()->grab_focus(); } else { - TabContainer *tab_container = EditorDockManager::get_singleton()->get_dock_tab_container(SceneTreeDock::get_singleton()); + TabContainer *tab_container = SceneTreeDock::get_singleton()->get_parent_container(); if (tab_container) { // Another tab is active (e.g., Import) - focus the tab bar so user can switch. tab_container->get_tab_bar()->grab_focus(); @@ -8518,24 +8518,36 @@ EditorNode::EditorNode() { left_l_vsplit->set_vertical(true); main_hsplit->add_child(left_l_vsplit); - TabContainer *dock_slot[EditorDock::DOCK_SLOT_MAX]; - dock_slot[EditorDock::DOCK_SLOT_LEFT_UL] = memnew(TabContainer); - dock_slot[EditorDock::DOCK_SLOT_LEFT_UL]->set_name("DockSlotLeftUL"); - left_l_vsplit->add_child(dock_slot[EditorDock::DOCK_SLOT_LEFT_UL]); - dock_slot[EditorDock::DOCK_SLOT_LEFT_BL] = memnew(TabContainer); - dock_slot[EditorDock::DOCK_SLOT_LEFT_BL]->set_name("DockSlotLeftBL"); - left_l_vsplit->add_child(dock_slot[EditorDock::DOCK_SLOT_LEFT_BL]); + DockTabContainer *dock_slots[EditorDock::DOCK_SLOT_MAX]; + { + DockTabContainer *dock_container = memnew(SideDockTabContainer(EditorDock::DOCK_SLOT_LEFT_UL)); + dock_container->set_name("DockSlotLeftUL"); + left_l_vsplit->add_child(dock_container); + dock_slots[dock_container->dock_slot] = dock_container; + } + { + DockTabContainer *dock_container = memnew(SideDockTabContainer(EditorDock::DOCK_SLOT_LEFT_BL)); + dock_container->set_name("DockSlotLeftBL"); + left_l_vsplit->add_child(dock_container); + dock_slots[dock_container->dock_slot] = dock_container; + } left_r_vsplit = memnew(DockSplitContainer); left_r_vsplit->set_name("DockVSplitLeftR"); left_r_vsplit->set_vertical(true); main_hsplit->add_child(left_r_vsplit); - dock_slot[EditorDock::DOCK_SLOT_LEFT_UR] = memnew(TabContainer); - dock_slot[EditorDock::DOCK_SLOT_LEFT_UR]->set_name("DockSlotLeftUR"); - left_r_vsplit->add_child(dock_slot[EditorDock::DOCK_SLOT_LEFT_UR]); - dock_slot[EditorDock::DOCK_SLOT_LEFT_BR] = memnew(TabContainer); - dock_slot[EditorDock::DOCK_SLOT_LEFT_BR]->set_name("DockSlotLeftBR"); - left_r_vsplit->add_child(dock_slot[EditorDock::DOCK_SLOT_LEFT_BR]); + { + DockTabContainer *dock_container = memnew(SideDockTabContainer(EditorDock::DOCK_SLOT_LEFT_UR)); + dock_container->set_name("DockSlotLeftUR"); + left_r_vsplit->add_child(dock_container); + dock_slots[dock_container->dock_slot] = dock_container; + } + { + DockTabContainer *dock_container = memnew(SideDockTabContainer(EditorDock::DOCK_SLOT_LEFT_BR)); + dock_container->set_name("DockSlotLeftBR"); + left_r_vsplit->add_child(dock_container); + dock_slots[dock_container->dock_slot] = dock_container; + } VBoxContainer *center_vb = memnew(VBoxContainer); center_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL); @@ -8553,23 +8565,35 @@ EditorNode::EditorNode() { right_l_vsplit->set_name("DockVSplitRightL"); right_l_vsplit->set_vertical(true); main_hsplit->add_child(right_l_vsplit); - dock_slot[EditorDock::DOCK_SLOT_RIGHT_UL] = memnew(TabContainer); - dock_slot[EditorDock::DOCK_SLOT_RIGHT_UL]->set_name("DockSlotRightUL"); - right_l_vsplit->add_child(dock_slot[EditorDock::DOCK_SLOT_RIGHT_UL]); - dock_slot[EditorDock::DOCK_SLOT_RIGHT_BL] = memnew(TabContainer); - dock_slot[EditorDock::DOCK_SLOT_RIGHT_BL]->set_name("DockSlotRightBL"); - right_l_vsplit->add_child(dock_slot[EditorDock::DOCK_SLOT_RIGHT_BL]); + { + DockTabContainer *dock_container = memnew(SideDockTabContainer(EditorDock::DOCK_SLOT_RIGHT_UL)); + dock_container->set_name("DockSlotRightUL"); + right_l_vsplit->add_child(dock_container); + dock_slots[dock_container->dock_slot] = dock_container; + } + { + DockTabContainer *dock_container = memnew(SideDockTabContainer(EditorDock::DOCK_SLOT_RIGHT_BL)); + dock_container->set_name("DockSlotRightBL"); + right_l_vsplit->add_child(dock_container); + dock_slots[dock_container->dock_slot] = dock_container; + } right_r_vsplit = memnew(DockSplitContainer); right_r_vsplit->set_name("DockVSplitRightR"); right_r_vsplit->set_vertical(true); main_hsplit->add_child(right_r_vsplit); - dock_slot[EditorDock::DOCK_SLOT_RIGHT_UR] = memnew(TabContainer); - dock_slot[EditorDock::DOCK_SLOT_RIGHT_UR]->set_name("DockSlotRightUR"); - right_r_vsplit->add_child(dock_slot[EditorDock::DOCK_SLOT_RIGHT_UR]); - dock_slot[EditorDock::DOCK_SLOT_RIGHT_BR] = memnew(TabContainer); - dock_slot[EditorDock::DOCK_SLOT_RIGHT_BR]->set_name("DockSlotRightBR"); - right_r_vsplit->add_child(dock_slot[EditorDock::DOCK_SLOT_RIGHT_BR]); + { + DockTabContainer *dock_container = memnew(SideDockTabContainer(EditorDock::DOCK_SLOT_RIGHT_UR)); + dock_container->set_name("DockSlotRightUR"); + right_r_vsplit->add_child(dock_container); + dock_slots[dock_container->dock_slot] = dock_container; + } + { + DockTabContainer *dock_container = memnew(SideDockTabContainer(EditorDock::DOCK_SLOT_RIGHT_BR)); + dock_container->set_name("DockSlotRightBR"); + right_r_vsplit->add_child(dock_container); + dock_slots[dock_container->dock_slot] = dock_container; + } editor_dock_manager = memnew(EditorDockManager); @@ -8582,7 +8606,7 @@ EditorNode::EditorNode() { editor_dock_manager->set_hsplit(main_hsplit); for (int i = 0; i < EditorDock::DOCK_SLOT_BOTTOM; i++) { - editor_dock_manager->register_dock_slot((EditorDock::DockSlot)i, dock_slot[i], EditorDock::DOCK_LAYOUT_VERTICAL); + editor_dock_manager->register_dock_slot(dock_slots[i]); } editor_layout_save_delay_timer = memnew(Timer); @@ -8988,8 +9012,7 @@ EditorNode::EditorNode() { // Bottom panels. bottom_panel = memnew(EditorBottomPanel); - editor_dock_manager->register_dock_slot(EditorDock::DOCK_SLOT_BOTTOM, bottom_panel, EditorDock::DOCK_LAYOUT_HORIZONTAL); - bottom_panel->set_theme_type_variation("BottomPanel"); + editor_dock_manager->register_dock_slot(bottom_panel); 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 22e85ad31fd..4daf76ce900 100644 --- a/editor/gui/editor_bottom_panel.cpp +++ b/editor/gui/editor_bottom_panel.cpp @@ -39,6 +39,7 @@ #include "editor/gui/editor_version_button.h" #include "editor/scene/editor_scene_tabs.h" #include "editor/settings/editor_command_palette.h" +#include "editor/settings/editor_settings.h" #include "scene/gui/box_container.h" #include "scene/gui/button.h" #include "scene/gui/separator.h" @@ -48,7 +49,6 @@ void EditorBottomPanel::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: { set_accessibility_region(true); - layout_popup = get_popup(); } break; case NOTIFICATION_THEME_CHANGED: { @@ -110,7 +110,7 @@ void EditorBottomPanel::_repaint() { if (panel_collapsed && get_popup()) { set_popup(nullptr); } else if (!panel_collapsed && !get_popup()) { - set_popup(layout_popup); + set_popup(dock_context_popup); } if (!panel_collapsed && (previous_tab != -1)) { return; @@ -132,6 +132,36 @@ void EditorBottomPanel::_repaint() { } } +void EditorBottomPanel::dock_closed(EditorDock *p_dock) { + if (p_dock == get_current_tab_control()) { + hide_bottom_panel(); + } +} + +void EditorBottomPanel::dock_focused(EditorDock *p_dock, bool p_was_visible) { + if (p_was_visible && p_dock->is_visible()) { + hide_bottom_panel(); + } +} + +DockTabContainer::TabStyle EditorBottomPanel::get_tab_style() const { + return (TabStyle)EDITOR_GET("interface/editor/bottom_dock_tab_style").operator int(); +} + +bool EditorBottomPanel::can_switch_dock() const { + return !is_locked(); +} + +void EditorBottomPanel::load_selected_tab(int p_idx) { + EditorDock *selected_dock = get_dock(p_idx); + if (!selected_dock) { + p_idx = -1; + } + set_block_signals(true); + set_current_tab(p_idx); + set_block_signals(false); +} + void EditorBottomPanel::save_layout_to_config(Ref p_config_file, const String &p_section) const { Dictionary offsets; for (const KeyValue &E : dock_offsets) { @@ -251,10 +281,14 @@ void EditorBottomPanel::_on_button_visibility_changed(Button *p_button, EditorDo } } -EditorBottomPanel::EditorBottomPanel() { +EditorBottomPanel::EditorBottomPanel() : + DockTabContainer(EditorDock::DOCK_SLOT_BOTTOM) { + layout = EditorDock::DOCK_LAYOUT_HORIZONTAL; + get_tab_bar()->connect("tab_changed", callable_mp(this, &EditorBottomPanel::_on_tab_changed)); set_tabs_position(TabPosition::POSITION_BOTTOM); set_deselect_enabled(true); + set_theme_type_variation("BottomPanel"); bottom_hbox = memnew(HBoxContainer); bottom_hbox->set_mouse_filter(MOUSE_FILTER_IGNORE); diff --git a/editor/gui/editor_bottom_panel.h b/editor/gui/editor_bottom_panel.h index 238ed5e6c37..7d2648ca523 100644 --- a/editor/gui/editor_bottom_panel.h +++ b/editor/gui/editor_bottom_panel.h @@ -30,7 +30,7 @@ #pragma once -#include "scene/gui/tab_container.h" +#include "editor/docks/dock_tab_container.h" class Button; class ConfigFile; @@ -38,15 +38,14 @@ class EditorDock; class EditorToaster; class HBoxContainer; -class EditorBottomPanel : public TabContainer { - GDCLASS(EditorBottomPanel, TabContainer); +class EditorBottomPanel : public DockTabContainer { + GDCLASS(EditorBottomPanel, DockTabContainer); 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; @@ -67,6 +66,13 @@ protected: void _notification(int p_what); public: + virtual void dock_closed(EditorDock *p_dock) override; + virtual void dock_focused(EditorDock *p_dock, bool p_was_visible) override; + virtual void update_visibility() override { show(); } // Never hide bottom panel. + virtual TabStyle get_tab_style() const override; + virtual bool can_switch_dock() const override; + virtual void load_selected_tab(int p_idx) override; + void save_layout_to_config(Ref p_config_file, const String &p_section) const; void load_layout_from_config(Ref p_config_file, const String &p_section); diff --git a/editor/shader/shader_editor_plugin.cpp b/editor/shader/shader_editor_plugin.cpp index a9d304b3019..fb61f9b0240 100644 --- a/editor/shader/shader_editor_plugin.cpp +++ b/editor/shader/shader_editor_plugin.cpp @@ -849,7 +849,7 @@ void ShaderEditorPlugin::shortcut_input(const Ref &p_event) { } if (make_floating_shortcut.is_valid() && make_floating_shortcut->matches_event(p_event)) { - EditorDockManager::get_singleton()->make_dock_floating(shader_dock); + shader_dock->make_floating(); } }