Skip to content

Commit 47c5035

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

File tree

4 files changed

+161
-14
lines changed

4 files changed

+161
-14
lines changed

editor/editor_dock_manager.cpp

Lines changed: 121 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,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+
// Only run if we are dragging a dock.
59+
Ref<InputEventMouseMotion> me = p_event;
60+
if (me.is_valid()) {
61+
if (mouse_inside != get_global_rect().has_point(me->get_position())) {
62+
mouse_inside = !mouse_inside;
63+
queue_redraw();
64+
}
65+
66+
if (mouse_inside) {
67+
callable_mp(DisplayServer::get_singleton(), &DisplayServer::cursor_set_shape).call_deferred(can_drop_dock ? DisplayServer::CURSOR_CAN_DROP : DisplayServer::CURSOR_FORBIDDEN);
68+
}
69+
}
70+
}
71+
72+
void EditorDockTabContainer::_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 (mouse_inside) {
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_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+
const String type = dock_drop_data.get("type", "");
97+
if (type == "tab_container_tab") {
98+
Node *from_node = get_node(dock_drop_data["from_path"]);
99+
if (!from_node) {
100+
return;
101+
}
102+
103+
EditorDockTabContainer *parent = Object::cast_to<EditorDockTabContainer>(from_node->get_parent());
104+
if (!parent) {
105+
return;
106+
}
107+
108+
Control *dock = parent->get_tab_control(dock_drop_data["tab_index"]);
109+
if (!dock) {
110+
return;
111+
}
112+
113+
is_dragging_dock = true;
114+
mouse_inside = get_global_rect().has_point(get_global_mouse_position());
115+
116+
// TODO: Update logic when GH-106503 is merged to use flags.
117+
can_drop_dock = is_layout_horizontal ? bool(dock->call("_can_dock_horizontal")) : true;
118+
dock_drop_highlight->set_border_color(can_drop_dock ? valid_drop_color : invalid_drop_color);
119+
120+
set_process_input(true);
121+
queue_redraw();
122+
}
123+
} break;
124+
case NOTIFICATION_DRAG_END: {
125+
if (!is_dragging_dock) {
126+
return;
127+
}
128+
129+
is_dragging_dock = false;
130+
set_process_input(false);
131+
132+
if (mouse_inside && can_drop_dock) {
133+
// Only perform a manual drop if an automatic drop was not performed.
134+
if (!get_tab_bar()->get_global_rect().has_point(get_global_mouse_position())) {
135+
get_tab_bar()->drop_data(get_local_mouse_position(), dock_drop_data);
136+
}
137+
138+
queue_redraw();
139+
} else if (mouse_inside) {
140+
queue_redraw();
141+
}
142+
143+
mouse_inside = false;
144+
} break;
145+
146+
case NOTIFICATION_DRAW: {
147+
// Draw highlights around docks that can be dropped.
148+
if (is_dragging_dock && mouse_inside) {
149+
Rect2 dock_rect = Rect2(Vector2(), get_size()).grow(Math::round(2 * EDSCALE));
150+
draw_style_box(dock_drop_highlight, dock_rect);
151+
}
152+
} break;
153+
154+
case NOTIFICATION_VISIBILITY_CHANGED: {
155+
set_process_input(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,6 +229,9 @@ 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
}
@@ -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:

editor/editor_dock_manager.h

Lines changed: 24 additions & 0 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,29 @@ 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+
Dictionary dock_drop_data;
51+
Color valid_drop_color;
52+
Color invalid_drop_color;
53+
Ref<StyleBoxFlat> dock_drop_highlight;
54+
bool is_layout_horizontal = false;
55+
bool is_dragging_dock = false;
56+
bool can_drop_dock = false;
57+
bool mouse_inside = false;
58+
59+
virtual void input(const Ref<InputEvent> &p_event) override;
60+
61+
protected:
62+
void _notification(int p_what);
63+
64+
public:
65+
EditorDockTabContainer();
66+
};
4367

4468
class DockSplitContainer : public SplitContainer {
4569
GDCLASS(DockSplitContainer, SplitContainer);

editor/editor_node.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7642,10 +7642,10 @@ EditorNode::EditorNode() {
76427642
left_l_hsplit->add_child(left_l_vsplit);
76437643

76447644
TabContainer *dock_slot[EditorDockManager::DOCK_SLOT_MAX];
7645-
dock_slot[EditorDockManager::DOCK_SLOT_LEFT_UL] = memnew(TabContainer);
7645+
dock_slot[EditorDockManager::DOCK_SLOT_LEFT_UL] = memnew(EditorDockTabContainer);
76467646
dock_slot[EditorDockManager::DOCK_SLOT_LEFT_UL]->set_name("DockSlotLeftUL");
76477647
left_l_vsplit->add_child(dock_slot[EditorDockManager::DOCK_SLOT_LEFT_UL]);
7648-
dock_slot[EditorDockManager::DOCK_SLOT_LEFT_BL] = memnew(TabContainer);
7648+
dock_slot[EditorDockManager::DOCK_SLOT_LEFT_BL] = memnew(EditorDockTabContainer);
76497649
dock_slot[EditorDockManager::DOCK_SLOT_LEFT_BL]->set_name("DockSlotLeftBL");
76507650
left_l_vsplit->add_child(dock_slot[EditorDockManager::DOCK_SLOT_LEFT_BL]);
76517651

@@ -7656,10 +7656,10 @@ EditorNode::EditorNode() {
76567656
left_r_vsplit->set_name("DockVSplitLeftR");
76577657
left_r_vsplit->set_vertical(true);
76587658
left_r_hsplit->add_child(left_r_vsplit);
7659-
dock_slot[EditorDockManager::DOCK_SLOT_LEFT_UR] = memnew(TabContainer);
7659+
dock_slot[EditorDockManager::DOCK_SLOT_LEFT_UR] = memnew(EditorDockTabContainer);
76607660
dock_slot[EditorDockManager::DOCK_SLOT_LEFT_UR]->set_name("DockSlotLeftUR");
76617661
left_r_vsplit->add_child(dock_slot[EditorDockManager::DOCK_SLOT_LEFT_UR]);
7662-
dock_slot[EditorDockManager::DOCK_SLOT_LEFT_BR] = memnew(TabContainer);
7662+
dock_slot[EditorDockManager::DOCK_SLOT_LEFT_BR] = memnew(EditorDockTabContainer);
76637663
dock_slot[EditorDockManager::DOCK_SLOT_LEFT_BR]->set_name("DockSlotLeftBR");
76647664
left_r_vsplit->add_child(dock_slot[EditorDockManager::DOCK_SLOT_LEFT_BR]);
76657665

@@ -7686,21 +7686,21 @@ EditorNode::EditorNode() {
76867686
right_l_vsplit->set_name("DockVSplitRightL");
76877687
right_l_vsplit->set_vertical(true);
76887688
right_hsplit->add_child(right_l_vsplit);
7689-
dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_UL] = memnew(TabContainer);
7689+
dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_UL] = memnew(EditorDockTabContainer);
76907690
dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_UL]->set_name("DockSlotRightUL");
76917691
right_l_vsplit->add_child(dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_UL]);
7692-
dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_BL] = memnew(TabContainer);
7692+
dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_BL] = memnew(EditorDockTabContainer);
76937693
dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_BL]->set_name("DockSlotRightBL");
76947694
right_l_vsplit->add_child(dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_BL]);
76957695

76967696
right_r_vsplit = memnew(DockSplitContainer);
76977697
right_r_vsplit->set_name("DockVSplitRightR");
76987698
right_r_vsplit->set_vertical(true);
76997699
right_hsplit->add_child(right_r_vsplit);
7700-
dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_UR] = memnew(TabContainer);
7700+
dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_UR] = memnew(EditorDockTabContainer);
77017701
dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_UR]->set_name("DockSlotRightUR");
77027702
right_r_vsplit->add_child(dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_UR]);
7703-
dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_BR] = memnew(TabContainer);
7703+
dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_BR] = memnew(EditorDockTabContainer);
77047704
dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_BR]->set_name("DockSlotRightBR");
77057705
right_r_vsplit->add_child(dock_slot[EditorDockManager::DOCK_SLOT_RIGHT_BR]);
77067706

scene/gui/tab_bar.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
class TabBar : public Control {
3838
GDCLASS(TabBar, Control);
3939

40+
friend class TabContainer;
41+
4042
public:
4143
enum AlignmentMode {
4244
ALIGNMENT_LEFT,
@@ -189,18 +191,18 @@ class TabBar : public Control {
189191
void _notification(int p_what);
190192
static void _bind_methods();
191193

192-
Variant get_drag_data(const Point2 &p_point) override;
193-
bool can_drop_data(const Point2 &p_point, const Variant &p_data) const override;
194-
void drop_data(const Point2 &p_point, const Variant &p_data) override;
194+
Variant _handle_get_drag_data(const String &p_type, const Point2 &p_point);
195+
bool _handle_can_drop_data(const String &p_type, const Point2 &p_point, const Variant &p_data) const;
196+
void _handle_drop_data(const String &p_type, const Point2 &p_point, const Variant &p_data, const Callable &p_move_tab_callback, const Callable &p_move_tab_from_other_callback);
195197
void _move_tab_from(TabBar *p_from_tabbar, int p_from_index, int p_to_index);
196198

197199
public:
198200
RID get_tab_accessibility_element(int p_tab) const;
199201
virtual RID get_focused_accessibility_element() const override;
200202

201-
Variant _handle_get_drag_data(const String &p_type, const Point2 &p_point);
202-
bool _handle_can_drop_data(const String &p_type, const Point2 &p_point, const Variant &p_data) const;
203-
void _handle_drop_data(const String &p_type, const Point2 &p_point, const Variant &p_data, const Callable &p_move_tab_callback, const Callable &p_move_tab_from_other_callback);
203+
Variant get_drag_data(const Point2 &p_point) override;
204+
bool can_drop_data(const Point2 &p_point, const Variant &p_data) const override;
205+
void drop_data(const Point2 &p_point, const Variant &p_data) override;
204206

205207
void add_tab(const String &p_str = "", const Ref<Texture2D> &p_icon = Ref<Texture2D>());
206208

0 commit comments

Comments
 (0)