2014-02-10 02:10:30 +01:00
/*************************************************************************/
/* split_container.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 14:16:55 +02:00
/* https://godotengine.org */
2014-02-10 02:10:30 +01:00
/*************************************************************************/
2022-01-03 21:27:34 +01:00
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
2014-02-10 02:10:30 +01:00
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
2018-01-05 00:50:27 +01:00
2014-02-10 02:10:30 +01:00
# include "split_container.h"
# include "label.h"
2017-03-05 16:44:50 +01:00
# include "margin_container.h"
2014-02-10 02:10:30 +01:00
Control * SplitContainer : : _getch ( int p_idx ) const {
2017-03-05 16:44:50 +01:00
int idx = 0 ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < get_child_count ( ) ; i + + ) {
2017-08-24 22:58:51 +02:00
Control * c = Object : : cast_to < Control > ( get_child ( i ) ) ;
2021-07-02 19:30:50 +02:00
if ( ! c | | ! c - > is_visible ( ) ) {
2014-02-10 02:10:30 +01:00
continue ;
2020-05-14 16:41:43 +02:00
}
2020-10-01 09:17:33 +02:00
if ( c - > is_set_as_top_level ( ) ) {
2014-02-10 02:10:30 +01:00
continue ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( idx = = p_idx ) {
2014-02-10 02:10:30 +01:00
return c ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
idx + + ;
}
2020-04-02 01:20:12 +02:00
return nullptr ;
2014-02-10 02:10:30 +01:00
}
void SplitContainer : : _resort ( ) {
2017-03-05 16:44:50 +01:00
int axis = vertical ? 1 : 0 ;
2014-02-10 02:10:30 +01:00
2018-01-20 21:03:20 +01:00
Control * first = _getch ( 0 ) ;
Control * second = _getch ( 1 ) ;
2014-02-10 02:10:30 +01:00
2018-01-20 21:03:20 +01:00
// If we have only one element
if ( ! first | | ! second ) {
if ( first ) {
2018-11-15 16:47:28 +01:00
fit_child_in_rect ( first , Rect2 ( Point2 ( ) , get_size ( ) ) ) ;
2018-01-20 21:03:20 +01:00
} else if ( second ) {
2018-11-15 16:47:28 +01:00
fit_child_in_rect ( second , Rect2 ( Point2 ( ) , get_size ( ) ) ) ;
2018-01-20 21:03:20 +01:00
}
2014-02-10 02:10:30 +01:00
return ;
}
2018-01-20 21:03:20 +01:00
// Determine expanded children
2018-11-15 16:47:28 +01:00
bool first_expanded = ( vertical ? first - > get_v_size_flags ( ) : first - > get_h_size_flags ( ) ) & SIZE_EXPAND ;
bool second_expanded = ( vertical ? second - > get_v_size_flags ( ) : second - > get_h_size_flags ( ) ) & SIZE_EXPAND ;
2014-02-10 02:10:30 +01:00
2018-01-20 21:03:20 +01:00
// Determine the separation between items
2021-07-17 23:22:52 +02:00
Ref < Texture2D > g = get_theme_icon ( SNAME ( " grabber " ) ) ;
int sep = get_theme_constant ( SNAME ( " separation " ) ) ;
2018-11-15 16:47:28 +01:00
sep = ( dragger_visibility ! = DRAGGER_HIDDEN_COLLAPSED ) ? MAX ( sep , vertical ? g - > get_height ( ) : g - > get_width ( ) ) : 0 ;
2014-02-10 02:10:30 +01:00
2018-01-20 21:03:20 +01:00
// Compute the minimum size
2014-02-10 02:10:30 +01:00
Size2 ms_first = first - > get_combined_minimum_size ( ) ;
Size2 ms_second = second - > get_combined_minimum_size ( ) ;
2018-11-15 16:47:28 +01:00
// Compute the separator position without the split offset
2018-01-20 21:03:20 +01:00
float ratio = first - > get_stretch_ratio ( ) / ( first - > get_stretch_ratio ( ) + second - > get_stretch_ratio ( ) ) ;
int no_offset_middle_sep = 0 ;
if ( first_expanded & & second_expanded ) {
no_offset_middle_sep = get_size ( ) [ axis ] * ratio - sep / 2 ;
} else if ( first_expanded ) {
no_offset_middle_sep = get_size ( ) [ axis ] - ms_second [ axis ] - sep ;
2014-02-10 02:10:30 +01:00
} else {
2018-01-20 21:03:20 +01:00
no_offset_middle_sep = ms_first [ axis ] ;
}
2014-02-10 02:10:30 +01:00
2018-11-15 16:47:28 +01:00
// Compute the final middle separation
2018-12-01 17:31:37 +01:00
middle_sep = no_offset_middle_sep ;
if ( ! collapsed ) {
int clamped_split_offset = CLAMP ( split_offset , ms_first [ axis ] - no_offset_middle_sep , ( get_size ( ) [ axis ] - ms_second [ axis ] - sep ) - no_offset_middle_sep ) ;
middle_sep + = clamped_split_offset ;
if ( should_clamp_split_offset ) {
split_offset = clamped_split_offset ;
2021-02-10 21:18:45 +01:00
2018-12-01 17:31:37 +01:00
should_clamp_split_offset = false ;
}
2014-02-10 02:10:30 +01:00
}
if ( vertical ) {
2017-03-05 16:44:50 +01:00
fit_child_in_rect ( first , Rect2 ( Point2 ( 0 , 0 ) , Size2 ( get_size ( ) . width , middle_sep ) ) ) ;
int sofs = middle_sep + sep ;
fit_child_in_rect ( second , Rect2 ( Point2 ( 0 , sofs ) , Size2 ( get_size ( ) . width , get_size ( ) . height - sofs ) ) ) ;
2014-02-10 02:10:30 +01:00
} else {
2020-09-03 13:22:16 +02:00
if ( is_layout_rtl ( ) ) {
middle_sep = get_size ( ) . width - middle_sep - sep ;
fit_child_in_rect ( second , Rect2 ( Point2 ( 0 , 0 ) , Size2 ( middle_sep , get_size ( ) . height ) ) ) ;
int sofs = middle_sep + sep ;
fit_child_in_rect ( first , Rect2 ( Point2 ( sofs , 0 ) , Size2 ( get_size ( ) . width - sofs , get_size ( ) . height ) ) ) ;
} else {
fit_child_in_rect ( first , Rect2 ( Point2 ( 0 , 0 ) , Size2 ( middle_sep , get_size ( ) . height ) ) ) ;
int sofs = middle_sep + sep ;
fit_child_in_rect ( second , Rect2 ( Point2 ( sofs , 0 ) , Size2 ( get_size ( ) . width - sofs , get_size ( ) . height ) ) ) ;
}
2014-02-10 02:10:30 +01:00
}
update ( ) ;
}
Size2 SplitContainer : : get_minimum_size ( ) const {
/* Calculate MINIMUM SIZE */
Size2i minimum ;
2021-07-17 23:22:52 +02:00
Ref < Texture2D > g = get_theme_icon ( SNAME ( " grabber " ) ) ;
int sep = get_theme_constant ( SNAME ( " separation " ) ) ;
2017-03-05 16:44:50 +01:00
sep = ( dragger_visibility ! = DRAGGER_HIDDEN_COLLAPSED ) ? MAX ( sep , vertical ? g - > get_height ( ) : g - > get_width ( ) ) : 0 ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < 2 ; i + + ) {
2020-05-14 16:41:43 +02:00
if ( ! _getch ( i ) ) {
2014-02-10 02:10:30 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( i = = 1 ) {
2020-05-14 16:41:43 +02:00
if ( vertical ) {
2017-03-05 16:44:50 +01:00
minimum . height + = sep ;
2020-05-14 16:41:43 +02:00
} else {
2017-03-05 16:44:50 +01:00
minimum . width + = sep ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
Size2 ms = _getch ( i ) - > get_combined_minimum_size ( ) ;
if ( vertical ) {
2017-03-05 16:44:50 +01:00
minimum . height + = ms . height ;
minimum . width = MAX ( minimum . width , ms . width ) ;
2014-02-10 02:10:30 +01:00
} else {
2017-03-05 16:44:50 +01:00
minimum . width + = ms . width ;
minimum . height = MAX ( minimum . height , ms . height ) ;
2014-02-10 02:10:30 +01:00
}
}
return minimum ;
}
void SplitContainer : : _notification ( int p_what ) {
2017-03-05 16:44:50 +01:00
switch ( p_what ) {
2020-09-03 13:22:16 +02:00
case NOTIFICATION_TRANSLATION_CHANGED :
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED : {
queue_sort ( ) ;
} break ;
2014-02-10 02:10:30 +01:00
case NOTIFICATION_SORT_CHILDREN : {
_resort ( ) ;
} break ;
case NOTIFICATION_MOUSE_EXIT : {
2017-03-05 16:44:50 +01:00
mouse_inside = false ;
2021-07-17 23:22:52 +02:00
if ( get_theme_constant ( SNAME ( " autohide " ) ) ) {
2019-02-22 14:12:39 +01:00
update ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
} break ;
case NOTIFICATION_DRAW : {
2020-05-14 16:41:43 +02:00
if ( ! _getch ( 0 ) | | ! _getch ( 1 ) ) {
2014-02-10 02:10:30 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2021-07-17 23:22:52 +02:00
if ( collapsed | | ( ! dragging & & ! mouse_inside & & get_theme_constant ( SNAME ( " autohide " ) ) ) ) {
2014-02-10 02:10:30 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2017-12-20 16:55:03 +01:00
2020-05-14 16:41:43 +02:00
if ( dragger_visibility ! = DRAGGER_VISIBLE ) {
2019-02-22 02:56:38 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2019-02-22 02:56:38 +01:00
2021-07-17 23:22:52 +02:00
int sep = dragger_visibility ! = DRAGGER_HIDDEN_COLLAPSED ? get_theme_constant ( SNAME ( " separation " ) ) : 0 ;
Ref < Texture2D > tex = get_theme_icon ( SNAME ( " grabber " ) ) ;
2017-03-05 16:44:50 +01:00
Size2 size = get_size ( ) ;
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( vertical ) {
2019-02-22 02:56:38 +01:00
draw_texture ( tex , Point2i ( ( size . x - tex - > get_width ( ) ) / 2 , middle_sep + ( sep - tex - > get_height ( ) ) / 2 ) ) ;
2020-05-14 16:41:43 +02:00
} else {
2019-02-22 02:56:38 +01:00
draw_texture ( tex , Point2i ( middle_sep + ( sep - tex - > get_width ( ) ) / 2 , ( size . y - tex - > get_height ( ) ) / 2 ) ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
} break ;
2019-01-24 22:31:33 +01:00
case NOTIFICATION_THEME_CHANGED : {
2021-12-06 14:02:34 +01:00
update_minimum_size ( ) ;
2019-01-24 22:31:33 +01:00
} break ;
2014-02-10 02:10:30 +01:00
}
}
2021-08-22 17:37:22 +02:00
void SplitContainer : : gui_input ( const Ref < InputEvent > & p_event ) {
2021-04-05 08:52:21 +02:00
ERR_FAIL_COND ( p_event . is_null ( ) ) ;
2020-05-14 16:41:43 +02:00
if ( collapsed | | ! _getch ( 0 ) | | ! _getch ( 1 ) | | dragger_visibility ! = DRAGGER_VISIBLE ) {
2014-02-10 02:10:30 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-05-20 17:38:03 +02:00
Ref < InputEventMouseButton > mb = p_event ;
2014-02-10 02:10:30 +01:00
2017-05-20 17:38:03 +02:00
if ( mb . is_valid ( ) ) {
2021-08-13 23:31:57 +02:00
if ( mb - > get_button_index ( ) = = MouseButton : : LEFT ) {
2017-05-20 17:38:03 +02:00
if ( mb - > is_pressed ( ) ) {
2021-07-17 23:22:52 +02:00
int sep = get_theme_constant ( SNAME ( " separation " ) ) ;
2014-02-10 02:10:30 +01:00
if ( vertical ) {
2017-06-03 10:54:24 +02:00
if ( mb - > get_position ( ) . y > middle_sep & & mb - > get_position ( ) . y < middle_sep + sep ) {
2017-03-05 16:44:50 +01:00
dragging = true ;
2017-06-03 10:54:24 +02:00
drag_from = mb - > get_position ( ) . y ;
2018-01-20 21:03:20 +01:00
drag_ofs = split_offset ;
2014-02-10 02:10:30 +01:00
}
} else {
2017-06-03 10:54:24 +02:00
if ( mb - > get_position ( ) . x > middle_sep & & mb - > get_position ( ) . x < middle_sep + sep ) {
2017-03-05 16:44:50 +01:00
dragging = true ;
2017-06-03 10:54:24 +02:00
drag_from = mb - > get_position ( ) . x ;
2018-01-20 21:03:20 +01:00
drag_ofs = split_offset ;
2014-02-10 02:10:30 +01:00
}
}
} else {
2017-03-05 16:44:50 +01:00
dragging = false ;
2014-02-10 02:10:30 +01:00
}
}
}
2017-05-20 17:38:03 +02:00
Ref < InputEventMouseMotion > mm = p_event ;
2014-02-10 02:10:30 +01:00
2019-02-22 02:56:38 +01:00
if ( mm . is_valid ( ) ) {
bool mouse_inside_state = false ;
2020-05-14 16:41:43 +02:00
if ( vertical ) {
2021-07-17 23:22:52 +02:00
mouse_inside_state = mm - > get_position ( ) . y > middle_sep & & mm - > get_position ( ) . y < middle_sep + get_theme_constant ( SNAME ( " separation " ) ) ;
2020-05-14 16:41:43 +02:00
} else {
2021-07-17 23:22:52 +02:00
mouse_inside_state = mm - > get_position ( ) . x > middle_sep & & mm - > get_position ( ) . x < middle_sep + get_theme_constant ( SNAME ( " separation " ) ) ;
2020-05-14 16:41:43 +02:00
}
2019-02-22 02:56:38 +01:00
if ( mouse_inside ! = mouse_inside_state ) {
mouse_inside = mouse_inside_state ;
2021-07-17 23:22:52 +02:00
if ( get_theme_constant ( SNAME ( " autohide " ) ) ) {
2019-02-22 14:12:39 +01:00
update ( ) ;
2020-05-14 16:41:43 +02:00
}
2019-02-22 02:56:38 +01:00
}
2020-05-14 16:41:43 +02:00
if ( ! dragging ) {
2019-02-22 02:56:38 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-09-03 13:22:16 +02:00
if ( ! vertical & & is_layout_rtl ( ) ) {
split_offset = drag_ofs + ( drag_from - ( vertical ? mm - > get_position ( ) . y : mm - > get_position ( ) . x ) ) ;
} else {
split_offset = drag_ofs + ( ( vertical ? mm - > get_position ( ) . y : mm - > get_position ( ) . x ) - drag_from ) ;
}
2018-11-15 16:47:28 +01:00
should_clamp_split_offset = true ;
2017-12-20 16:55:03 +01:00
queue_sort ( ) ;
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " dragged " ) , get_split_offset ( ) ) ;
2014-02-10 02:10:30 +01:00
}
}
2017-03-05 16:44:50 +01:00
Control : : CursorShape SplitContainer : : get_cursor_shape ( const Point2 & p_pos ) const {
2020-05-14 16:41:43 +02:00
if ( dragging ) {
2020-02-08 02:23:38 +01:00
return ( vertical ? CURSOR_VSPLIT : CURSOR_HSPLIT ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-12-20 16:55:03 +01:00
if ( ! collapsed & & _getch ( 0 ) & & _getch ( 1 ) & & dragger_visibility = = DRAGGER_VISIBLE ) {
2021-07-17 23:22:52 +02:00
int sep = get_theme_constant ( SNAME ( " separation " ) ) ;
2014-02-10 02:10:30 +01:00
2017-12-20 16:55:03 +01:00
if ( vertical ) {
2020-05-14 16:41:43 +02:00
if ( p_pos . y > middle_sep & & p_pos . y < middle_sep + sep ) {
2020-02-08 02:23:38 +01:00
return CURSOR_VSPLIT ;
2020-05-14 16:41:43 +02:00
}
2017-12-20 16:55:03 +01:00
} else {
2020-05-14 16:41:43 +02:00
if ( p_pos . x > middle_sep & & p_pos . x < middle_sep + sep ) {
2020-02-08 02:23:38 +01:00
return CURSOR_HSPLIT ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
}
return Control : : get_cursor_shape ( p_pos ) ;
}
void SplitContainer : : set_split_offset ( int p_offset ) {
2020-05-14 16:41:43 +02:00
if ( split_offset = = p_offset ) {
2014-02-10 02:10:30 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2017-12-20 16:55:03 +01:00
2018-01-20 21:03:20 +01:00
split_offset = p_offset ;
2018-11-15 16:47:28 +01:00
2014-02-10 02:10:30 +01:00
queue_sort ( ) ;
}
int SplitContainer : : get_split_offset ( ) const {
2018-01-20 21:03:20 +01:00
return split_offset ;
2014-02-10 02:10:30 +01:00
}
2018-11-15 16:47:28 +01:00
void SplitContainer : : clamp_split_offset ( ) {
should_clamp_split_offset = true ;
queue_sort ( ) ;
}
2014-02-10 02:10:30 +01:00
void SplitContainer : : set_collapsed ( bool p_collapsed ) {
2020-05-14 16:41:43 +02:00
if ( collapsed = = p_collapsed ) {
2014-02-10 02:10:30 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2017-12-20 16:55:03 +01:00
2017-03-05 16:44:50 +01:00
collapsed = p_collapsed ;
2014-02-10 02:10:30 +01:00
queue_sort ( ) ;
}
2016-01-18 00:03:57 +01:00
void SplitContainer : : set_dragger_visibility ( DraggerVisibility p_visibility ) {
2017-03-05 16:44:50 +01:00
dragger_visibility = p_visibility ;
2014-02-10 02:10:30 +01:00
queue_sort ( ) ;
update ( ) ;
}
2016-01-18 00:03:57 +01:00
SplitContainer : : DraggerVisibility SplitContainer : : get_dragger_visibility ( ) const {
return dragger_visibility ;
2014-02-10 02:10:30 +01:00
}
bool SplitContainer : : is_collapsed ( ) const {
return collapsed ;
}
void SplitContainer : : _bind_methods ( ) {
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_split_offset " , " offset " ) , & SplitContainer : : set_split_offset ) ;
ClassDB : : bind_method ( D_METHOD ( " get_split_offset " ) , & SplitContainer : : get_split_offset ) ;
2018-11-15 16:47:28 +01:00
ClassDB : : bind_method ( D_METHOD ( " clamp_split_offset " ) , & SplitContainer : : clamp_split_offset ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_collapsed " , " collapsed " ) , & SplitContainer : : set_collapsed ) ;
ClassDB : : bind_method ( D_METHOD ( " is_collapsed " ) , & SplitContainer : : is_collapsed ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_dragger_visibility " , " mode " ) , & SplitContainer : : set_dragger_visibility ) ;
ClassDB : : bind_method ( D_METHOD ( " get_dragger_visibility " ) , & SplitContainer : : get_dragger_visibility ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
ADD_SIGNAL ( MethodInfo ( " dragged " , PropertyInfo ( Variant : : INT , " offset " ) ) ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " split_offset " ) , " set_split_offset " , " get_split_offset " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " collapsed " ) , " set_collapsed " , " is_collapsed " ) ;
2021-05-22 04:30:58 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " dragger_visibility " , PROPERTY_HINT_ENUM , " Visible,Hidden,Hidden and Collapsed " ) , " set_dragger_visibility " , " get_dragger_visibility " ) ;
2015-06-14 07:13:47 +02:00
2017-08-20 17:45:01 +02:00
BIND_ENUM_CONSTANT ( DRAGGER_VISIBLE ) ;
BIND_ENUM_CONSTANT ( DRAGGER_HIDDEN ) ;
BIND_ENUM_CONSTANT ( DRAGGER_HIDDEN_COLLAPSED ) ;
2014-02-10 02:10:30 +01:00
}
SplitContainer : : SplitContainer ( bool p_vertical ) {
2017-03-05 16:44:50 +01:00
vertical = p_vertical ;
2014-02-10 02:10:30 +01:00
}