Skip to content

Commit cf45af4

Browse files
committed
Add dock dragging area and highlight
1 parent 6c9765d commit cf45af4

File tree

3 files changed

+174
-0
lines changed

3 files changed

+174
-0
lines changed

editor/editor_dock_manager.cpp

Lines changed: 139 additions & 0 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,86 @@ enum class TabStyle {
5253

5354
EditorDockManager *EditorDockManager::singleton = nullptr;
5455

56+
bool EditorDockDragHint::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
57+
return can_drop_dock;
58+
}
59+
60+
void EditorDockDragHint::drop_data(const Point2 &p_point, const Variant &p_data) {
61+
if (can_drop_dock) {
62+
Control *dragged_dock = dock_manager->_get_dock_tab_dragged();
63+
int drop_tab = dock_manager->dock_slot[occupied_slot]->get_tab_bar()->get_closest_tab_idx_to_point(p_point);
64+
dock_manager->_move_dock(dragged_dock, dock_manager->dock_slot[occupied_slot], drop_tab);
65+
}
66+
}
67+
68+
void EditorDockDragHint::set_slot(EditorDockManager::DockSlot p_slot) {
69+
occupied_slot = p_slot;
70+
}
71+
72+
void EditorDockDragHint::_notification(int p_what) {
73+
switch (p_what) {
74+
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
75+
if (EditorSettings::get_singleton()->check_changed_settings_in_group("interface/theme")) {
76+
dock_drop_highlight->set_corner_radius_all(EDSCALE * EDITOR_GET("interface/theme/corner_radius").operator int());
77+
if (is_visible_in_tree()) {
78+
queue_redraw();
79+
}
80+
}
81+
} break;
82+
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_MOUSE_ENTER:
89+
case NOTIFICATION_MOUSE_EXIT: {
90+
bool new_mouse_inside = p_what == NOTIFICATION_MOUSE_ENTER;
91+
if (new_mouse_inside != mouse_inside) {
92+
mouse_inside = new_mouse_inside;
93+
queue_redraw();
94+
}
95+
} break;
96+
97+
case NOTIFICATION_DRAG_BEGIN: {
98+
Control *dragged_dock = dock_manager->_get_dock_tab_dragged();
99+
if (!dragged_dock) {
100+
return;
101+
}
102+
103+
// TODO: Update logic when GH-106503 is merged to use flags.
104+
can_drop_dock = is_layout_horizontal ? bool(dragged_dock->call("_can_dock_horizontal")) : true;
105+
dock_drop_highlight->set_border_color(can_drop_dock ? valid_drop_color : invalid_drop_color);
106+
} break;
107+
case NOTIFICATION_DRAG_END: {
108+
dock_manager->_dock_drag_stopped();
109+
can_drop_dock = false;
110+
hide();
111+
} break;
112+
113+
case NOTIFICATION_DRAW: {
114+
// Draw highlights around docks that can be dropped.
115+
if (mouse_inside) {
116+
Rect2 dock_rect = Rect2(Point2(), get_size()).grow(2 * EDSCALE);
117+
draw_style_box(dock_drop_highlight, dock_rect);
118+
}
119+
} break;
120+
}
121+
}
122+
123+
EditorDockDragHint::EditorDockDragHint() {
124+
dock_manager = EditorDockManager::get_singleton();
125+
126+
set_as_top_level(true);
127+
dock_drop_highlight.instantiate();
128+
dock_drop_highlight->set_draw_center(false);
129+
dock_drop_highlight->set_corner_radius_all(EDSCALE * EDITOR_GET("interface/theme/corner_radius").operator int());
130+
dock_drop_highlight->set_border_width_all(Math::round(2 * EDSCALE));
131+
}
132+
133+
////////////////////////////////////////////////
134+
////////////////////////////////////////////////
135+
55136
void DockSplitContainer::_update_visibility() {
56137
if (is_updating) {
57138
return;
@@ -114,6 +195,55 @@ void DockSplitContainer::remove_child_notify(Node *p_child) {
114195
_update_visibility();
115196
}
116197

198+
////////////////////////////////////////////////
199+
////////////////////////////////////////////////
200+
201+
Control *EditorDockManager::_get_dock_tab_dragged() {
202+
if (dock_tab_dragged) {
203+
return dock_tab_dragged;
204+
}
205+
206+
Dictionary dock_drop_data = dock_slot[DOCK_SLOT_LEFT_BL]->get_viewport()->gui_get_drag_data();
207+
208+
// Check if we are dragging a dock.
209+
const String type = dock_drop_data.get("type", "");
210+
if (type == "tab_container_tab") {
211+
Node *from_node = dock_slot[DOCK_SLOT_LEFT_BL]->get_node(dock_drop_data["from_path"]);
212+
if (!from_node) {
213+
return nullptr;
214+
}
215+
216+
TabContainer *parent = Object::cast_to<TabContainer>(from_node->get_parent());
217+
if (!parent) {
218+
return nullptr;
219+
}
220+
221+
// TODO: Update logic when GH-106503 is merged to cast to EditorDock and remove friend class.
222+
bool dock_found = false;
223+
for (int i = 0; i < DOCK_SLOT_MAX; i++) {
224+
if (dock_slot[i] == parent) {
225+
dock_tab_dragged = parent->get_tab_control(dock_drop_data["tab_index"]);
226+
dock_found = true;
227+
}
228+
}
229+
if (!dock_found) {
230+
return nullptr;
231+
}
232+
233+
for (int i = 0; i < DOCK_SLOT_MAX; i++) {
234+
if (dock_slot[i]->is_visible_in_tree()) {
235+
dock_drag_rects[i]->set_rect(dock_slot[i]->get_global_rect());
236+
dock_drag_rects[i]->show();
237+
}
238+
}
239+
}
240+
return nullptr;
241+
}
242+
243+
void EditorDockManager::_dock_drag_stopped() {
244+
dock_tab_dragged = nullptr;
245+
}
246+
117247
void EditorDockManager::_dock_split_dragged(int p_offset) {
118248
EditorNode::get_singleton()->save_editor_layout_delayed();
119249
}
@@ -830,6 +960,12 @@ void EditorDockManager::register_dock_slot(DockSlot p_dock_slot, TabContainer *p
830960
p_tab_container->set_use_hidden_tabs_for_min_size(true);
831961
p_tab_container->get_tab_bar()->connect(SceneStringName(gui_input), callable_mp(this, &EditorDockManager::_dock_container_gui_input).bind(p_tab_container));
832962
p_tab_container->hide();
963+
964+
// Create dock dragging hint.
965+
dock_drag_rects[p_dock_slot] = memnew(EditorDockDragHint);
966+
dock_drag_rects[p_dock_slot]->set_slot(p_dock_slot);
967+
dock_drag_rects[p_dock_slot]->hide();
968+
EditorNode::get_singleton()->get_gui_base()->add_child(dock_drag_rects[p_dock_slot]);
833969
}
834970

835971
int EditorDockManager::get_vsplit_count() const {
@@ -854,6 +990,9 @@ EditorDockManager::EditorDockManager() {
854990
EditorNode::get_singleton()->get_gui_base()->connect(SceneStringName(theme_changed), callable_mp(this, &EditorDockManager::update_docks_menu));
855991
}
856992

993+
////////////////////////////////////////////////
994+
////////////////////////////////////////////////
995+
857996
void DockContextPopup::_notification(int p_what) {
858997
switch (p_what) {
859998
case Control::NOTIFICATION_LAYOUT_DIRECTION_CHANGED:

editor/editor_dock_manager.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class PopupMenu;
4040
class TabContainer;
4141
class VBoxContainer;
4242
class WindowWrapper;
43+
class StyleBoxFlat;
4344

4445
class DockSplitContainer : public SplitContainer {
4546
GDCLASS(DockSplitContainer, SplitContainer);
@@ -55,6 +56,7 @@ class DockSplitContainer : public SplitContainer {
5556
};
5657

5758
class DockContextPopup;
59+
class EditorDockDragHint;
5860

5961
class EditorDockManager : public Object {
6062
GDCLASS(EditorDockManager, Object);
@@ -75,6 +77,7 @@ class EditorDockManager : public Object {
7577

7678
private:
7779
friend class DockContextPopup;
80+
friend class EditorDockDragHint;
7881

7982
struct DockInfo {
8083
String title;
@@ -98,14 +101,18 @@ class EditorDockManager : public Object {
98101

99102
Vector<WindowWrapper *> dock_windows;
100103
TabContainer *dock_slot[DOCK_SLOT_MAX];
104+
EditorDockDragHint *dock_drag_rects[DOCK_SLOT_MAX];
101105
HashMap<Control *, DockInfo> all_docks;
106+
Control *dock_tab_dragged = nullptr;
102107
bool docks_visible = true;
103108

104109
DockContextPopup *dock_context_popup = nullptr;
105110
PopupMenu *docks_menu = nullptr;
106111
Vector<Control *> docks_menu_docks;
107112
Control *closed_dock_parent = nullptr;
108113

114+
Control *_get_dock_tab_dragged();
115+
void _dock_drag_stopped();
109116
void _dock_split_dragged(int p_offset);
110117
void _dock_container_gui_input(const Ref<InputEvent> &p_input, TabContainer *p_dock_container);
111118
void _bottom_dock_button_gui_input(const Ref<InputEvent> &p_input, Control *p_dock, Button *p_bottom_button);
@@ -164,9 +171,36 @@ class EditorDockManager : public Object {
164171
EditorDockManager();
165172
};
166173

174+
class EditorDockDragHint : public Control {
175+
GDCLASS(EditorDockDragHint, Control);
176+
177+
private:
178+
EditorDockManager *dock_manager = nullptr;
179+
EditorDockManager::DockSlot occupied_slot = EditorDockManager::DOCK_SLOT_MAX;
180+
181+
Color valid_drop_color;
182+
Color invalid_drop_color;
183+
Ref<StyleBoxFlat> dock_drop_highlight;
184+
bool is_layout_horizontal = false;
185+
bool can_drop_dock = false;
186+
bool mouse_inside = false;
187+
188+
protected:
189+
void _notification(int p_what);
190+
191+
public:
192+
bool can_drop_data(const Point2 &p_point, const Variant &p_data) const override;
193+
void drop_data(const Point2 &p_point, const Variant &p_data) override;
194+
195+
void set_slot(EditorDockManager::DockSlot p_slot);
196+
197+
EditorDockDragHint();
198+
};
199+
167200
class DockContextPopup : public PopupPanel {
168201
GDCLASS(DockContextPopup, PopupPanel);
169202

203+
private:
170204
VBoxContainer *dock_select_popup_vb = nullptr;
171205

172206
Button *make_float_button = nullptr;

editor/editor_node.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7717,6 +7717,7 @@ EditorNode::EditorNode() {
77177717
editor_dock_manager->add_hsplit(main_hsplit);
77187718
editor_dock_manager->add_hsplit(right_hsplit);
77197719

7720+
// Register dock TabContainers.
77207721
for (int i = 0; i < EditorDockManager::DOCK_SLOT_MAX; i++) {
77217722
editor_dock_manager->register_dock_slot((EditorDockManager::DockSlot)i, dock_slot[i]);
77227723
}

0 commit comments

Comments
 (0)