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); }