SplitContainer drag nested intersections

This commit is contained in:
kit 2026-02-19 16:11:07 -05:00
parent bfbe95e510
commit 339bc4f88f
6 changed files with 429 additions and 22 deletions

View file

@ -56,6 +56,9 @@
<member name="drag_area_offset" type="int" setter="set_drag_area_offset" getter="get_drag_area_offset" default="0">
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.
</member>
<member name="drag_nested_intersections" type="bool" setter="set_drag_nested_intersections" getter="is_dragging_nested_intersections" default="false">
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]).
</member>
<member name="dragger_visibility" type="int" setter="set_dragger_visibility" getter="get_dragger_visibility" enum="SplitContainer.DraggerVisibility" default="0">
Determines the dragger's visibility. This property does not determine whether dragging is enabled or not. Use [member dragging_enabled] for that.
</member>

View file

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

View file

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

View file

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

View file

@ -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<InputEvent> &p_event) {
SplitContainer *parent_sc = Object::cast_to<SplitContainer>(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<InputEventMouseButton> 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<InputEventMouseMotion> 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<SplitContainer>(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<SplitContainer>(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<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
@ -77,9 +183,7 @@ void SplitContainerDragger::gui_input(const Ref<InputEvent> &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<SplitContainer>(get_parent());
sc->emit_signal(SNAME("drag_ended"));
for (Node *child : iterate_children()) {
SplitContainerMultiDragger *dragger = Object::cast_to<SplitContainerMultiDragger>(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<SplitContainer>(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<Texture2D> 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<SplitContainerMultiDragger>(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<SplitContainer *> to_update;
for (SplitContainerDragger *dragger : dragging_area_controls) {
for (Node *child : dragger->iterate_children()) {
SplitContainerMultiDragger *multi_dragger = Object::cast_to<SplitContainerMultiDragger>(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<SplitContainerMultiDragger>(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<SplitContainerDragger *> 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<SplitContainerMultiDragger>(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<SplitContainerMultiDragger>(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<SplitContainer>(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<false>()) {
Control *child_control = Object::cast_to<Control>(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<SplitContainer>(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");

View file

@ -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<InputEvent> &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); }