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
4748enum class TabStyle {
4849 TEXT_ONLY,
@@ -52,6 +53,115 @@ enum class TabStyle {
5253
5354EditorDockManager *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+ // Drop dock into last spot if not over tabbar.
62+ if (drop_tabbar->get_rect ().has_point (p_point)) {
63+ 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));
64+ } else {
65+ dock_manager->_move_dock (dock_manager->_get_dock_tab_dragged (), dock_manager->dock_slot [occupied_slot], drop_tabbar->get_tab_count ());
66+ }
67+ }
68+
69+ void EditorDockDragHint::_drag_move_tab (int p_from_index, int p_to_index) {
70+ dock_manager->_move_dock_tab_index (dock_manager->_get_dock_tab_dragged (), p_to_index, true );
71+ }
72+
73+ void EditorDockDragHint::_drag_move_tab_from (TabBar *p_from_tabbar, int p_from_index, int p_to_index) {
74+ dock_manager->_move_dock (dock_manager->_get_dock_tab_dragged (), dock_manager->dock_slot [occupied_slot], p_to_index);
75+ }
76+
77+ void EditorDockDragHint::gui_input (const Ref<InputEvent> &p_event) {
78+ ERR_FAIL_COND (p_event.is_null ());
79+
80+ Ref<InputEventMouseMotion> mm = p_event;
81+ if (mm.is_valid ()) {
82+ Point2 pos = mm->get_position ();
83+
84+ // Redraw when inside the tabbar and just exited.
85+ if (mouse_inside_tabbar) {
86+ queue_redraw ();
87+ }
88+ mouse_inside_tabbar = drop_tabbar->get_rect ().has_point (pos);
89+ }
90+ }
91+
92+ void EditorDockDragHint::set_slot (EditorDockManager::DockSlot p_slot) {
93+ occupied_slot = p_slot;
94+ drop_tabbar = dock_manager->dock_slot [occupied_slot]->get_tab_bar ();
95+ }
96+
97+ void EditorDockDragHint::_notification (int p_what) {
98+ switch (p_what) {
99+ case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
100+ if (EditorSettings::get_singleton ()->check_changed_settings_in_group (" interface/theme" )) {
101+ dock_drop_highlight->set_corner_radius_all (EDSCALE * EDITOR_GET (" interface/theme/corner_radius" ).operator int ());
102+ if (mouse_inside) {
103+ queue_redraw ();
104+ }
105+ }
106+ } break ;
107+
108+ case NOTIFICATION_THEME_CHANGED: {
109+ valid_drop_color = get_theme_color (SNAME (" accent_color" ), EditorStringName (Editor));
110+ } break ;
111+
112+ case NOTIFICATION_MOUSE_ENTER:
113+ case NOTIFICATION_MOUSE_EXIT: {
114+ mouse_inside = p_what == NOTIFICATION_MOUSE_ENTER;
115+ queue_redraw ();
116+ } break ;
117+
118+ case NOTIFICATION_DRAG_BEGIN: {
119+ Control *dragged_dock = dock_manager->_get_dock_tab_dragged ();
120+ if (!dragged_dock) {
121+ return ;
122+ }
123+
124+ can_drop_dock = true ;
125+
126+ dock_drop_highlight->set_border_color (valid_drop_color);
127+ dock_drop_highlight->set_bg_color (valid_drop_color * Color (1 , 1 , 1 , 0.1 ));
128+ } break ;
129+ case NOTIFICATION_DRAG_END: {
130+ dock_manager->_dock_drag_stopped ();
131+ can_drop_dock = false ;
132+ mouse_inside = false ;
133+ hide ();
134+ } break ;
135+
136+ case NOTIFICATION_DRAW: {
137+ if (!mouse_inside) {
138+ return ;
139+ }
140+
141+ // Draw highlights around docks that can be dropped.
142+ Rect2 dock_rect = Rect2 (Point2 (), get_size ()).grow (2 * EDSCALE);
143+ draw_style_box (dock_drop_highlight, dock_rect);
144+
145+ // Only display tabbar hint if the mouse is over the tabbar.
146+ if (drop_tabbar->get_global_rect ().has_point (get_global_mouse_position ())) {
147+ drop_tabbar->_draw_tab_drop (get_canvas_item ());
148+ }
149+ } break ;
150+ }
151+ }
152+
153+ EditorDockDragHint::EditorDockDragHint () {
154+ dock_manager = EditorDockManager::get_singleton ();
155+
156+ set_as_top_level (true );
157+ dock_drop_highlight.instantiate ();
158+ dock_drop_highlight->set_corner_radius_all (EDSCALE * EDITOR_GET (" interface/theme/corner_radius" ).operator int ());
159+ dock_drop_highlight->set_border_width_all (Math::round (2 * EDSCALE));
160+ }
161+
162+ // //////////////////////////////////////////////
163+ // //////////////////////////////////////////////
164+
55165void DockSplitContainer::_update_visibility () {
56166 if (is_updating) {
57167 return ;
@@ -120,6 +230,56 @@ DockSplitContainer::DockSplitContainer() {
120230 }
121231}
122232
233+ // //////////////////////////////////////////////
234+ // //////////////////////////////////////////////
235+
236+ Control *EditorDockManager::_get_dock_tab_dragged () {
237+ if (dock_tab_dragged) {
238+ return dock_tab_dragged;
239+ }
240+
241+ Dictionary dock_drop_data = dock_slot[DOCK_SLOT_LEFT_BL]->get_viewport ()->gui_get_drag_data ();
242+
243+ // Check if we are dragging a dock.
244+ const String type = dock_drop_data.get (" type" , " " );
245+ if (type == " tab_container_tab" ) {
246+ Node *from_node = dock_slot[DOCK_SLOT_LEFT_BL]->get_node (dock_drop_data[" from_path" ]);
247+ if (!from_node) {
248+ return nullptr ;
249+ }
250+
251+ TabContainer *parent = Object::cast_to<TabContainer>(from_node->get_parent ());
252+ if (!parent) {
253+ return nullptr ;
254+ }
255+
256+ // TODO: Update logic when GH-106503 is merged to cast directly to EditorDock instead of the below check.
257+ for (int i = 0 ; i < DOCK_SLOT_MAX; i++) {
258+ if (dock_slot[i] == parent) {
259+ dock_tab_dragged = parent->get_tab_control (dock_drop_data[" tab_index" ]);
260+ break ;
261+ }
262+ }
263+ if (!dock_tab_dragged) {
264+ return nullptr ;
265+ }
266+
267+ for (int i = 0 ; i < DOCK_SLOT_MAX; i++) {
268+ if (dock_slot[i]->is_visible_in_tree ()) {
269+ dock_drag_rects[i]->set_rect (dock_slot[i]->get_global_rect ());
270+ dock_drag_rects[i]->show ();
271+ }
272+ }
273+
274+ return dock_tab_dragged;
275+ }
276+ return nullptr ;
277+ }
278+
279+ void EditorDockManager::_dock_drag_stopped () {
280+ dock_tab_dragged = nullptr ;
281+ }
282+
123283void EditorDockManager::_dock_split_dragged (int p_offset) {
124284 EditorNode::get_singleton ()->save_editor_layout_delayed ();
125285}
@@ -836,6 +996,12 @@ void EditorDockManager::register_dock_slot(DockSlot p_dock_slot, TabContainer *p
836996 p_tab_container->set_use_hidden_tabs_for_min_size (true );
837997 p_tab_container->get_tab_bar ()->connect (SceneStringName (gui_input), callable_mp (this , &EditorDockManager::_dock_container_gui_input).bind (p_tab_container));
838998 p_tab_container->hide ();
999+
1000+ // Create dock dragging hint.
1001+ dock_drag_rects[p_dock_slot] = memnew (EditorDockDragHint);
1002+ dock_drag_rects[p_dock_slot]->set_slot (p_dock_slot);
1003+ dock_drag_rects[p_dock_slot]->hide ();
1004+ EditorNode::get_singleton ()->get_gui_base ()->add_child (dock_drag_rects[p_dock_slot]);
8391005}
8401006
8411007int EditorDockManager::get_vsplit_count () const {
@@ -860,6 +1026,9 @@ EditorDockManager::EditorDockManager() {
8601026 EditorNode::get_singleton ()->get_gui_base ()->connect (SceneStringName (theme_changed), callable_mp (this , &EditorDockManager::update_docks_menu));
8611027}
8621028
1029+ // //////////////////////////////////////////////
1030+ // //////////////////////////////////////////////
1031+
8631032void DockContextPopup::_notification (int p_what) {
8641033 switch (p_what) {
8651034 case Control::NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
0 commit comments