Skip to content

Commit 75ca1f0

Browse files
committed
Add dock dragging area and highlight
1 parent 7a0ab9d commit 75ca1f0

File tree

4 files changed

+187
-37
lines changed

4 files changed

+187
-37
lines changed

editor/editor_dock_manager.cpp

Lines changed: 138 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,120 @@ 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+
just_inputed = true;
67+
}
68+
}
69+
70+
void EditorDockTabContainer::_notification(int p_what) {
71+
switch (p_what) {
72+
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
73+
int corner_radius = EDSCALE * EDITOR_GET("interface/theme/corner_radius").operator int();
74+
if (corner_radius != dock_drop_highlight->get_corner_radius(CORNER_TOP_LEFT)) {
75+
dock_drop_highlight->set_corner_radius_all(corner_radius);
76+
if (mouse_inside) {
77+
queue_redraw();
78+
}
79+
}
80+
} break;
81+
82+
case NOTIFICATION_POSTINITIALIZE:
83+
case NOTIFICATION_THEME_CHANGED: {
84+
valid_drop_color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
85+
invalid_drop_color = get_theme_color(SNAME("error_color"), EditorStringName(Editor));
86+
} break;
87+
88+
case NOTIFICATION_DRAG_BEGIN: {
89+
if (!is_visible_in_tree()) {
90+
return;
91+
}
92+
93+
dock_drop_data = get_viewport()->gui_get_drag_data();
94+
95+
// Check if we are dragging a dock.
96+
if (dock_drop_data.has("type") && dock_drop_data["type"] == "tab_container_tab") {
97+
Node *from_node = get_node(dock_drop_data["from_path"]);
98+
if (from_node && static_cast<EditorDockTabContainer *>(from_node->get_parent())) {
99+
Control *dock = static_cast<EditorDockTabContainer *>(from_node->get_parent())->get_tab_control(dock_drop_data["tab_index"]);
100+
if (dock) {
101+
is_dragging_dock = true;
102+
mouse_inside = get_global_rect().has_point(get_global_mouse_position());
103+
104+
// TODO: Update logic when GH-106503 is merged to use flags.
105+
can_drop_dock = is_layout_horizontal ? bool(dock->call("_can_dock_horizontal")) : true;
106+
dock_drop_highlight->set_border_color(can_drop_dock ? valid_drop_color : invalid_drop_color);
107+
108+
set_process_input(true);
109+
set_process(true);
110+
queue_redraw();
111+
}
112+
}
113+
}
114+
} break;
115+
case NOTIFICATION_DRAG_END: {
116+
if (!is_dragging_dock) {
117+
return;
118+
}
119+
120+
is_dragging_dock = false;
121+
set_process_input(false);
122+
set_process(false);
123+
124+
if (mouse_inside) {
125+
if (can_drop_dock) {
126+
// Only perform a manual drop if an automatic drop was not performed.
127+
if (!get_tab_bar()->get_global_rect().has_point(get_global_mouse_position())) {
128+
get_tab_bar()->drop_data(get_local_mouse_position(), dock_drop_data);
129+
}
130+
}
131+
132+
queue_redraw();
133+
}
134+
} break;
135+
136+
// Ideally this would be done directly after gui_input but this node doesn't always receive
137+
// gui_input and the input is accepted when dragging, so this can't be done in the input system.
138+
case NOTIFICATION_PROCESS: {
139+
if (just_inputed && is_dragging_dock && mouse_inside) {
140+
just_inputed = false;
141+
DisplayServer::get_singleton()->cursor_set_shape(can_drop_dock ? DisplayServer::CURSOR_CAN_DROP : DisplayServer::CURSOR_FORBIDDEN);
142+
}
143+
} break;
144+
145+
case NOTIFICATION_DRAW: {
146+
// Draw highlights around docks that can be dropped.
147+
if (is_dragging_dock && mouse_inside) {
148+
Rect2 dock_rect = Rect2(Vector2(), get_size()).grow(Math::round(2 * EDSCALE));
149+
draw_style_box(dock_drop_highlight, dock_rect);
150+
}
151+
} break;
152+
153+
case NOTIFICATION_VISIBILITY_CHANGED: {
154+
set_process_input(is_dragging_dock && is_visible_in_tree());
155+
set_process(is_dragging_dock && is_visible_in_tree());
156+
} break;
157+
}
158+
}
159+
160+
EditorDockTabContainer::EditorDockTabContainer() {
161+
dock_drop_highlight.instantiate();
162+
dock_drop_highlight->set_draw_center(false);
163+
dock_drop_highlight->set_corner_radius_all(EDSCALE * EDITOR_GET("interface/theme/corner_radius").operator int());
164+
dock_drop_highlight->set_border_width_all(Math::round(2 * EDSCALE));
165+
}
166+
167+
////////////////////////////////////////////////
168+
////////////////////////////////////////////////
169+
55170
void DockSplitContainer::_update_visibility() {
56171
if (is_updating) {
57172
return;
@@ -114,11 +229,14 @@ void DockSplitContainer::remove_child_notify(Node *p_child) {
114229
_update_visibility();
115230
}
116231

232+
////////////////////////////////////////////////
233+
////////////////////////////////////////////////
234+
117235
void EditorDockManager::_dock_split_dragged(int p_offset) {
118236
EditorNode::get_singleton()->save_editor_layout_delayed();
119237
}
120238

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

124242
if (mb.is_valid() && mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) {
@@ -145,7 +263,7 @@ void EditorDockManager::_bottom_dock_button_gui_input(const Ref<InputEvent> &p_i
145263
}
146264
}
147265

148-
void EditorDockManager::_dock_container_update_visibility(TabContainer *p_dock_container) {
266+
void EditorDockManager::_dock_container_update_visibility(EditorDockTabContainer *p_dock_container) {
149267
if (!docks_visible) {
150268
return;
151269
}
@@ -312,7 +430,7 @@ bool EditorDockManager::_is_dock_at_bottom(Control *p_dock) {
312430
}
313431

314432
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());
433+
EditorDockTabContainer *dock_tab_container = Object::cast_to<EditorDockTabContainer>(p_dock->get_parent());
316434
if (!dock_tab_container) {
317435
return;
318436
}
@@ -349,7 +467,7 @@ void EditorDockManager::_move_dock(Control *p_dock, Control *p_target, int p_tab
349467
_dock_remove_from_bottom(p_dock);
350468
} else {
351469
all_docks[p_dock].previous_at_bottom = false;
352-
TabContainer *parent_tabs = Object::cast_to<TabContainer>(parent);
470+
EditorDockTabContainer *parent_tabs = Object::cast_to<EditorDockTabContainer>(parent);
353471
if (parent_tabs) {
354472
all_docks[p_dock].previous_tab_index = parent_tabs->get_tab_idx_from_control(p_dock);
355473
}
@@ -369,7 +487,7 @@ void EditorDockManager::_move_dock(Control *p_dock, Control *p_target, int p_tab
369487
p_target->set_block_signals(true);
370488
p_target->add_child(p_dock);
371489
p_target->set_block_signals(false);
372-
TabContainer *dock_tab_container = Object::cast_to<TabContainer>(p_target);
490+
EditorDockTabContainer *dock_tab_container = Object::cast_to<EditorDockTabContainer>(p_target);
373491
if (dock_tab_container) {
374492
if (dock_tab_container->is_inside_tree()) {
375493
_update_tab_style(p_dock);
@@ -390,7 +508,7 @@ void EditorDockManager::_update_tab_style(Control *p_dock) {
390508
return; // Floating or sent to bottom.
391509
}
392510

393-
TabContainer *tab_container = get_dock_tab_container(p_dock);
511+
EditorDockTabContainer *tab_container = get_dock_tab_container(p_dock);
394512
ERR_FAIL_NULL(tab_container);
395513
int index = tab_container->get_tab_idx_from_control(p_dock);
396514
ERR_FAIL_COND(index == -1);
@@ -682,7 +800,7 @@ void EditorDockManager::open_dock(Control *p_dock, bool p_set_current) {
682800
if (all_docks[p_dock].previous_at_bottom) {
683801
_dock_move_to_bottom(p_dock, true);
684802
} else if (all_docks[p_dock].dock_slot_index != DOCK_SLOT_NONE) {
685-
TabContainer *slot = dock_slot[all_docks[p_dock].dock_slot_index];
803+
EditorDockTabContainer *slot = dock_slot[all_docks[p_dock].dock_slot_index];
686804
int tab_index = all_docks[p_dock].previous_tab_index;
687805
if (tab_index < 0) {
688806
tab_index = slot->get_tab_count();
@@ -696,8 +814,8 @@ void EditorDockManager::open_dock(Control *p_dock, bool p_set_current) {
696814
_update_layout();
697815
}
698816

699-
TabContainer *EditorDockManager::get_dock_tab_container(Control *p_dock) const {
700-
return Object::cast_to<TabContainer>(p_dock->get_parent());
817+
EditorDockTabContainer *EditorDockManager::get_dock_tab_container(Control *p_dock) const {
818+
return Object::cast_to<EditorDockTabContainer>(p_dock->get_parent());
701819
}
702820

703821
void EditorDockManager::focus_dock(Control *p_dock) {
@@ -726,7 +844,7 @@ void EditorDockManager::focus_dock(Control *p_dock) {
726844
return;
727845
}
728846

729-
TabContainer *tab_container = get_dock_tab_container(p_dock);
847+
EditorDockTabContainer *tab_container = get_dock_tab_container(p_dock);
730848
if (!tab_container) {
731849
return;
732850
}
@@ -797,7 +915,7 @@ void EditorDockManager::update_tab_styles() {
797915

798916
void EditorDockManager::set_tab_icon_max_width(int p_max_width) {
799917
for (int i = 0; i < DOCK_SLOT_MAX; i++) {
800-
TabContainer *tab_container = dock_slot[i];
918+
EditorDockTabContainer *tab_container = dock_slot[i];
801919
tab_container->add_theme_constant_override(SNAME("icon_max_width"), p_max_width);
802920
}
803921
}
@@ -812,7 +930,7 @@ void EditorDockManager::add_hsplit(DockSplitContainer *p_split) {
812930
p_split->connect("dragged", callable_mp(this, &EditorDockManager::_dock_split_dragged));
813931
}
814932

815-
void EditorDockManager::register_dock_slot(DockSlot p_dock_slot, TabContainer *p_tab_container) {
933+
void EditorDockManager::register_dock_slot(DockSlot p_dock_slot, EditorDockTabContainer *p_tab_container) {
816934
ERR_FAIL_NULL(p_tab_container);
817935
ERR_FAIL_INDEX(p_dock_slot, DOCK_SLOT_MAX);
818936

@@ -854,6 +972,9 @@ EditorDockManager::EditorDockManager() {
854972
EditorNode::get_singleton()->get_gui_base()->connect(SceneStringName(theme_changed), callable_mp(this, &EditorDockManager::update_docks_menu));
855973
}
856974

975+
////////////////////////////////////////////////
976+
////////////////////////////////////////////////
977+
857978
void DockContextPopup::_notification(int p_what) {
858979
switch (p_what) {
859980
case Control::NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
@@ -880,7 +1001,7 @@ void DockContextPopup::_notification(int p_what) {
8801001
}
8811002

8821003
void DockContextPopup::_tab_move_left() {
883-
TabContainer *tab_container = dock_manager->get_dock_tab_container(context_dock);
1004+
EditorDockTabContainer *tab_container = dock_manager->get_dock_tab_container(context_dock);
8841005
if (!tab_container) {
8851006
return;
8861007
}
@@ -891,7 +1012,7 @@ void DockContextPopup::_tab_move_left() {
8911012
}
8921013

8931014
void DockContextPopup::_tab_move_right() {
894-
TabContainer *tab_container = dock_manager->get_dock_tab_container(context_dock);
1015+
EditorDockTabContainer *tab_container = dock_manager->get_dock_tab_container(context_dock);
8951016
if (!tab_container) {
8961017
return;
8971018
}
@@ -941,7 +1062,7 @@ void DockContextPopup::_dock_select_input(const Ref<InputEvent> &p_input) {
9411062
}
9421063

9431064
Ref<InputEventMouseButton> mb = me;
944-
TabContainer *target_tab_container = dock_manager->dock_slot[over_dock_slot];
1065+
EditorDockTabContainer *target_tab_container = dock_manager->dock_slot[over_dock_slot];
9451066

9461067
if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
9471068
if (dock_manager->get_dock_tab_container(context_dock) != target_tab_container) {
@@ -1004,7 +1125,7 @@ void DockContextPopup::_dock_select_draw() {
10041125
real_t dock_spacing = 2.0 * EDSCALE;
10051126
real_t dock_top_spacing = tab_height + dock_spacing;
10061127

1007-
TabContainer *context_tab_container = dock_manager->get_dock_tab_container(context_dock);
1128+
EditorDockTabContainer *context_tab_container = dock_manager->get_dock_tab_container(context_dock);
10081129
int context_tab_index = -1;
10091130
if (context_tab_container && context_tab_container->get_tab_count() > 0) {
10101131
context_tab_index = context_tab_container->get_tab_idx_from_control(context_dock);
@@ -1047,7 +1168,7 @@ void DockContextPopup::_dock_select_draw() {
10471168
}
10481169

10491170
void DockContextPopup::_update_buttons() {
1050-
TabContainer *context_tab_container = dock_manager->get_dock_tab_container(context_dock);
1171+
EditorDockTabContainer *context_tab_container = dock_manager->get_dock_tab_container(context_dock);
10511172
bool dock_at_bottom = dock_manager->_is_dock_at_bottom(context_dock);
10521173

10531174
// 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+
bool is_dragging_dock = false;
57+
bool can_drop_dock = false;
58+
bool mouse_inside = false;
59+
bool is_layout_horizontal = false;
60+
bool just_inputed = 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)