Skip to content

Commit 56502b3

Browse files
committed
Add dock dragging area and highlight
1 parent 7a0ab9d commit 56502b3

File tree

4 files changed

+180
-37
lines changed

4 files changed

+180
-37
lines changed

editor/editor_dock_manager.cpp

Lines changed: 131 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include "editor/gui/editor_bottom_panel.h"
4444
#include "editor/themes/editor_scale.h"
4545
#include "editor/window_wrapper.h"
46+
#include "scene/resources/style_box_flat.h"
4647

4748
enum class TabStyle {
4849
TEXT_ONLY,
@@ -52,6 +53,113 @@ enum class TabStyle {
5253

5354
EditorDockManager *EditorDockManager::singleton = nullptr;
5455

56+
// Implemented in input(..) as nearly every single child consumes the mouse input.
57+
void EditorDockTabContainer::input(const Ref<InputEvent> &p_event) {
58+
ERR_FAIL_COND(p_event.is_null());
59+
60+
Ref<InputEventMouse> me = p_event;
61+
if (me.is_valid() && is_dragging_dock) {
62+
if (mouse_inside != get_global_rect().has_point(me->get_position())) {
63+
mouse_inside = !mouse_inside;
64+
queue_redraw();
65+
}
66+
}
67+
}
68+
69+
void EditorDockTabContainer::_notification(int p_what) {
70+
switch (p_what) {
71+
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
72+
int _corner_radius = EDSCALE * EDITOR_GET("interface/theme/corner_radius").operator int();
73+
if (_corner_radius != dock_drop_highlight->get_corner_radius(CORNER_TOP_LEFT)) {
74+
dock_drop_highlight->set_corner_radius_all(_corner_radius);
75+
if (mouse_inside) {
76+
queue_redraw();
77+
}
78+
}
79+
} break;
80+
81+
case NOTIFICATION_POSTINITIALIZE:
82+
case NOTIFICATION_THEME_CHANGED: {
83+
valid_drop_color = get_theme_color("accent_color", EditorStringName(Editor));
84+
invalid_drop_color = get_theme_color(SNAME("error_color"), EditorStringName(Editor));
85+
int _dock_margin = get_theme_constant("base_margin", EditorStringName(Editor));
86+
87+
if (dock_margin != _dock_margin) {
88+
dock_margin = _dock_margin;
89+
dock_drop_highlight->set_border_width_all(dock_margin);
90+
if (mouse_inside) {
91+
queue_redraw();
92+
}
93+
}
94+
} break;
95+
96+
case NOTIFICATION_DRAG_BEGIN: {
97+
if (!is_visible_in_tree()) {
98+
return;
99+
}
100+
101+
dock_drop_data = get_viewport()->gui_get_drag_data();
102+
103+
// Check if we are dragging a dock.
104+
if (dock_drop_data.has("type") && dock_drop_data["type"] == "tab_container_tab") {
105+
Node *from_node = get_node(dock_drop_data["from_path"]);
106+
if (from_node && static_cast<EditorDockTabContainer *>(from_node->get_parent())) {
107+
Control *dock = static_cast<EditorDockTabContainer *>(from_node->get_parent())->get_tab_control(dock_drop_data["tab_index"]);
108+
if (dock) {
109+
is_dragging_dock = true;
110+
mouse_inside = get_global_rect().has_point(get_global_mouse_position());
111+
112+
// TODO: Update logic when GH-106503 is merged.
113+
can_drop_dock = is_layout_horizontal ? bool(dock->call("_can_dock_horizontal")) : true;
114+
dock_drop_highlight->set_border_color(can_drop_dock ? valid_drop_color : invalid_drop_color);
115+
116+
set_process_input(true);
117+
queue_redraw();
118+
}
119+
}
120+
}
121+
} break;
122+
case NOTIFICATION_DRAG_END: {
123+
if (!is_dragging_dock) {
124+
return;
125+
}
126+
127+
is_dragging_dock = false;
128+
set_process_input(false);
129+
130+
if (mouse_inside && can_drop_dock) {
131+
// Only perform a manual drop if an automatic drop was not performed.
132+
if (!get_tab_bar()->get_global_rect().has_point(get_global_mouse_position())) {
133+
get_tab_bar()->drop_data(get_local_mouse_position(), dock_drop_data);
134+
}
135+
136+
queue_redraw();
137+
}
138+
} break;
139+
140+
case NOTIFICATION_DRAW: {
141+
// Draw highlights around docks that can be dropped.
142+
if (is_dragging_dock && mouse_inside) {
143+
Rect2 dock_rect = Rect2(Vector2(), get_size()).grow(dock_margin);
144+
draw_style_box(dock_drop_highlight, dock_rect);
145+
}
146+
} break;
147+
148+
case NOTIFICATION_VISIBILITY_CHANGED: {
149+
set_process_input(is_dragging_dock && is_visible_in_tree());
150+
} break;
151+
}
152+
}
153+
154+
EditorDockTabContainer::EditorDockTabContainer() {
155+
dock_drop_highlight.instantiate();
156+
dock_drop_highlight->set_draw_center(false);
157+
dock_drop_highlight->set_corner_radius_all(EDSCALE * EDITOR_GET("interface/theme/corner_radius").operator int());
158+
}
159+
160+
////////////////////////////////////////////////
161+
////////////////////////////////////////////////
162+
55163
void DockSplitContainer::_update_visibility() {
56164
if (is_updating) {
57165
return;
@@ -114,11 +222,14 @@ void DockSplitContainer::remove_child_notify(Node *p_child) {
114222
_update_visibility();
115223
}
116224

225+
////////////////////////////////////////////////
226+
////////////////////////////////////////////////
227+
117228
void EditorDockManager::_dock_split_dragged(int p_offset) {
118229
EditorNode::get_singleton()->save_editor_layout_delayed();
119230
}
120231

121-
void EditorDockManager::_dock_container_gui_input(const Ref<InputEvent> &p_input, TabContainer *p_dock_container) {
232+
void EditorDockManager::_dock_container_gui_input(const Ref<InputEvent> &p_input, EditorDockTabContainer *p_dock_container) {
122233
Ref<InputEventMouseButton> mb = p_input;
123234

124235
if (mb.is_valid() && mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) {
@@ -145,7 +256,7 @@ void EditorDockManager::_bottom_dock_button_gui_input(const Ref<InputEvent> &p_i
145256
}
146257
}
147258

148-
void EditorDockManager::_dock_container_update_visibility(TabContainer *p_dock_container) {
259+
void EditorDockManager::_dock_container_update_visibility(EditorDockTabContainer *p_dock_container) {
149260
if (!docks_visible) {
150261
return;
151262
}
@@ -312,7 +423,7 @@ bool EditorDockManager::_is_dock_at_bottom(Control *p_dock) {
312423
}
313424

314425
void EditorDockManager::_move_dock_tab_index(Control *p_dock, int p_tab_index, bool p_set_current) {
315-
TabContainer *dock_tab_container = Object::cast_to<TabContainer>(p_dock->get_parent());
426+
EditorDockTabContainer *dock_tab_container = Object::cast_to<EditorDockTabContainer>(p_dock->get_parent());
316427
if (!dock_tab_container) {
317428
return;
318429
}
@@ -349,7 +460,7 @@ void EditorDockManager::_move_dock(Control *p_dock, Control *p_target, int p_tab
349460
_dock_remove_from_bottom(p_dock);
350461
} else {
351462
all_docks[p_dock].previous_at_bottom = false;
352-
TabContainer *parent_tabs = Object::cast_to<TabContainer>(parent);
463+
EditorDockTabContainer *parent_tabs = Object::cast_to<EditorDockTabContainer>(parent);
353464
if (parent_tabs) {
354465
all_docks[p_dock].previous_tab_index = parent_tabs->get_tab_idx_from_control(p_dock);
355466
}
@@ -369,7 +480,7 @@ void EditorDockManager::_move_dock(Control *p_dock, Control *p_target, int p_tab
369480
p_target->set_block_signals(true);
370481
p_target->add_child(p_dock);
371482
p_target->set_block_signals(false);
372-
TabContainer *dock_tab_container = Object::cast_to<TabContainer>(p_target);
483+
EditorDockTabContainer *dock_tab_container = Object::cast_to<EditorDockTabContainer>(p_target);
373484
if (dock_tab_container) {
374485
if (dock_tab_container->is_inside_tree()) {
375486
_update_tab_style(p_dock);
@@ -390,7 +501,7 @@ void EditorDockManager::_update_tab_style(Control *p_dock) {
390501
return; // Floating or sent to bottom.
391502
}
392503

393-
TabContainer *tab_container = get_dock_tab_container(p_dock);
504+
EditorDockTabContainer *tab_container = get_dock_tab_container(p_dock);
394505
ERR_FAIL_NULL(tab_container);
395506
int index = tab_container->get_tab_idx_from_control(p_dock);
396507
ERR_FAIL_COND(index == -1);
@@ -682,7 +793,7 @@ void EditorDockManager::open_dock(Control *p_dock, bool p_set_current) {
682793
if (all_docks[p_dock].previous_at_bottom) {
683794
_dock_move_to_bottom(p_dock, true);
684795
} else if (all_docks[p_dock].dock_slot_index != DOCK_SLOT_NONE) {
685-
TabContainer *slot = dock_slot[all_docks[p_dock].dock_slot_index];
796+
EditorDockTabContainer *slot = dock_slot[all_docks[p_dock].dock_slot_index];
686797
int tab_index = all_docks[p_dock].previous_tab_index;
687798
if (tab_index < 0) {
688799
tab_index = slot->get_tab_count();
@@ -696,8 +807,8 @@ void EditorDockManager::open_dock(Control *p_dock, bool p_set_current) {
696807
_update_layout();
697808
}
698809

699-
TabContainer *EditorDockManager::get_dock_tab_container(Control *p_dock) const {
700-
return Object::cast_to<TabContainer>(p_dock->get_parent());
810+
EditorDockTabContainer *EditorDockManager::get_dock_tab_container(Control *p_dock) const {
811+
return Object::cast_to<EditorDockTabContainer>(p_dock->get_parent());
701812
}
702813

703814
void EditorDockManager::focus_dock(Control *p_dock) {
@@ -726,7 +837,7 @@ void EditorDockManager::focus_dock(Control *p_dock) {
726837
return;
727838
}
728839

729-
TabContainer *tab_container = get_dock_tab_container(p_dock);
840+
EditorDockTabContainer *tab_container = get_dock_tab_container(p_dock);
730841
if (!tab_container) {
731842
return;
732843
}
@@ -797,7 +908,7 @@ void EditorDockManager::update_tab_styles() {
797908

798909
void EditorDockManager::set_tab_icon_max_width(int p_max_width) {
799910
for (int i = 0; i < DOCK_SLOT_MAX; i++) {
800-
TabContainer *tab_container = dock_slot[i];
911+
EditorDockTabContainer *tab_container = dock_slot[i];
801912
tab_container->add_theme_constant_override(SNAME("icon_max_width"), p_max_width);
802913
}
803914
}
@@ -812,7 +923,7 @@ void EditorDockManager::add_hsplit(DockSplitContainer *p_split) {
812923
p_split->connect("dragged", callable_mp(this, &EditorDockManager::_dock_split_dragged));
813924
}
814925

815-
void EditorDockManager::register_dock_slot(DockSlot p_dock_slot, TabContainer *p_tab_container) {
926+
void EditorDockManager::register_dock_slot(DockSlot p_dock_slot, EditorDockTabContainer *p_tab_container) {
816927
ERR_FAIL_NULL(p_tab_container);
817928
ERR_FAIL_INDEX(p_dock_slot, DOCK_SLOT_MAX);
818929

@@ -854,6 +965,9 @@ EditorDockManager::EditorDockManager() {
854965
EditorNode::get_singleton()->get_gui_base()->connect(SceneStringName(theme_changed), callable_mp(this, &EditorDockManager::update_docks_menu));
855966
}
856967

968+
////////////////////////////////////////////////
969+
////////////////////////////////////////////////
970+
857971
void DockContextPopup::_notification(int p_what) {
858972
switch (p_what) {
859973
case Control::NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
@@ -880,7 +994,7 @@ void DockContextPopup::_notification(int p_what) {
880994
}
881995

882996
void DockContextPopup::_tab_move_left() {
883-
TabContainer *tab_container = dock_manager->get_dock_tab_container(context_dock);
997+
EditorDockTabContainer *tab_container = dock_manager->get_dock_tab_container(context_dock);
884998
if (!tab_container) {
885999
return;
8861000
}
@@ -891,7 +1005,7 @@ void DockContextPopup::_tab_move_left() {
8911005
}
8921006

8931007
void DockContextPopup::_tab_move_right() {
894-
TabContainer *tab_container = dock_manager->get_dock_tab_container(context_dock);
1008+
EditorDockTabContainer *tab_container = dock_manager->get_dock_tab_container(context_dock);
8951009
if (!tab_container) {
8961010
return;
8971011
}
@@ -941,7 +1055,7 @@ void DockContextPopup::_dock_select_input(const Ref<InputEvent> &p_input) {
9411055
}
9421056

9431057
Ref<InputEventMouseButton> mb = me;
944-
TabContainer *target_tab_container = dock_manager->dock_slot[over_dock_slot];
1058+
EditorDockTabContainer *target_tab_container = dock_manager->dock_slot[over_dock_slot];
9451059

9461060
if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
9471061
if (dock_manager->get_dock_tab_container(context_dock) != target_tab_container) {
@@ -1004,7 +1118,7 @@ void DockContextPopup::_dock_select_draw() {
10041118
real_t dock_spacing = 2.0 * EDSCALE;
10051119
real_t dock_top_spacing = tab_height + dock_spacing;
10061120

1007-
TabContainer *context_tab_container = dock_manager->get_dock_tab_container(context_dock);
1121+
EditorDockTabContainer *context_tab_container = dock_manager->get_dock_tab_container(context_dock);
10081122
int context_tab_index = -1;
10091123
if (context_tab_container && context_tab_container->get_tab_count() > 0) {
10101124
context_tab_index = context_tab_container->get_tab_idx_from_control(context_dock);
@@ -1047,7 +1161,7 @@ void DockContextPopup::_dock_select_draw() {
10471161
}
10481162

10491163
void DockContextPopup::_update_buttons() {
1050-
TabContainer *context_tab_container = dock_manager->get_dock_tab_container(context_dock);
1164+
EditorDockTabContainer *context_tab_container = dock_manager->get_dock_tab_container(context_dock);
10511165
bool dock_at_bottom = dock_manager->_is_dock_at_bottom(context_dock);
10521166

10531167
// Update tab move buttons.

editor/editor_dock_manager.h

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
#include "scene/gui/popup.h"
3434
#include "scene/gui/split_container.h"
35+
#include "scene/gui/tab_container.h"
3536

3637
class Button;
3738
class ConfigFile;
@@ -40,6 +41,32 @@ class PopupMenu;
4041
class TabContainer;
4142
class VBoxContainer;
4243
class WindowWrapper;
44+
class StyleBoxFlat;
45+
46+
class EditorDockTabContainer : public TabContainer {
47+
GDCLASS(EditorDockTabContainer, TabContainer);
48+
49+
private:
50+
friend class EditorDockManager;
51+
52+
Dictionary dock_drop_data;
53+
Color valid_drop_color;
54+
Color invalid_drop_color;
55+
Ref<StyleBoxFlat> dock_drop_highlight;
56+
int dock_margin = 0;
57+
bool is_dragging_dock = false;
58+
bool can_drop_dock = false;
59+
bool mouse_inside = false;
60+
bool is_layout_horizontal = false;
61+
62+
virtual void input(const Ref<InputEvent> &p_event) override;
63+
64+
protected:
65+
void _notification(int p_what);
66+
67+
public:
68+
EditorDockTabContainer();
69+
};
4370

4471
class DockSplitContainer : public SplitContainer {
4572
GDCLASS(DockSplitContainer, SplitContainer);
@@ -97,7 +124,7 @@ class EditorDockManager : public Object {
97124
Vector<DockSplitContainer *> hsplits;
98125

99126
Vector<WindowWrapper *> dock_windows;
100-
TabContainer *dock_slot[DOCK_SLOT_MAX];
127+
EditorDockTabContainer *dock_slot[DOCK_SLOT_MAX];
101128
HashMap<Control *, DockInfo> all_docks;
102129
bool docks_visible = true;
103130

@@ -107,9 +134,9 @@ class EditorDockManager : public Object {
107134
Control *closed_dock_parent = nullptr;
108135

109136
void _dock_split_dragged(int p_offset);
110-
void _dock_container_gui_input(const Ref<InputEvent> &p_input, TabContainer *p_dock_container);
137+
void _dock_container_gui_input(const Ref<InputEvent> &p_input, EditorDockTabContainer *p_dock_container);
111138
void _bottom_dock_button_gui_input(const Ref<InputEvent> &p_input, Control *p_dock, Button *p_bottom_button);
112-
void _dock_container_update_visibility(TabContainer *p_dock_container);
139+
void _dock_container_update_visibility(EditorDockTabContainer *p_dock_container);
113140
void _update_layout();
114141

115142
void _docks_menu_option(int p_id);
@@ -137,7 +164,7 @@ class EditorDockManager : public Object {
137164

138165
void add_vsplit(DockSplitContainer *p_split);
139166
void add_hsplit(DockSplitContainer *p_split);
140-
void register_dock_slot(DockSlot p_dock_slot, TabContainer *p_tab_container);
167+
void register_dock_slot(DockSlot p_dock_slot, EditorDockTabContainer *p_tab_container);
141168
int get_vsplit_count() const;
142169
PopupMenu *get_docks_menu();
143170

@@ -149,7 +176,7 @@ class EditorDockManager : public Object {
149176
void open_dock(Control *p_dock, bool p_set_current = true);
150177
void focus_dock(Control *p_dock);
151178

152-
TabContainer *get_dock_tab_container(Control *p_dock) const;
179+
EditorDockTabContainer *get_dock_tab_container(Control *p_dock) const;
153180

154181
void bottom_dock_show_placement_popup(const Rect2i &p_position, Control *p_dock);
155182

0 commit comments

Comments
 (0)