@@ -57,9 +57,7 @@ void SplitContainerDragger::gui_input(const Ref<InputEvent> &p_event) {
5757 drag_from = get_transform ().xform (mb->get_position ()).x ;
5858 }
5959 } else {
60- dragging = false ;
61- queue_redraw ();
62- sc->emit_signal (SNAME (" drag_ended" ));
60+ _end_dragging ();
6361 }
6462 }
6563 }
@@ -83,6 +81,13 @@ void SplitContainerDragger::gui_input(const Ref<InputEvent> &p_event) {
8381 }
8482}
8583
84+ void SplitContainerDragger::_end_dragging () {
85+ SplitContainer *sc = Object::cast_to<SplitContainer>(get_parent ());
86+ dragging = false ;
87+ queue_redraw ();
88+ sc->emit_signal (SNAME (" drag_ended" ));
89+ }
90+
8691Control::CursorShape SplitContainerDragger::get_cursor_shape (const Point2 &p_pos) const {
8792 SplitContainer *sc = Object::cast_to<SplitContainer>(get_parent ());
8893 if (!sc->collapsed && sc->dragging_enabled ) {
@@ -196,6 +201,19 @@ Control *SplitContainer::_get_sortable_child(int p_idx, SortableVisibilityMode p
196201 return nullptr ;
197202}
198203
204+ void SplitContainer::_fit_child_in_rect_with_visibility_update (Control *p_child, const Rect2 &p_rect) {
205+ // For very small rects (like when a size is set to 0 via autosnapping) hide the child instead
206+ // of changing its rectangle. The child is scaled to 0 instead of changing visibility to avoid
207+ // affecting any existing visibility state for both the SplitContainer users and for the size
208+ // calculations SplitContainer itself does
209+ if (p_rect.size .y < 1.0 || p_rect.size .x < 1.0 ) {
210+ p_child->set_scale (Vector2 (0 , 0 ));
211+ } else {
212+ // fit_child_in_rect resets scaling to 1
213+ fit_child_in_rect (p_child, p_rect);
214+ }
215+ }
216+
199217Ref<Texture2D> SplitContainer::_get_grabber_icon () const {
200218 if (is_fixed) {
201219 return theme_cache.grabber_icon ;
@@ -245,7 +263,26 @@ void SplitContainer::_compute_split_offset(bool p_clamp) {
245263 // Clamp the split offset to acceptable values.
246264 int first_min_size = first->get_combined_minimum_size ()[axis_index];
247265 int second_min_size = second->get_combined_minimum_size ()[axis_index];
248- computed_split_offset = CLAMP (wished_size, first_min_size, size - sep - second_min_size);
266+
267+ // Check autosnapping
268+ if (dragging_area_control->dragging ) {
269+ if ((auto_snap & SNAP_FIRST) != 0 && wished_size < first_min_size / 3 ) {
270+ set_snap_state (Snap::SNAP_FIRST);
271+ } else if ((auto_snap & SNAP_SECOND) != 0 && wished_size > size - sep - second_min_size / 3 ) {
272+ set_snap_state (Snap::SNAP_SECOND);
273+ } else {
274+ set_snap_state (Snap::SNAP_NONE);
275+ }
276+ }
277+
278+ // Apply snapping
279+ if (!collapsed && (snap_state & SNAP_FIRST) != 0 ) {
280+ computed_split_offset = 0 ;
281+ } else if (!collapsed && (snap_state & SNAP_SECOND) != 0 ) {
282+ computed_split_offset = size;
283+ } else {
284+ computed_split_offset = CLAMP (wished_size, first_min_size, size - sep - second_min_size);
285+ }
249286
250287 // Clamp the split_offset if requested.
251288 if (p_clamp) {
@@ -259,9 +296,9 @@ void SplitContainer::_resort() {
259296
260297 if (!first || !second) { // Only one child.
261298 if (first) {
262- fit_child_in_rect (first, Rect2 (Point2 (), get_size ()));
299+ _fit_child_in_rect_with_visibility_update (first, Rect2 (Point2 (), get_size ()));
263300 } else if (second) {
264- fit_child_in_rect (second, Rect2 (Point2 (), get_size ()));
301+ _fit_child_in_rect_with_visibility_update (second, Rect2 (Point2 (), get_size ()));
265302 }
266303 dragging_area_control->hide ();
267304 return ;
@@ -275,19 +312,19 @@ void SplitContainer::_resort() {
275312
276313 // Move the children.
277314 if (vertical) {
278- fit_child_in_rect (first, Rect2 (Point2 (0 , 0 ), Size2 (get_size ().width , computed_split_offset)));
315+ _fit_child_in_rect_with_visibility_update (first, Rect2 (Point2 (0 , 0 ), Size2 (get_size ().width , computed_split_offset)));
279316 int sofs = computed_split_offset + sep;
280- fit_child_in_rect (second, Rect2 (Point2 (0 , sofs), Size2 (get_size ().width , get_size ().height - sofs)));
317+ _fit_child_in_rect_with_visibility_update (second, Rect2 (Point2 (0 , sofs), Size2 (get_size ().width , get_size ().height - sofs)));
281318 } else {
282319 if (is_rtl) {
283320 computed_split_offset = get_size ().width - computed_split_offset - sep;
284- fit_child_in_rect (second, Rect2 (Point2 (0 , 0 ), Size2 (computed_split_offset, get_size ().height )));
321+ _fit_child_in_rect_with_visibility_update (second, Rect2 (Point2 (0 , 0 ), Size2 (computed_split_offset, get_size ().height )));
285322 int sofs = computed_split_offset + sep;
286- fit_child_in_rect (first, Rect2 (Point2 (sofs, 0 ), Size2 (get_size ().width - sofs, get_size ().height )));
323+ _fit_child_in_rect_with_visibility_update (first, Rect2 (Point2 (sofs, 0 ), Size2 (get_size ().width - sofs, get_size ().height )));
287324 } else {
288- fit_child_in_rect (first, Rect2 (Point2 (0 , 0 ), Size2 (computed_split_offset, get_size ().height )));
325+ _fit_child_in_rect_with_visibility_update (first, Rect2 (Point2 (0 , 0 ), Size2 (computed_split_offset, get_size ().height )));
289326 int sofs = computed_split_offset + sep;
290- fit_child_in_rect (second, Rect2 (Point2 (sofs, 0 ), Size2 (get_size ().width - sofs, get_size ().height )));
327+ _fit_child_in_rect_with_visibility_update (second, Rect2 (Point2 (sofs, 0 ), Size2 (get_size ().width - sofs, get_size ().height )));
291328 }
292329 }
293330
@@ -385,23 +422,55 @@ void SplitContainer::set_collapsed(bool p_collapsed) {
385422 return ;
386423 }
387424 collapsed = p_collapsed;
425+ if (collapsed && dragging_area_control->dragging ) {
426+ dragging_area_control->_end_dragging ();
427+ }
388428 queue_sort ();
389429}
390430
431+ bool SplitContainer::is_collapsed () const {
432+ return collapsed;
433+ }
434+
391435void SplitContainer::set_dragger_visibility (DraggerVisibility p_visibility) {
392436 if (dragger_visibility == p_visibility) {
393437 return ;
394438 }
395439 dragger_visibility = p_visibility;
440+ if (dragger_visibility != DraggerVisibility::DRAGGER_VISIBLE && dragging_area_control->dragging ) {
441+ dragging_area_control->_end_dragging ();
442+ }
396443 queue_sort ();
397444}
398445
399446SplitContainer::DraggerVisibility SplitContainer::get_dragger_visibility () const {
400447 return dragger_visibility;
401448}
402449
403- bool SplitContainer::is_collapsed () const {
404- return collapsed;
450+ void SplitContainer::set_auto_snap (Snap p_auto_snap) {
451+ if (auto_snap == p_auto_snap) {
452+ return ;
453+ }
454+ auto_snap = p_auto_snap;
455+ }
456+
457+ SplitContainer::Snap SplitContainer::get_auto_snap () const {
458+ return auto_snap;
459+ }
460+
461+ void SplitContainer::set_snap_state (Snap p_snap_state) {
462+ if (snap_state == p_snap_state) {
463+ return ;
464+ }
465+ snap_state = p_snap_state;
466+ if (!collapsed) {
467+ queue_sort ();
468+ }
469+ emit_signal (SNAME (" snap_state_changed" ));
470+ }
471+
472+ SplitContainer::Snap SplitContainer::get_snap_state () const {
473+ return snap_state;
405474}
406475
407476void SplitContainer::set_vertical (bool p_vertical) {
@@ -421,9 +490,7 @@ void SplitContainer::set_dragging_enabled(bool p_enabled) {
421490 }
422491 dragging_enabled = p_enabled;
423492 if (!dragging_enabled && dragging_area_control->dragging ) {
424- dragging_area_control->dragging = false ;
425- // queue_redraw() is called by _resort().
426- emit_signal (SNAME (" drag_ended" ));
493+ dragging_area_control->_end_dragging ();
427494 }
428495 if (get_viewport ()) {
429496 get_viewport ()->update_mouse_cursor_state ();
@@ -515,6 +582,9 @@ void SplitContainer::_bind_methods() {
515582 ClassDB::bind_method (D_METHOD (" set_dragger_visibility" , " mode" ), &SplitContainer::set_dragger_visibility);
516583 ClassDB::bind_method (D_METHOD (" get_dragger_visibility" ), &SplitContainer::get_dragger_visibility);
517584
585+ ClassDB::bind_method (D_METHOD (" set_auto_snap" , " auto_snap" ), &SplitContainer::set_auto_snap);
586+ ClassDB::bind_method (D_METHOD (" get_auto_snap" ), &SplitContainer::get_auto_snap);
587+
518588 ClassDB::bind_method (D_METHOD (" set_vertical" , " vertical" ), &SplitContainer::set_vertical);
519589 ClassDB::bind_method (D_METHOD (" is_vertical" ), &SplitContainer::is_vertical);
520590
@@ -538,11 +608,13 @@ void SplitContainer::_bind_methods() {
538608 ADD_SIGNAL (MethodInfo (" dragged" , PropertyInfo (Variant::INT, " offset" )));
539609 ADD_SIGNAL (MethodInfo (" drag_started" ));
540610 ADD_SIGNAL (MethodInfo (" drag_ended" ));
611+ ADD_SIGNAL (MethodInfo (" snap_state_changed" ));
541612
542613 ADD_PROPERTY (PropertyInfo (Variant::INT, " split_offset" , PROPERTY_HINT_NONE, " suffix:px" ), " set_split_offset" , " get_split_offset" );
543614 ADD_PROPERTY (PropertyInfo (Variant::BOOL, " collapsed" ), " set_collapsed" , " is_collapsed" );
544615 ADD_PROPERTY (PropertyInfo (Variant::BOOL, " dragging_enabled" ), " set_dragging_enabled" , " is_dragging_enabled" );
545616 ADD_PROPERTY (PropertyInfo (Variant::INT, " dragger_visibility" , PROPERTY_HINT_ENUM, " Visible,Hidden,Hidden and Collapsed" ), " set_dragger_visibility" , " get_dragger_visibility" );
617+ ADD_PROPERTY (PropertyInfo (Variant::INT, " auto_snap" , PROPERTY_HINT_ENUM, " None,First,Second,Both" ), " set_auto_snap" , " get_auto_snap" );
546618 ADD_PROPERTY (PropertyInfo (Variant::BOOL, " vertical" ), " set_vertical" , " is_vertical" );
547619
548620 ADD_GROUP (" Drag Area" , " drag_area_" );
@@ -555,6 +627,11 @@ void SplitContainer::_bind_methods() {
555627 BIND_ENUM_CONSTANT (DRAGGER_HIDDEN);
556628 BIND_ENUM_CONSTANT (DRAGGER_HIDDEN_COLLAPSED);
557629
630+ BIND_ENUM_CONSTANT (SNAP_NONE);
631+ BIND_ENUM_CONSTANT (SNAP_FIRST);
632+ BIND_ENUM_CONSTANT (SNAP_SECOND);
633+ BIND_ENUM_CONSTANT (SNAP_BOTH);
634+
558635 BIND_THEME_ITEM (Theme::DATA_TYPE_CONSTANT, SplitContainer, separation);
559636 BIND_THEME_ITEM (Theme::DATA_TYPE_CONSTANT, SplitContainer, minimum_grab_thickness);
560637 BIND_THEME_ITEM (Theme::DATA_TYPE_CONSTANT, SplitContainer, autohide);
0 commit comments