From 90be4208e8605138e062361d4433ee1c22231f64 Mon Sep 17 00:00:00 2001 From: kit Date: Fri, 6 Mar 2026 17:45:01 -0500 Subject: [PATCH] Use SplitContainer for Node3D Editor viewports --- editor/icons/GuiViewportHdiagsplitter.svg | 1 - editor/icons/GuiViewportVdiagsplitter.svg | 1 - editor/icons/GuiViewportVhsplitter.svg | 1 - editor/scene/3d/node_3d_editor_plugin.cpp | 401 ++++++---------------- editor/scene/3d/node_3d_editor_plugin.h | 26 +- scene/gui/split_container.cpp | 9 +- 6 files changed, 130 insertions(+), 309 deletions(-) delete mode 100644 editor/icons/GuiViewportHdiagsplitter.svg delete mode 100644 editor/icons/GuiViewportVdiagsplitter.svg delete mode 100644 editor/icons/GuiViewportVhsplitter.svg diff --git a/editor/icons/GuiViewportHdiagsplitter.svg b/editor/icons/GuiViewportHdiagsplitter.svg deleted file mode 100644 index ad55722da1..0000000000 --- a/editor/icons/GuiViewportHdiagsplitter.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/editor/icons/GuiViewportVdiagsplitter.svg b/editor/icons/GuiViewportVdiagsplitter.svg deleted file mode 100644 index d5f035b4e6..0000000000 --- a/editor/icons/GuiViewportVdiagsplitter.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/editor/icons/GuiViewportVhsplitter.svg b/editor/icons/GuiViewportVhsplitter.svg deleted file mode 100644 index ae3746ea9f..0000000000 --- a/editor/icons/GuiViewportVhsplitter.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/editor/scene/3d/node_3d_editor_plugin.cpp b/editor/scene/3d/node_3d_editor_plugin.cpp index dbe5e5e168..e758c10713 100644 --- a/editor/scene/3d/node_3d_editor_plugin.cpp +++ b/editor/scene/3d/node_3d_editor_plugin.cpp @@ -6128,317 +6128,139 @@ Node3DEditorViewport::~Node3DEditorViewport() { ////////////////////////////////////////////////////////////// -void Node3DEditorViewportContainer::gui_input(const Ref &p_event) { - ERR_FAIL_COND(p_event.is_null()); - - if (_redirect_freelook_input(p_event)) { +void Node3DEditorViewportContainer::_update_split_drag_margin() { + if (view != VIEW_USE_4_VIEWPORTS && view != VIEW_USE_3_VIEWPORTS) { return; } + // Also for 3 viewports view since the first split container is used to remember the offset. + first_split->set_split_offset(second_split->get_split_offset()); - Ref mb = p_event; - - if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT) { - if (mb->is_pressed()) { - Vector2 size = get_size(); - - int h_sep = get_theme_constant(SNAME("separation"), SNAME("HSplitContainer")); - int v_sep = get_theme_constant(SNAME("separation"), SNAME("VSplitContainer")); - - int mid_w = size.width * ratio_h; - int mid_h = size.height * ratio_v; - - dragging_h = mb->get_position().x >= (mid_w - h_sep / 2) && mb->get_position().x < (mid_w + h_sep / 2); - dragging_v = mb->get_position().y >= (mid_h - v_sep / 2) && mb->get_position().y < (mid_h + v_sep / 2); - - drag_begin_pos = mb->get_position(); - drag_begin_ratio.x = ratio_h; - drag_begin_ratio.y = ratio_v; - - switch (view) { - case VIEW_USE_1_VIEWPORT: { - dragging_h = false; - dragging_v = false; - - } break; - case VIEW_USE_2_VIEWPORTS: { - dragging_h = false; - - } break; - case VIEW_USE_2_VIEWPORTS_ALT: { - dragging_v = false; - - } break; - case VIEW_USE_3_VIEWPORTS: - case VIEW_USE_3_VIEWPORTS_ALT: - case VIEW_USE_4_VIEWPORTS: { - // Do nothing. - - } break; - } - } else { - dragging_h = false; - dragging_v = false; - } - } - - Ref mm = p_event; - - if (mm.is_valid()) { - if (view == VIEW_USE_3_VIEWPORTS || view == VIEW_USE_3_VIEWPORTS_ALT || view == VIEW_USE_4_VIEWPORTS) { - Vector2 size = get_size(); - - int h_sep = get_theme_constant(SNAME("separation"), SNAME("HSplitContainer")); - int v_sep = get_theme_constant(SNAME("separation"), SNAME("VSplitContainer")); - - int mid_w = size.width * ratio_h; - int mid_h = size.height * ratio_v; - - bool was_hovering_h = hovering_h; - bool was_hovering_v = hovering_v; - hovering_h = mm->get_position().x >= (mid_w - h_sep / 2) && mm->get_position().x < (mid_w + h_sep / 2); - hovering_v = mm->get_position().y >= (mid_h - v_sep / 2) && mm->get_position().y < (mid_h + v_sep / 2); - - if (was_hovering_h != hovering_h || was_hovering_v != hovering_v) { - queue_redraw(); - } - } - - if (dragging_h) { - real_t new_ratio = drag_begin_ratio.x + (mm->get_position().x - drag_begin_pos.x) / get_size().width; - new_ratio = CLAMP(new_ratio, 40 / get_size().width, (get_size().width - 40) / get_size().width); - ratio_h = new_ratio; - queue_sort(); - queue_redraw(); - } - if (dragging_v) { - real_t new_ratio = drag_begin_ratio.y + (mm->get_position().y - drag_begin_pos.y) / get_size().height; - new_ratio = CLAMP(new_ratio, 40 / get_size().height, (get_size().height - 40) / get_size().height); - ratio_v = new_ratio; - queue_sort(); - queue_redraw(); - } + if (view == VIEW_USE_4_VIEWPORTS) { + // Extend to cover the first split on top. + second_split->set_drag_area_margin_begin(second_split->get_size().y - get_size().y); } } void Node3DEditorViewportContainer::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_MOUSE_ENTER: - case NOTIFICATION_MOUSE_EXIT: { - mouseover = (p_what == NOTIFICATION_MOUSE_ENTER); - queue_redraw(); - } break; - - case NOTIFICATION_DRAW: { - if (mouseover && Input::get_singleton()->get_mouse_mode() != Input::MouseMode::MOUSE_MODE_CAPTURED) { - Ref h_grabber = get_theme_icon(SNAME("grabber"), SNAME("HSplitContainer")); - Ref v_grabber = get_theme_icon(SNAME("grabber"), SNAME("VSplitContainer")); - - Ref hdiag_grabber = get_editor_theme_icon(SNAME("GuiViewportHdiagsplitter")); - Ref vdiag_grabber = get_editor_theme_icon(SNAME("GuiViewportVdiagsplitter")); - Ref vh_grabber = get_editor_theme_icon(SNAME("GuiViewportVhsplitter")); - - Vector2 size = get_size(); - - int h_sep = get_theme_constant(SNAME("separation"), SNAME("HSplitContainer")); - - int v_sep = get_theme_constant(SNAME("separation"), SNAME("VSplitContainer")); - - int mid_w = size.width * ratio_h; - int mid_h = size.height * ratio_v; - - int size_left = mid_w - h_sep / 2; - int size_bottom = size.height - mid_h - v_sep / 2; - - switch (view) { - case VIEW_USE_1_VIEWPORT: { - // Nothing to show. - - } break; - case VIEW_USE_2_VIEWPORTS: { - draw_texture(v_grabber, Vector2((size.width - v_grabber->get_width()) / 2, mid_h - v_grabber->get_height() / 2)); - set_default_cursor_shape(CURSOR_VSPLIT); - - } break; - case VIEW_USE_2_VIEWPORTS_ALT: { - draw_texture(h_grabber, Vector2(mid_w - h_grabber->get_width() / 2, (size.height - h_grabber->get_height()) / 2)); - set_default_cursor_shape(CURSOR_HSPLIT); - - } break; - case VIEW_USE_3_VIEWPORTS: { - if ((hovering_v && hovering_h && !dragging_v && !dragging_h) || (dragging_v && dragging_h)) { - draw_texture(hdiag_grabber, Vector2(mid_w - hdiag_grabber->get_width() / 2, mid_h - v_grabber->get_height() / 4)); - set_default_cursor_shape(CURSOR_DRAG); - } else if ((hovering_v && !dragging_h) || dragging_v) { - draw_texture(v_grabber, Vector2((size.width - v_grabber->get_width()) / 2, mid_h - v_grabber->get_height() / 2)); - set_default_cursor_shape(CURSOR_VSPLIT); - } else if (hovering_h || dragging_h) { - draw_texture(h_grabber, Vector2(mid_w - h_grabber->get_width() / 2, mid_h + v_grabber->get_height() / 2 + (size_bottom - h_grabber->get_height()) / 2)); - set_default_cursor_shape(CURSOR_HSPLIT); - } - - } break; - case VIEW_USE_3_VIEWPORTS_ALT: { - if ((hovering_v && hovering_h && !dragging_v && !dragging_h) || (dragging_v && dragging_h)) { - draw_texture(vdiag_grabber, Vector2(mid_w - vdiag_grabber->get_width() + v_grabber->get_height() / 4, mid_h - vdiag_grabber->get_height() / 2)); - set_default_cursor_shape(CURSOR_DRAG); - } else if ((hovering_v && !dragging_h) || dragging_v) { - draw_texture(v_grabber, Vector2((size_left - v_grabber->get_width()) / 2, mid_h - v_grabber->get_height() / 2)); - set_default_cursor_shape(CURSOR_VSPLIT); - } else if (hovering_h || dragging_h) { - draw_texture(h_grabber, Vector2(mid_w - h_grabber->get_width() / 2, (size.height - h_grabber->get_height()) / 2)); - set_default_cursor_shape(CURSOR_HSPLIT); - } - - } break; - case VIEW_USE_4_VIEWPORTS: { - Vector2 half(mid_w, mid_h); - if ((hovering_v && hovering_h && !dragging_v && !dragging_h) || (dragging_v && dragging_h)) { - draw_texture(vh_grabber, half - vh_grabber->get_size() / 2.0); - set_default_cursor_shape(CURSOR_DRAG); - } else if ((hovering_v && !dragging_h) || dragging_v) { - draw_texture(v_grabber, half - v_grabber->get_size() / 2.0); - set_default_cursor_shape(CURSOR_VSPLIT); - } else if (hovering_h || dragging_h) { - draw_texture(h_grabber, half - h_grabber->get_size() / 2.0); - set_default_cursor_shape(CURSOR_HSPLIT); - } - - } break; - } - } - } break; - - case NOTIFICATION_SORT_CHILDREN: { - Node3DEditorViewport *viewports[4]; - int vc = 0; - for (int i = 0; i < get_child_count(); i++) { - viewports[vc] = Object::cast_to(get_child(i)); - if (viewports[vc]) { - vc++; - } - } - - ERR_FAIL_COND(vc != 4); - - Size2 size = get_size(); - - if (size.x < 10 || size.y < 10) { - for (int i = 0; i < 4; i++) { - viewports[i]->hide(); - } + case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { + if (!EditorSettings::get_singleton()->check_changed_settings_in_group("interface/touchscreen")) { return; } - int h_sep = get_theme_constant(SNAME("separation"), SNAME("HSplitContainer")); - - int v_sep = get_theme_constant(SNAME("separation"), SNAME("VSplitContainer")); - - int mid_w = size.width * ratio_h; - int mid_h = size.height * ratio_v; - - int size_left = mid_w - h_sep / 2; - int size_right = size.width - mid_w - h_sep / 2; - - int size_top = mid_h - v_sep / 2; - int size_bottom = size.height - mid_h - v_sep / 2; - - switch (view) { - case VIEW_USE_1_VIEWPORT: { - viewports[0]->show(); - for (int i = 1; i < 4; i++) { - viewports[i]->hide(); - } - - fit_child_in_rect(viewports[0], Rect2(Vector2(), size)); - - } break; - case VIEW_USE_2_VIEWPORTS: { - for (int i = 0; i < 4; i++) { - if (i == 1 || i == 3) { - viewports[i]->hide(); - } else { - viewports[i]->show(); - } - } - - fit_child_in_rect(viewports[0], Rect2(Vector2(), Vector2(size.width, size_top))); - fit_child_in_rect(viewports[2], Rect2(Vector2(0, mid_h + v_sep / 2), Vector2(size.width, size_bottom))); - - } break; - case VIEW_USE_2_VIEWPORTS_ALT: { - for (int i = 0; i < 4; i++) { - if (i == 1 || i == 3) { - viewports[i]->hide(); - } else { - viewports[i]->show(); - } - } - fit_child_in_rect(viewports[0], Rect2(Vector2(), Vector2(size_left, size.height))); - fit_child_in_rect(viewports[2], Rect2(Vector2(mid_w + h_sep / 2, 0), Vector2(size_right, size.height))); - - } break; - case VIEW_USE_3_VIEWPORTS: { - for (int i = 0; i < 4; i++) { - if (i == 1) { - viewports[i]->hide(); - } else { - viewports[i]->show(); - } - } - - fit_child_in_rect(viewports[0], Rect2(Vector2(), Vector2(size.width, size_top))); - fit_child_in_rect(viewports[2], Rect2(Vector2(0, mid_h + v_sep / 2), Vector2(size_left, size_bottom))); - fit_child_in_rect(viewports[3], Rect2(Vector2(mid_w + h_sep / 2, mid_h + v_sep / 2), Vector2(size_right, size_bottom))); - - } break; - case VIEW_USE_3_VIEWPORTS_ALT: { - for (int i = 0; i < 4; i++) { - if (i == 1) { - viewports[i]->hide(); - } else { - viewports[i]->show(); - } - } - - fit_child_in_rect(viewports[0], Rect2(Vector2(), Vector2(size_left, size_top))); - fit_child_in_rect(viewports[2], Rect2(Vector2(0, mid_h + v_sep / 2), Vector2(size_left, size_bottom))); - fit_child_in_rect(viewports[3], Rect2(Vector2(mid_w + h_sep / 2, 0), Vector2(size_right, size.height))); - - } break; - case VIEW_USE_4_VIEWPORTS: { - for (int i = 0; i < 4; i++) { - viewports[i]->show(); - } - - fit_child_in_rect(viewports[0], Rect2(Vector2(), Vector2(size_left, size_top))); - fit_child_in_rect(viewports[1], Rect2(Vector2(mid_w + h_sep / 2, 0), Vector2(size_right, size_top))); - fit_child_in_rect(viewports[2], Rect2(Vector2(0, mid_h + v_sep / 2), Vector2(size_left, size_bottom))); - fit_child_in_rect(viewports[3], Rect2(Vector2(mid_w + h_sep / 2, mid_h + v_sep / 2), Vector2(size_right, size_bottom))); - - } break; - } + [[fallthrough]]; + } + case NOTIFICATION_READY: { + bool touch_optimizations = EDITOR_GET("interface/touchscreen/enable_touch_optimizations"); + main_split->set_touch_dragger_enabled(touch_optimizations); + first_split->set_touch_dragger_enabled(touch_optimizations); + second_split->set_touch_dragger_enabled(touch_optimizations); } break; } } void Node3DEditorViewportContainer::set_view(View p_view) { view = p_view; - queue_sort(); + + Node3DEditorViewport *viewports[4]; + for (uint32_t i = 0; i < 4; i++) { + viewports[i] = Node3DEditor::get_singleton()->get_editor_viewport(i); + ERR_FAIL_NULL(viewports[i]); + } + + const bool previous_main_vertical = !first_split->is_vertical(); + const float horizontal_offset = previous_main_vertical ? first_split->get_split_offset() : main_split->get_split_offset(); + const float vertical_offset = previous_main_vertical ? main_split->get_split_offset() : first_split->get_split_offset(); + + first_split->set_dragging_enabled(true); + second_split->set_drag_area_margin_begin(0); + viewports[0]->show(); + + switch (view) { + case VIEW_USE_1_VIEWPORT: { + for (int i = 1; i < 4; i++) { + viewports[i]->hide(); + } + second_split->hide(); + } break; + case VIEW_USE_2_VIEWPORTS: + case VIEW_USE_2_VIEWPORTS_ALT: { + viewports[1]->show(); + viewports[2]->hide(); + viewports[3]->hide(); + second_split->hide(); + const bool is_vertical = view == VIEW_USE_2_VIEWPORTS; + if (first_split->is_vertical() != is_vertical) { + first_split->set_vertical(is_vertical); + first_split->set_split_offset(is_vertical ? vertical_offset : horizontal_offset); + main_split->set_split_offset(is_vertical ? horizontal_offset : vertical_offset); // Store the other offset here for later. + } + } break; + case VIEW_USE_3_VIEWPORTS: + case VIEW_USE_3_VIEWPORTS_ALT: { + // Default mode has two on bottom (second_split). Alt mode has two on the left (first_split). + const bool main_vertical = view == VIEW_USE_3_VIEWPORTS; + viewports[1]->set_visible(!main_vertical); + viewports[2]->show(); + viewports[3]->set_visible(main_vertical); + second_split->show(); + main_split->set_vertical(main_vertical); + main_split->set_split_offset(main_vertical ? vertical_offset : horizontal_offset); + first_split->set_vertical(!main_vertical); + first_split->set_split_offset(main_vertical ? horizontal_offset : vertical_offset); + second_split->set_split_offset(main_vertical ? horizontal_offset : vertical_offset); + } break; + case VIEW_USE_4_VIEWPORTS: { + for (int i = 1; i < 4; i++) { + viewports[i]->show(); + } + second_split->show(); + main_split->set_vertical(true); + main_split->set_split_offset(vertical_offset); + first_split->set_vertical(false); + first_split->set_split_offset(horizontal_offset); + second_split->set_split_offset(horizontal_offset); + + first_split->set_dragging_enabled(false); + _update_split_drag_margin(); + } break; + } } Node3DEditorViewportContainer::View Node3DEditorViewportContainer::get_view() { return view; } +void Node3DEditorViewportContainer::add_viewport(Node3DEditorViewport *p_viewport, int p_index) { + if (p_index <= 1) { + first_split->add_child(p_viewport); + } else { + second_split->add_child(p_viewport); + if (p_index == 3) { + // Connect to the second split's child to update the drag margin immediately whenever the split offset changes. + p_viewport->connect(SceneStringName(resized), callable_mp(this, &Node3DEditorViewportContainer::_update_split_drag_margin)); + } + } +} + Node3DEditorViewportContainer::Node3DEditorViewportContainer() { set_clip_contents(true); - view = VIEW_USE_1_VIEWPORT; - mouseover = false; - ratio_h = 0.5; - ratio_v = 0.5; - hovering_v = false; - hovering_h = false; - dragging_v = false; - dragging_h = false; + + main_split = memnew(SplitContainer); + main_split->set_drag_nested_intersections(true); + + first_split = memnew(SplitContainer); + first_split->set_h_size_flags(SIZE_EXPAND_FILL); + first_split->set_v_size_flags(SIZE_EXPAND_FILL); + first_split->set_drag_nested_intersections(true); + + second_split = memnew(SplitContainer); + second_split->set_h_size_flags(SIZE_EXPAND_FILL); + second_split->set_v_size_flags(SIZE_EXPAND_FILL); + second_split->set_drag_nested_intersections(true); + + main_split->add_child(first_split); + main_split->add_child(second_split); + add_child(main_split); } /////////////////////////////////////////////////////////////////// @@ -9752,7 +9574,10 @@ Node3DEditor::Node3DEditor() { viewports[i]->connect("toggle_maximize_view", callable_mp(this, &Node3DEditor::_toggle_maximize_view)); viewports[i]->connect("clicked", callable_mp(this, &Node3DEditor::_viewport_clicked).bind(i)); viewports[i]->assign_pending_data_pointers(preview_node, &preview_bounds, accept); - viewport_base->add_child(viewports[i]); + viewports[i]->set_h_size_flags(SIZE_EXPAND_FILL); + viewports[i]->set_v_size_flags(SIZE_EXPAND_FILL); + viewports[i]->set_custom_minimum_size(Size2(39, 39)); + viewport_base->add_viewport(viewports[i], i); } /* SNAP DIALOG */ diff --git a/editor/scene/3d/node_3d_editor_plugin.h b/editor/scene/3d/node_3d_editor_plugin.h index e07314b7d3..ed69218509 100644 --- a/editor/scene/3d/node_3d_editor_plugin.h +++ b/editor/scene/3d/node_3d_editor_plugin.h @@ -37,6 +37,7 @@ #include "scene/debugger/view_3d_controller.h" #include "scene/gui/box_container.h" #include "scene/gui/button.h" +#include "scene/gui/margin_container.h" #include "scene/gui/spin_box.h" #include "scene/resources/gradient.h" #include "scene/resources/immediate_mesh.h" @@ -58,6 +59,7 @@ class OptionButton; class PanelContainer; class ProceduralSkyMaterial; class RichTextLabel; +class SplitContainer; class SubViewport; class SubViewportContainer; class VSeparator; @@ -519,8 +521,8 @@ public: ~Node3DEditorSelectedItem(); }; -class Node3DEditorViewportContainer : public Container { - GDCLASS(Node3DEditorViewportContainer, Container); +class Node3DEditorViewportContainer : public MarginContainer { + GDCLASS(Node3DEditorViewportContainer, MarginContainer); public: enum View { @@ -533,20 +535,12 @@ public: }; private: - View view; - bool mouseover; - real_t ratio_h; - real_t ratio_v; + View view = VIEW_USE_1_VIEWPORT; + SplitContainer *main_split = nullptr; + SplitContainer *first_split = nullptr; + SplitContainer *second_split = nullptr; - bool hovering_v; - bool hovering_h; - - bool dragging_v; - bool dragging_h; - Vector2 drag_begin_pos; - Vector2 drag_begin_ratio; - - virtual void gui_input(const Ref &p_event) override; + void _update_split_drag_margin(); protected: void _notification(int p_what); @@ -555,6 +549,8 @@ public: void set_view(View p_view); View get_view(); + void add_viewport(Node3DEditorViewport *p_viewport, int p_index); + Node3DEditorViewportContainer(); }; diff --git a/scene/gui/split_container.cpp b/scene/gui/split_container.cpp index 2d3e7665cf..6feee52efc 100644 --- a/scene/gui/split_container.cpp +++ b/scene/gui/split_container.cpp @@ -302,6 +302,7 @@ void SplitContainerDragger::update_touch_dragger() { touch_dragger->set_texture(sc->_get_touch_dragger_icon()); touch_dragger->set_anchors_and_offsets_preset(Control::PRESET_CENTER); touch_dragger->set_default_cursor_shape(sc->vertical ? CURSOR_VSPLIT : CURSOR_HSPLIT); + touch_dragger->set_visible(sc->dragging_enabled); } void SplitContainerDragger::stop_dragging() { @@ -850,9 +851,6 @@ void SplitContainer::_resort() { } for (SplitContainerDragger *dragger : dragging_area_controls) { dragger->set_visible(!collapsed); - if (touch_dragger_enabled) { - dragger->touch_dragger->set_visible(dragging_enabled); - } } _update_default_dragger_positions(); @@ -1483,6 +1481,11 @@ void SplitContainer::set_dragging_enabled(bool p_enabled) { dragger->stop_dragging(); } } + if (touch_dragger_enabled) { + for (SplitContainerDragger *dragger : dragging_area_controls) { + dragger->update_touch_dragger(); + } + } if (get_viewport()) { get_viewport()->update_mouse_cursor_state(); }