diff --git a/doc/classes/SplitContainer.xml b/doc/classes/SplitContainer.xml
index 8f243abb97..485ffce01b 100644
--- a/doc/classes/SplitContainer.xml
+++ b/doc/classes/SplitContainer.xml
@@ -56,6 +56,9 @@
Shifts the drag area in the axis of the container to prevent the drag area from overlapping the [ScrollBar] or other selectable [Control] of a child node.
+
+ Adds extra draggers at the intersection of the draggers of two SplitContainers to allow dragging both at once. This must be set to [code]true[/code] for both SplitContainers, and one needs to be a descendant of the other. They also must be orthogonal (their [member vertical] are different) and the descendant must be next to at least one of the ancestor's draggers (within [theme_item minimum_grab_thickness]).
+
Determines the dragger's visibility. This property does not determine whether dragging is enabled or not. Use [member dragging_enabled] for that.
diff --git a/editor/docks/editor_dock_manager.cpp b/editor/docks/editor_dock_manager.cpp
index a346d305ba..76343eb8d7 100644
--- a/editor/docks/editor_dock_manager.cpp
+++ b/editor/docks/editor_dock_manager.cpp
@@ -116,6 +116,7 @@ DockSplitContainer::DockSplitContainer() {
if (EDITOR_GET("interface/touchscreen/enable_touch_optimizations")) {
callable_mp((SplitContainer *)this, &SplitContainer::set_touch_dragger_enabled).call_deferred(true);
}
+ set_drag_nested_intersections(true);
}
////////////////////////////////////////////////
diff --git a/editor/script/script_editor_plugin.cpp b/editor/script/script_editor_plugin.cpp
index eb03a12d71..b64240c677 100644
--- a/editor/script/script_editor_plugin.cpp
+++ b/editor/script/script_editor_plugin.cpp
@@ -3804,6 +3804,7 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) {
script_split = memnew(HSplitContainer);
main_container->add_child(script_split);
script_split->set_v_size_flags(SIZE_EXPAND_FILL);
+ script_split->set_drag_nested_intersections(true);
#ifdef ANDROID_ENABLED
virtual_keyboard_spacer = memnew(Control);
@@ -3814,6 +3815,7 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) {
list_split = memnew(VSplitContainer);
script_split->add_child(list_split);
list_split->set_v_size_flags(SIZE_EXPAND_FILL);
+ list_split->set_drag_nested_intersections(true);
scripts_vbox = memnew(VBoxContainer);
scripts_vbox->set_v_size_flags(SIZE_EXPAND_FILL);
diff --git a/editor/shader/shader_editor_plugin.cpp b/editor/shader/shader_editor_plugin.cpp
index 2654434cf7..cc72f9708e 100644
--- a/editor/shader/shader_editor_plugin.cpp
+++ b/editor/shader/shader_editor_plugin.cpp
@@ -880,6 +880,7 @@ ShaderEditorPlugin::ShaderEditorPlugin() {
files_split = memnew(HSplitContainer);
files_split->set_split_offset(200 * EDSCALE);
files_split->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ files_split->set_drag_nested_intersections(true);
shader_dock->add_child(files_split);
context_menu = memnew(PopupMenu);
diff --git a/scene/gui/split_container.cpp b/scene/gui/split_container.cpp
index 98da7f03fe..2d3e7665cf 100644
--- a/scene/gui/split_container.cpp
+++ b/scene/gui/split_container.cpp
@@ -32,6 +32,7 @@
#include "split_container.compat.inc"
#include "core/config/engine.h"
+#include "core/input/input.h"
#include "core/object/callable_mp.h"
#include "core/object/class_db.h"
#include "scene/gui/texture_rect.h"
@@ -39,6 +40,111 @@
#include "scene/theme/theme_db.h"
#include "servers/display/accessibility_server.h"
+void SplitContainerMultiDragger::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_MOUSE_ENTER: {
+ if (!dragging && !Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT)) {
+ split_container->show_grabber_icon(dragger_index);
+ }
+ } break;
+
+ case NOTIFICATION_MOUSE_EXIT: {
+ if (!dragging && !Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT)) {
+ split_container->show_grabber_icon(-1);
+ }
+ } break;
+ }
+}
+
+void SplitContainerMultiDragger::gui_input(const Ref &p_event) {
+ SplitContainer *parent_sc = Object::cast_to(get_parent()->get_parent());
+ ERR_FAIL_NULL(parent_sc);
+
+ if (parent_sc->collapsed || parent_sc->valid_children.size() < 2u || !parent_sc->dragging_enabled) {
+ return;
+ }
+ if (split_container->collapsed || split_container->valid_children.size() < 2u || !split_container->dragging_enabled || !split_container->is_visible_in_tree()) {
+ return;
+ }
+
+ Ref mb = p_event;
+
+ if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT) {
+ if (mb->is_pressed()) {
+ // To match the visual position, clamp on the first split.
+ split_container->_update_dragger_positions(0);
+ dragging = true;
+ split_container->show_grabber_icon(dragger_index);
+ split_container->emit_signal(SNAME("drag_started"));
+ const int axis = split_container->vertical ? 1 : 0;
+ start_drag_split_offset = split_container->get_split_offset(dragger_index);
+ drag_from = (int)get_transform().xform(mb->get_position())[axis];
+ } else {
+ stop_dragging();
+ }
+ // Don't accept to allow the event to go to the parent dragger.
+ }
+
+ Ref mm = p_event;
+
+ if (mm.is_valid()) {
+ if (!dragging) {
+ return;
+ }
+
+ Vector2i in_parent_pos = get_transform().xform(mm->get_position());
+ int new_drag_offset;
+ if (!split_container->vertical && is_layout_rtl()) {
+ new_drag_offset = start_drag_split_offset - (in_parent_pos.x - drag_from);
+ } else {
+ new_drag_offset = start_drag_split_offset + ((split_container->vertical ? in_parent_pos.y : in_parent_pos.x) - drag_from);
+ }
+ split_container->set_split_offset(new_drag_offset, dragger_index);
+ split_container->_update_dragger_positions(dragger_index);
+ split_container->queue_sort();
+ split_container->emit_signal(SNAME("dragged"), split_container->get_split_offset(dragger_index));
+ }
+}
+
+Control::CursorShape SplitContainerMultiDragger::get_cursor_shape(const Point2 &p_pos) const {
+ SplitContainer *parent_sc = Object::cast_to(get_parent()->get_parent());
+ ERR_FAIL_NULL_V(parent_sc, CURSOR_ARROW);
+ if (dragging || (!parent_sc->collapsed && parent_sc->dragging_enabled)) {
+ return CURSOR_DRAG;
+ }
+ return Control::get_cursor_shape(p_pos);
+}
+
+void SplitContainerMultiDragger::update_position() {
+ if (dragging || is_queued_for_deletion()) {
+ return;
+ }
+ SplitContainer *parent_sc = Object::cast_to(get_parent()->get_parent());
+ ERR_FAIL_NULL(parent_sc);
+ ERR_FAIL_INDEX(dragger_index, (int)split_container->dragging_area_controls.size());
+ const int axis = parent_sc->vertical ? 1 : 0;
+ const int cross_axis = parent_sc->vertical ? 0 : 1;
+ Control *control = split_container->dragging_area_controls[dragger_index];
+ Point2 pos;
+ pos[axis] = get_parent_control()->get_global_position()[axis];
+ pos[cross_axis] = control->get_global_position()[cross_axis];
+ set_global_position(pos);
+}
+
+void SplitContainerMultiDragger::stop_dragging() {
+ if (!dragging) {
+ return;
+ }
+ dragging = false;
+ split_container->show_grabber_icon(-1);
+ update_position();
+ split_container->emit_signal(SNAME("drag_ended"));
+}
+
+SplitContainerMultiDragger::SplitContainerMultiDragger() {
+ set_mouse_filter(MOUSE_FILTER_PASS);
+}
+
void SplitContainerDragger::gui_input(const Ref &p_event) {
ERR_FAIL_COND(p_event.is_null());
@@ -77,9 +183,7 @@ void SplitContainerDragger::gui_input(const Ref &p_event) {
drag_from = (int)get_transform().xform(mb->get_position()).x;
}
} else {
- dragging = false;
- queue_redraw();
- sc->emit_signal(SNAME("drag_ended"));
+ stop_dragging();
}
accept_event();
}
@@ -200,6 +304,23 @@ void SplitContainerDragger::update_touch_dragger() {
touch_dragger->set_default_cursor_shape(sc->vertical ? CURSOR_VSPLIT : CURSOR_HSPLIT);
}
+void SplitContainerDragger::stop_dragging() {
+ ERR_THREAD_GUARD;
+ if (!dragging) {
+ return;
+ }
+ dragging = false;
+ queue_redraw();
+ SplitContainer *sc = Object::cast_to(get_parent());
+ sc->emit_signal(SNAME("drag_ended"));
+ for (Node *child : iterate_children()) {
+ SplitContainerMultiDragger *dragger = Object::cast_to(child);
+ if (dragger) {
+ dragger->stop_dragging();
+ }
+ }
+}
+
void SplitContainerDragger::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
@@ -249,22 +370,19 @@ void SplitContainerDragger::_notification(int p_what) {
} break;
case NOTIFICATION_FOCUS_EXIT: {
- if (dragging) {
- dragging = false;
- queue_redraw();
- }
+ stop_dragging();
} break;
case NOTIFICATION_VISIBILITY_CHANGED: {
if (dragging && !is_visible_in_tree()) {
- dragging = false;
+ stop_dragging();
}
} break;
case NOTIFICATION_DRAW: {
SplitContainer *sc = Object::cast_to(get_parent());
draw_style_box(sc->theme_cache.split_bar_background, split_bar_rect);
- if (sc->dragger_visibility == SplitContainer::DRAGGER_VISIBLE && (dragging || mouse_inside || !sc->theme_cache.autohide) && !sc->touch_dragger_enabled) {
+ if (!sc->touch_dragger_enabled && sc->dragger_visibility == SplitContainer::DRAGGER_VISIBLE && (dragging || mouse_inside || !sc->theme_cache.autohide || sc->force_show_grabber_icon == dragger_index)) {
Ref tex = sc->_get_grabber_icon();
float available_size = sc->vertical ? (sc->get_size().x - tex->get_size().x) : (sc->get_size().y - tex->get_size().y);
if (available_size - sc->drag_area_margin_begin - sc->drag_area_margin_end > 0) { // Draw the grabber only if it fits.
@@ -724,6 +842,10 @@ void SplitContainer::_resort() {
for (SplitContainerDragger *dragger : dragging_area_controls) {
dragger->hide();
}
+ if (drag_nested_intersections) {
+ // Ancestors may need to remove multi draggers.
+ _update_nested_ancestors();
+ }
return;
}
for (SplitContainerDragger *dragger : dragging_area_controls) {
@@ -783,9 +905,23 @@ void SplitContainer::_resort() {
dragging_area_controls[i]->queue_redraw();
}
queue_redraw();
+
+ if (drag_nested_intersections) {
+ _update_nested_ancestors();
+ // Update all multi dragger positions in case size changed.
+ for (SplitContainerDragger *dragger : dragging_area_controls) {
+ for (Node *child : dragger->iterate_children()) {
+ SplitContainerMultiDragger *multi_dragger = Object::cast_to(child);
+ if (multi_dragger) {
+ multi_dragger->update_position();
+ }
+ }
+ }
+ }
}
void SplitContainer::_update_draggers() {
+ ERR_THREAD_GUARD;
if (!is_visible_in_tree()) {
return;
}
@@ -793,6 +929,24 @@ void SplitContainer::_update_draggers() {
const int dragger_count = MAX(valid_child_count - 1, 1);
const int draggers_size_diff = dragger_count - (int)dragging_area_controls.size();
+ if (draggers_size_diff != 0) {
+ LocalVector to_update;
+ for (SplitContainerDragger *dragger : dragging_area_controls) {
+ for (Node *child : dragger->iterate_children()) {
+ SplitContainerMultiDragger *multi_dragger = Object::cast_to(child);
+ if (multi_dragger == nullptr || multi_dragger->is_queued_for_deletion()) {
+ continue;
+ }
+ to_update.push_back(multi_dragger->split_container);
+ }
+ }
+ _remove_nested_descendent(nullptr);
+ // Existing descendents may not resort, so need to update them after draggers update.
+ for (SplitContainer *to_update_sc : to_update) {
+ callable_mp(to_update_sc, &SplitContainer::_update_nested_ancestors).call_deferred(false);
+ }
+ }
+
// Add new draggers.
for (int i = 0; i < draggers_size_diff; i++) {
SplitContainerDragger *dragger = memnew(SplitContainerDragger);
@@ -807,6 +961,7 @@ void SplitContainer::_update_draggers() {
for (int i = 0; i < -draggers_size_diff; i++) {
const int remove_at = (int)dragging_area_controls.size() - 1;
SplitContainerDragger *dragger = dragging_area_controls[remove_at];
+ dragger->stop_dragging();
dragging_area_controls.remove_at(remove_at);
// replace_by removes all children, so make sure it is a child before removing.
if (dragger->get_parent() == this) {
@@ -862,9 +1017,17 @@ void SplitContainer::_notification(int p_what) {
case NOTIFICATION_THEME_CHANGED: {
update_minimum_size();
} break;
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+ if (!is_visible_in_tree()) {
+ _update_nested_ancestors();
+ }
+ } break;
case NOTIFICATION_PREDELETE: {
valid_children.clear();
dragging_area_controls.clear();
+ if (drag_nested_intersections) {
+ _remove_nested_descendent(nullptr);
+ }
} break;
}
}
@@ -1057,6 +1220,178 @@ void SplitContainer::_remove_valid_child(Control *p_control) {
_set_desired_sizes(desired_sizes);
}
+void SplitContainer::_remove_nested_descendent(SplitContainer *p_nested_sc) {
+ for (SplitContainerDragger *dragger : dragging_area_controls) {
+ for (int i = dragger->get_child_count() - 1; i >= 0; i--) {
+ SplitContainerMultiDragger *multi_dragger = Object::cast_to(dragger->get_child(i));
+ if (multi_dragger == nullptr || multi_dragger->is_queued_for_deletion()) {
+ continue;
+ }
+ if (p_nested_sc != nullptr && multi_dragger->split_container != p_nested_sc) {
+ continue;
+ }
+ dragger->stop_dragging();
+
+ SplitContainer *nested_sc = multi_dragger->split_container;
+ if (nested_sc->is_connected(SceneStringName(tree_exiting), callable_mp(this, &SplitContainer::_remove_nested_descendent))) {
+ nested_sc->disconnect(SceneStringName(tree_exiting), callable_mp(this, &SplitContainer::_remove_nested_descendent).bind(nested_sc));
+ }
+ multi_dragger->queue_free();
+ }
+ }
+}
+
+void SplitContainer::_update_nested_descendent(SplitContainer *p_nested_sc, Control *p_direct_child) {
+ ERR_THREAD_GUARD;
+ // Remove multi draggers if the descendent cannot drag or is not orthogonal. If any of these change, the descendent will call this update again.
+ if (!p_nested_sc->is_dragging_enabled() || !p_nested_sc->is_dragging_nested_intersections() || p_nested_sc->is_collapsed() || !p_nested_sc->is_visible_in_tree() || p_nested_sc->is_vertical() == vertical || p_nested_sc->valid_children.size() < 2u) {
+ _remove_nested_descendent(p_nested_sc);
+ return;
+ }
+ if (dragging_area_controls.size() != valid_children.size() - 1) {
+ _update_draggers();
+ }
+
+ // Make sure the nested SplitContainer is adjacent.
+ const int child_index = valid_children.find(p_direct_child);
+ if (child_index < 0) {
+ return;
+ }
+ const int axis = vertical ? 1 : 0;
+ const float max_delta_pos = theme_cache.minimum_grab_thickness;
+
+ const float start_pos = p_direct_child->get_global_rect().get_position()[axis];
+ const float end_pos = p_direct_child->get_global_rect().get_end()[axis];
+ const float child_start_pos = p_nested_sc->get_global_rect().get_position()[axis];
+ const float child_end_pos = p_nested_sc->get_global_rect().get_end()[axis];
+ bool nested_to_start = Math::abs(start_pos - child_start_pos) <= max_delta_pos;
+ bool nested_to_end = Math::abs(end_pos - child_end_pos) <= max_delta_pos;
+
+ if (is_layout_rtl() && !is_vertical()) {
+ SWAP(nested_to_start, nested_to_end);
+ }
+
+ LocalVector connected_draggers;
+ if (nested_to_start && child_index > 0) {
+ connected_draggers.push_back(dragging_area_controls[child_index - 1]);
+ }
+ if (nested_to_end && child_index < (int)dragging_area_controls.size()) {
+ connected_draggers.push_back(dragging_area_controls[child_index]);
+ }
+
+ if (connected_draggers.is_empty()) {
+ _remove_nested_descendent(p_nested_sc);
+ return;
+ }
+
+ bool needs_rebuild = false;
+ for (SplitContainerDragger *dragger : connected_draggers) {
+ int needed = (int)p_nested_sc->valid_children.size() - 1;
+ for (Node *child : dragger->iterate_children()) {
+ SplitContainerMultiDragger *multi_dragger = Object::cast_to(child);
+ if (multi_dragger == nullptr || multi_dragger->is_queued_for_deletion()) {
+ continue;
+ }
+ if (multi_dragger->split_container != p_nested_sc) {
+ continue;
+ }
+ needed--;
+ if (needed < 0) {
+ break;
+ }
+ }
+ if (needed != 0) {
+ needs_rebuild = true;
+ break;
+ }
+ }
+ if (!needs_rebuild) {
+ // Only update the positions.
+ for (SplitContainerDragger *dragger : dragging_area_controls) {
+ for (Node *child : dragger->iterate_children()) {
+ SplitContainerMultiDragger *multi_dragger = Object::cast_to(child);
+ if (multi_dragger) {
+ multi_dragger->update_position();
+ }
+ }
+ }
+ return;
+ }
+
+ _remove_nested_descendent(p_nested_sc);
+
+ // Add new multi draggers.
+ const int sep = _get_separation();
+ const int drag_thickness = MAX(sep, theme_cache.minimum_grab_thickness);
+
+ for (SplitContainerDragger *dragger : connected_draggers) {
+ for (int i = 0; i < (int)p_nested_sc->dragging_area_controls.size(); i++) {
+ SplitContainerMultiDragger *new_dragger = memnew(SplitContainerMultiDragger);
+ new_dragger->dragger_index = i;
+ new_dragger->split_container = p_nested_sc;
+ new_dragger->set_size(Size2(drag_thickness, drag_thickness));
+ dragger->add_child(new_dragger, false, INTERNAL_MODE_FRONT);
+
+ if (!p_nested_sc->is_connected(SceneStringName(tree_exiting), callable_mp(this, &SplitContainer::_remove_nested_descendent))) {
+ p_nested_sc->connect(SceneStringName(tree_exiting), callable_mp(this, &SplitContainer::_remove_nested_descendent).bind(p_nested_sc));
+ }
+ callable_mp(new_dragger, &SplitContainerMultiDragger::update_position).call_deferred();
+ }
+ }
+}
+
+void SplitContainer::_update_nested_ancestors(bool p_remove) {
+ if (!is_inside_tree()) {
+ return;
+ }
+ if (!p_remove && !drag_nested_intersections) {
+ return;
+ }
+ Control *last_ancestor = this;
+ Control *ancestor = get_parent_control();
+ while (ancestor) {
+ SplitContainer *ancestor_split_container = Object::cast_to(ancestor);
+ if (ancestor_split_container && ancestor_split_container->is_dragging_nested_intersections()) {
+ if (p_remove) {
+ ancestor_split_container->_remove_nested_descendent(this);
+ } else {
+ ancestor_split_container->_update_nested_descendent(this, last_ancestor);
+ }
+ }
+ if (ancestor->is_part_of_edited_scene() != is_part_of_edited_scene()) {
+ break;
+ }
+
+ if (ancestor->is_set_as_top_level()) {
+ break;
+ }
+ last_ancestor = ancestor;
+ ancestor = ancestor->get_parent_control();
+ }
+}
+
+void SplitContainer::_update_all_nested_descendents(Control *p_control, Control *p_first_child) {
+ ERR_THREAD_GUARD;
+ for (Node *child_node : p_control->iterate_children()) {
+ Control *child_control = Object::cast_to(child_node);
+ if (!child_control || child_control->is_set_as_top_level()) {
+ break;
+ }
+ if (child_control->is_part_of_edited_scene() != is_part_of_edited_scene()) {
+ break;
+ }
+ if (p_first_child == nullptr) {
+ p_first_child = child_control;
+ }
+
+ SplitContainer *control_sc = Object::cast_to(child_control);
+ if (control_sc) {
+ _update_nested_descendent(control_sc, p_first_child);
+ }
+ _update_all_nested_descendents(child_control, p_first_child);
+ }
+}
+
void SplitContainer::set_split_offset(int p_offset, int p_index) {
ERR_FAIL_INDEX(p_index, split_offsets.size());
if (split_offsets[p_index] == p_offset) {
@@ -1144,13 +1479,8 @@ void SplitContainer::set_dragging_enabled(bool p_enabled) {
}
dragging_enabled = p_enabled;
if (!dragging_enabled) {
- bool was_dragging = false;
for (SplitContainerDragger *dragger : dragging_area_controls) {
- was_dragging |= dragger->dragging;
- dragger->dragging = false;
- }
- if (was_dragging) {
- emit_signal(SNAME("drag_ended"));
+ dragger->stop_dragging();
}
}
if (get_viewport()) {
@@ -1257,6 +1587,36 @@ bool SplitContainer::is_touch_dragger_enabled() const {
return touch_dragger_enabled;
}
+void SplitContainer::show_grabber_icon(int p_index) {
+ if (force_show_grabber_icon == p_index) {
+ return;
+ }
+ force_show_grabber_icon = p_index;
+ for (SplitContainerDragger *dragger : dragging_area_controls) {
+ dragger->queue_redraw();
+ }
+}
+
+void SplitContainer::set_drag_nested_intersections(bool p_enabled) {
+ if (drag_nested_intersections == p_enabled) {
+ return;
+ }
+ drag_nested_intersections = p_enabled;
+ if (!is_inside_tree()) {
+ return;
+ }
+ _update_nested_ancestors(!drag_nested_intersections);
+ if (drag_nested_intersections) {
+ _update_all_nested_descendents(this);
+ } else {
+ _remove_nested_descendent(nullptr);
+ }
+}
+
+bool SplitContainer::is_dragging_nested_intersections() const {
+ return drag_nested_intersections;
+}
+
void SplitContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_split_offsets", "offsets"), &SplitContainer::set_split_offsets);
ClassDB::bind_method(D_METHOD("get_split_offsets"), &SplitContainer::get_split_offsets);
@@ -1292,6 +1652,9 @@ void SplitContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_touch_dragger_enabled", "enabled"), &SplitContainer::set_touch_dragger_enabled);
ClassDB::bind_method(D_METHOD("is_touch_dragger_enabled"), &SplitContainer::is_touch_dragger_enabled);
+ ClassDB::bind_method(D_METHOD("set_drag_nested_intersections", "enabled"), &SplitContainer::set_drag_nested_intersections);
+ ClassDB::bind_method(D_METHOD("is_dragging_nested_intersections"), &SplitContainer::is_dragging_nested_intersections);
+
ADD_SIGNAL(MethodInfo("dragged", PropertyInfo(Variant::INT, "offset")));
ADD_SIGNAL(MethodInfo("drag_started"));
ADD_SIGNAL(MethodInfo("drag_ended"));
@@ -1302,6 +1665,7 @@ void SplitContainer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "dragger_visibility", PROPERTY_HINT_ENUM, "Visible,Hidden,Hidden and Collapsed"), "set_dragger_visibility", "get_dragger_visibility");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "touch_dragger_enabled"), "set_touch_dragger_enabled", "is_touch_dragger_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_nested_intersections"), "set_drag_nested_intersections", "is_dragging_nested_intersections");
ADD_GROUP("Drag Area", "drag_area_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "drag_area_margin_begin", PROPERTY_HINT_NONE, "suffix:px"), "set_drag_area_margin_begin", "get_drag_area_margin_begin");
diff --git a/scene/gui/split_container.h b/scene/gui/split_container.h
index 72ead3bc1f..7f774dda7b 100644
--- a/scene/gui/split_container.h
+++ b/scene/gui/split_container.h
@@ -33,11 +33,40 @@
#include "scene/gui/container.h"
class TextureRect;
+class SplitContainer;
+
+class SplitContainerMultiDragger : public Control {
+ GDCLASS(SplitContainerMultiDragger, Control);
+
+ bool dragging = false;
+ int drag_from = 0;
+ int start_drag_split_offset = 0;
+
+protected:
+ void _notification(int p_what);
+ virtual void gui_input(const Ref &p_event) override;
+
+public:
+ int dragger_index = -1;
+ SplitContainer *split_container = nullptr;
+
+ virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override;
+
+ void update_position();
+ void stop_dragging();
+
+ SplitContainerMultiDragger();
+};
class SplitContainerDragger : public Control {
GDCLASS(SplitContainerDragger, Control);
friend class SplitContainer;
+ bool dragging = false;
+ int drag_from = 0;
+ int start_drag_split_offset = 0;
+ bool mouse_inside = false;
+
Rect2 split_bar_rect;
TextureRect *touch_dragger = nullptr;
@@ -52,12 +81,6 @@ protected:
void _accessibility_action_dec(const Variant &p_data);
void _accessibility_action_set_value(const Variant &p_data);
-private:
- bool dragging = false;
- int drag_from = 0;
- int start_drag_split_offset = 0;
- bool mouse_inside = false;
-
public:
int dragger_index = -1;
@@ -65,7 +88,7 @@ public:
void set_touch_dragger_enabled(bool p_enabled);
void update_touch_dragger();
- bool is_touch_dragger_enabled() const;
+ void stop_dragging();
SplitContainerDragger();
};
@@ -73,6 +96,7 @@ public:
class SplitContainer : public Container {
GDCLASS(SplitContainer, Container);
friend class SplitContainerDragger;
+ friend class SplitContainerMultiDragger;
public:
enum DraggerVisibility {
@@ -102,6 +126,8 @@ private:
bool initialized = false;
bool touch_dragger_enabled = false;
+ bool drag_nested_intersections = false;
+ int force_show_grabber_icon = -1;
struct ThemeCache {
Color touch_dragger_color;
@@ -136,6 +162,11 @@ private:
void _add_valid_child(Control *p_control);
void _remove_valid_child(Control *p_control);
+ void _remove_nested_descendent(SplitContainer *p_nested_sc);
+ void _update_nested_descendent(SplitContainer *p_nested_sc, Control *p_direct_child);
+ void _update_nested_ancestors(bool p_remove = false);
+ void _update_all_nested_descendents(Control *p_control, Control *p_first_child = nullptr);
+
protected:
bool is_fixed = false;
@@ -195,6 +226,11 @@ public:
void set_touch_dragger_enabled(bool p_enabled);
bool is_touch_dragger_enabled() const;
+ void show_grabber_icon(int p_index);
+
+ void set_drag_nested_intersections(bool p_enabled);
+ bool is_dragging_nested_intersections() const;
+
#ifndef DISABLE_DEPRECATED
Control *get_drag_area_control() { return dragging_area_controls[0]; }
void _set_split_offset_first(int p_offset) { set_split_offset(p_offset); }