2014-02-10 02:10:30 +01:00
/**************************************************************************/
/* scroll_container.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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 "scroll_container.h"
2022-02-12 02:46:22 +01:00
# include "core/config/project_settings.h"
2018-09-11 18:13:45 +02:00
# include "core/os/os.h"
2020-03-04 02:51:12 +01:00
# include "scene/main/window.h"
2023-09-08 21:00:10 +02:00
# include "scene/theme/theme_db.h"
2018-09-26 16:35:32 +02:00
2014-02-10 02:10:30 +01:00
Size2 ScrollContainer : : get_minimum_size ( ) const {
2016-03-12 14:44:12 +01:00
Size2 min_size ;
2022-07-23 14:53:46 +02:00
// Calculated in this function, as it needs to traverse all child controls once to calculate;
// and needs to be calculated before being used by update_scrollbars().
largest_child_min_size = Size2 ( ) ;
2016-03-12 14:44:12 +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 ) ) ;
2022-06-16 08:12:31 +02:00
if ( ! c | | ! c - > is_visible ( ) ) {
2016-03-12 14:44:12 +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 ( ) ) {
2016-03-12 14:44:12 +01:00
continue ;
2020-05-14 16:41:43 +02:00
}
if ( c = = h_scroll | | c = = v_scroll ) {
2016-03-12 14:44:12 +01:00
continue ;
2020-05-14 16:41:43 +02:00
}
2016-03-12 14:44:12 +01:00
2022-07-23 14:53:46 +02:00
Size2 child_min_size = c - > get_combined_minimum_size ( ) ;
2024-03-03 12:49:08 +01:00
largest_child_min_size = largest_child_min_size . max ( child_min_size ) ;
2022-06-16 08:12:31 +02:00
}
if ( horizontal_scroll_mode = = SCROLL_MODE_DISABLED ) {
2022-07-23 14:53:46 +02:00
min_size . x = MAX ( min_size . x , largest_child_min_size . x ) ;
2022-06-16 08:12:31 +02:00
}
if ( vertical_scroll_mode = = SCROLL_MODE_DISABLED ) {
2022-07-23 14:53:46 +02:00
min_size . y = MAX ( min_size . y , largest_child_min_size . y ) ;
2016-03-12 14:44:12 +01:00
}
2022-07-23 14:53:46 +02:00
bool h_scroll_show = horizontal_scroll_mode = = SCROLL_MODE_SHOW_ALWAYS | | ( horizontal_scroll_mode = = SCROLL_MODE_AUTO & & largest_child_min_size . x > min_size . x ) ;
bool v_scroll_show = vertical_scroll_mode = = SCROLL_MODE_SHOW_ALWAYS | | ( vertical_scroll_mode = = SCROLL_MODE_AUTO & & largest_child_min_size . y > min_size . y ) ;
if ( h_scroll_show & & h_scroll - > get_parent ( ) = = this ) {
2016-03-12 14:44:12 +01:00
min_size . y + = h_scroll - > get_minimum_size ( ) . y ;
}
2022-07-23 14:53:46 +02:00
if ( v_scroll_show & & v_scroll - > get_parent ( ) = = this ) {
2016-03-12 14:44:12 +01:00
min_size . x + = v_scroll - > get_minimum_size ( ) . x ;
}
2022-06-16 08:12:31 +02:00
2022-09-06 19:09:32 +02:00
min_size + = theme_cache . panel_style - > get_minimum_size ( ) ;
2016-03-12 14:44:12 +01:00
return min_size ;
2018-05-15 22:12:35 +02:00
}
2014-02-10 02:10:30 +01:00
void ScrollContainer : : _cancel_drag ( ) {
2018-04-11 09:28:14 +02:00
set_physics_process_internal ( false ) ;
2014-02-10 02:10:30 +01:00
drag_touching_deaccel = false ;
drag_touching = false ;
drag_speed = Vector2 ( ) ;
drag_accum = Vector2 ( ) ;
last_drag_accum = Vector2 ( ) ;
drag_from = Vector2 ( ) ;
2018-02-27 16:37:20 +01:00
if ( beyond_deadzone ) {
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " scroll_ended " ) ) ;
2018-02-27 16:37:20 +01:00
propagate_notification ( NOTIFICATION_SCROLL_END ) ;
beyond_deadzone = false ;
}
2014-02-10 02:10:30 +01:00
}
2021-08-22 17:37:22 +02:00
void ScrollContainer : : gui_input ( const Ref < InputEvent > & p_gui_input ) {
2021-04-05 08:52:21 +02:00
ERR_FAIL_COND ( p_gui_input . is_null ( ) ) ;
2019-05-21 18:53:29 +02:00
double prev_v_scroll = v_scroll - > get_value ( ) ;
double prev_h_scroll = h_scroll - > get_value ( ) ;
2022-07-28 00:54:06 +02:00
bool h_scroll_enabled = horizontal_scroll_mode ! = SCROLL_MODE_DISABLED ;
bool v_scroll_enabled = vertical_scroll_mode ! = SCROLL_MODE_DISABLED ;
2019-05-21 18:53:29 +02:00
2017-05-20 17:38:03 +02:00
Ref < InputEventMouseButton > mb = p_gui_input ;
2014-02-10 02:10:30 +01:00
2017-05-20 17:38:03 +02:00
if ( mb . is_valid ( ) ) {
2022-07-28 00:54:06 +02:00
if ( mb - > is_pressed ( ) ) {
bool scroll_value_modified = false ;
bool v_scroll_hidden = ! v_scroll - > is_visible ( ) & & vertical_scroll_mode ! = SCROLL_MODE_SHOW_NEVER ;
if ( mb - > get_button_index ( ) = = MouseButton : : WHEEL_UP ) {
// By default, the vertical orientation takes precedence. This is an exception.
if ( ( h_scroll_enabled & & mb - > is_shift_pressed ( ) ) | | v_scroll_hidden ) {
h_scroll - > set_value ( prev_h_scroll - h_scroll - > get_page ( ) / 8 * mb - > get_factor ( ) ) ;
scroll_value_modified = true ;
} else if ( v_scroll_enabled ) {
v_scroll - > set_value ( prev_v_scroll - v_scroll - > get_page ( ) / 8 * mb - > get_factor ( ) ) ;
scroll_value_modified = true ;
}
2014-02-10 02:10:30 +01:00
}
2022-07-28 00:54:06 +02:00
if ( mb - > get_button_index ( ) = = MouseButton : : WHEEL_DOWN ) {
if ( ( h_scroll_enabled & & mb - > is_shift_pressed ( ) ) | | v_scroll_hidden ) {
h_scroll - > set_value ( prev_h_scroll + h_scroll - > get_page ( ) / 8 * mb - > get_factor ( ) ) ;
scroll_value_modified = true ;
} else if ( v_scroll_enabled ) {
v_scroll - > set_value ( prev_v_scroll + v_scroll - > get_page ( ) / 8 * mb - > get_factor ( ) ) ;
scroll_value_modified = true ;
}
2017-02-21 23:45:31 +01:00
}
2022-07-28 00:54:06 +02:00
bool h_scroll_hidden = ! h_scroll - > is_visible ( ) & & horizontal_scroll_mode ! = SCROLL_MODE_SHOW_NEVER ;
if ( mb - > get_button_index ( ) = = MouseButton : : WHEEL_LEFT ) {
// By default, the horizontal orientation takes precedence. This is an exception.
if ( ( v_scroll_enabled & & mb - > is_shift_pressed ( ) ) | | h_scroll_hidden ) {
v_scroll - > set_value ( prev_v_scroll - v_scroll - > get_page ( ) / 8 * mb - > get_factor ( ) ) ;
scroll_value_modified = true ;
} else if ( h_scroll_enabled ) {
h_scroll - > set_value ( prev_h_scroll - h_scroll - > get_page ( ) / 8 * mb - > get_factor ( ) ) ;
scroll_value_modified = true ;
}
2017-02-21 23:45:31 +01:00
}
2022-07-28 00:54:06 +02:00
if ( mb - > get_button_index ( ) = = MouseButton : : WHEEL_RIGHT ) {
if ( ( v_scroll_enabled & & mb - > is_shift_pressed ( ) ) | | h_scroll_hidden ) {
v_scroll - > set_value ( prev_v_scroll + v_scroll - > get_page ( ) / 8 * mb - > get_factor ( ) ) ;
scroll_value_modified = true ;
} else if ( h_scroll_enabled ) {
h_scroll - > set_value ( prev_h_scroll + h_scroll - > get_page ( ) / 8 * mb - > get_factor ( ) ) ;
scroll_value_modified = true ;
}
2014-02-10 02:10:30 +01:00
}
2022-07-28 00:54:06 +02:00
if ( scroll_value_modified & & ( v_scroll - > get_value ( ) ! = prev_v_scroll | | h_scroll - > get_value ( ) ! = prev_h_scroll ) ) {
accept_event ( ) ; // Accept event if scroll changed.
return ;
}
2020-05-14 16:41:43 +02:00
}
2019-05-21 18:53:29 +02:00
2022-10-17 00:59:51 +02:00
bool is_touchscreen_available = DisplayServer : : get_singleton ( ) - > is_touchscreen_available ( ) ;
if ( ! is_touchscreen_available ) {
2017-05-20 17:38:03 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2021-08-13 23:31:57 +02:00
if ( mb - > get_button_index ( ) ! = MouseButton : : LEFT ) {
2017-05-20 17:38:03 +02: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
if ( mb - > is_pressed ( ) ) {
if ( drag_touching ) {
2018-02-27 16:37:20 +01:00
_cancel_drag ( ) ;
2017-05-20 17:38:03 +02:00
}
2014-02-10 02:10:30 +01:00
2019-06-26 15:08:25 +02:00
drag_speed = Vector2 ( ) ;
drag_accum = Vector2 ( ) ;
last_drag_accum = Vector2 ( ) ;
2022-07-28 00:54:06 +02:00
drag_from = Vector2 ( prev_h_scroll , prev_v_scroll ) ;
2022-11-03 23:57:07 +01:00
drag_touching = true ;
2019-06-26 15:08:25 +02:00
drag_touching_deaccel = false ;
beyond_deadzone = false ;
time_since_motion = 0 ;
2022-11-03 23:57:07 +01:00
set_physics_process_internal ( true ) ;
time_since_motion = 0 ;
2014-02-10 02:10:30 +01:00
2017-05-20 17:38:03 +02:00
} else {
if ( drag_touching ) {
if ( drag_speed = = Vector2 ( ) ) {
2018-02-27 16:37:20 +01:00
_cancel_drag ( ) ;
2017-05-20 17:38:03 +02:00
} else {
drag_touching_deaccel = true ;
2014-02-10 02:10:30 +01:00
}
}
2017-05-20 17:38:03 +02:00
}
2022-07-28 00:54:06 +02:00
return ;
2017-05-20 17:38:03 +02:00
}
2014-02-10 02:10:30 +01:00
2017-05-20 17:38:03 +02:00
Ref < InputEventMouseMotion > mm = p_gui_input ;
2014-02-10 02:10:30 +01:00
2017-05-20 17:38:03 +02:00
if ( mm . is_valid ( ) ) {
if ( drag_touching & & ! drag_touching_deaccel ) {
2021-09-23 16:58:43 +02:00
Vector2 motion = mm - > get_relative ( ) ;
2017-05-20 17:38:03 +02:00
drag_accum - = motion ;
2018-02-27 16:37:20 +01:00
2022-07-28 00:54:06 +02:00
if ( beyond_deadzone | | ( h_scroll_enabled & & Math : : abs ( drag_accum . x ) > deadzone ) | | ( v_scroll_enabled & & Math : : abs ( drag_accum . y ) > deadzone ) ) {
2018-02-27 16:37:20 +01:00
if ( ! beyond_deadzone ) {
propagate_notification ( NOTIFICATION_SCROLL_BEGIN ) ;
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " scroll_started " ) ) ;
2018-02-27 16:37:20 +01:00
beyond_deadzone = true ;
2022-07-28 00:54:06 +02:00
// Resetting drag_accum here ensures smooth scrolling after reaching deadzone.
2018-02-27 16:37:20 +01:00
drag_accum = - motion ;
}
Vector2 diff = drag_from + drag_accum ;
2022-07-28 00:54:06 +02:00
if ( h_scroll_enabled ) {
2018-02-27 16:37:20 +01:00
h_scroll - > set_value ( diff . x ) ;
2020-05-14 16:41:43 +02:00
} else {
2018-02-27 16:37:20 +01:00
drag_accum . x = 0 ;
2020-05-14 16:41:43 +02:00
}
2022-07-28 00:54:06 +02:00
if ( v_scroll_enabled ) {
2018-02-27 16:37:20 +01:00
v_scroll - > set_value ( diff . y ) ;
2020-05-14 16:41:43 +02:00
} else {
2018-02-27 16:37:20 +01:00
drag_accum . y = 0 ;
2020-05-14 16:41:43 +02:00
}
2018-02-27 16:37:20 +01:00
time_since_motion = 0 ;
}
2017-05-20 17:38:03 +02:00
}
2022-07-28 00:54:06 +02:00
if ( v_scroll - > get_value ( ) ! = prev_v_scroll | | h_scroll - > get_value ( ) ! = prev_h_scroll ) {
accept_event ( ) ; // Accept event if scroll changed.
}
return ;
2014-02-10 02:10:30 +01:00
}
2017-11-01 21:49:39 +01:00
Ref < InputEventPanGesture > pan_gesture = p_gui_input ;
if ( pan_gesture . is_valid ( ) ) {
2022-07-28 00:54:06 +02:00
if ( h_scroll_enabled ) {
h_scroll - > set_value ( prev_h_scroll + h_scroll - > get_page ( ) * pan_gesture - > get_delta ( ) . x / 8 ) ;
2017-11-01 21:49:39 +01:00
}
2022-07-28 00:54:06 +02:00
if ( v_scroll_enabled ) {
v_scroll - > set_value ( prev_v_scroll + v_scroll - > get_page ( ) * pan_gesture - > get_delta ( ) . y / 8 ) ;
2017-11-01 21:49:39 +01:00
}
2019-05-21 18:53:29 +02:00
2022-07-28 00:54:06 +02:00
if ( v_scroll - > get_value ( ) ! = prev_v_scroll | | h_scroll - > get_value ( ) ! = prev_h_scroll ) {
accept_event ( ) ; // Accept event if scroll changed.
}
return ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2020-01-16 04:36:31 +01:00
void ScrollContainer : : _update_scrollbar_position ( ) {
2020-09-03 13:22:16 +02:00
if ( ! _updating_scrollbars ) {
return ;
}
2020-01-16 04:36:31 +01:00
Size2 hmin = h_scroll - > get_combined_minimum_size ( ) ;
Size2 vmin = v_scroll - > get_combined_minimum_size ( ) ;
2020-12-22 17:24:29 +01:00
h_scroll - > set_anchor_and_offset ( SIDE_LEFT , ANCHOR_BEGIN , 0 ) ;
h_scroll - > set_anchor_and_offset ( SIDE_RIGHT , ANCHOR_END , 0 ) ;
h_scroll - > set_anchor_and_offset ( SIDE_TOP , ANCHOR_END , - hmin . height ) ;
h_scroll - > set_anchor_and_offset ( SIDE_BOTTOM , ANCHOR_END , 0 ) ;
2020-01-16 04:36:31 +01:00
2020-12-22 17:24:29 +01:00
v_scroll - > set_anchor_and_offset ( SIDE_LEFT , ANCHOR_END , - vmin . width ) ;
v_scroll - > set_anchor_and_offset ( SIDE_RIGHT , ANCHOR_END , 0 ) ;
v_scroll - > set_anchor_and_offset ( SIDE_TOP , ANCHOR_BEGIN , 0 ) ;
v_scroll - > set_anchor_and_offset ( SIDE_BOTTOM , ANCHOR_END , 0 ) ;
2020-01-16 04:36:31 +01:00
2020-09-03 13:22:16 +02:00
_updating_scrollbars = false ;
2020-01-16 04:36:31 +01:00
}
2021-05-15 05:19:23 +02:00
void ScrollContainer : : _gui_focus_changed ( Control * p_control ) {
2021-06-19 00:02:50 +02:00
if ( follow_focus & & is_ancestor_of ( p_control ) ) {
2021-05-15 05:19:23 +02:00
ensure_control_visible ( p_control ) ;
2019-12-24 00:03:24 +01:00
}
2021-05-15 05:19:23 +02:00
}
2019-12-24 00:03:24 +01:00
2021-05-15 05:19:23 +02:00
void ScrollContainer : : ensure_control_visible ( Control * p_control ) {
2021-06-19 00:02:50 +02:00
ERR_FAIL_COND_MSG ( ! is_ancestor_of ( p_control ) , " Must be an ancestor of the control. " ) ;
2019-12-11 14:29:36 +01:00
2021-05-15 05:19:23 +02:00
Rect2 global_rect = get_global_rect ( ) ;
Rect2 other_rect = p_control - > get_global_rect ( ) ;
float right_margin = v_scroll - > is_visible ( ) ? v_scroll - > get_size ( ) . x : 0.0f ;
float bottom_margin = h_scroll - > is_visible ( ) ? h_scroll - > get_size ( ) . y : 0.0f ;
Vector2 diff = Vector2 ( MAX ( MIN ( other_rect . position . x , global_rect . position . x ) , other_rect . position . x + other_rect . size . x - global_rect . size . x + ( ! is_layout_rtl ( ) ? right_margin : 0.0f ) ) ,
MAX ( MIN ( other_rect . position . y , global_rect . position . y ) , other_rect . position . y + other_rect . size . y - global_rect . size . y + bottom_margin ) ) ;
set_h_scroll ( get_h_scroll ( ) + ( diff . x - global_rect . position . x ) ) ;
set_v_scroll ( get_v_scroll ( ) + ( diff . y - global_rect . position . y ) ) ;
2019-12-11 14:29:36 +01:00
}
2022-07-23 14:53:46 +02:00
void ScrollContainer : : _reposition_children ( ) {
update_scrollbars ( ) ;
2020-12-29 03:51:38 +01:00
Size2 size = get_size ( ) ;
Point2 ofs ;
2022-09-06 19:09:32 +02:00
size - = theme_cache . panel_style - > get_minimum_size ( ) ;
ofs + = theme_cache . panel_style - > get_offset ( ) ;
2020-12-29 03:51:38 +01:00
bool rtl = is_layout_rtl ( ) ;
if ( h_scroll - > is_visible_in_tree ( ) & & h_scroll - > get_parent ( ) = = this ) { //scrolls may have been moved out for reasons
size . y - = h_scroll - > get_minimum_size ( ) . y ;
}
if ( v_scroll - > is_visible_in_tree ( ) & & v_scroll - > get_parent ( ) = = this ) { //scrolls may have been moved out for reasons
size . x - = v_scroll - > get_minimum_size ( ) . x ;
}
for ( int i = 0 ; i < get_child_count ( ) ; i + + ) {
Control * c = Object : : cast_to < Control > ( get_child ( i ) ) ;
2022-06-16 08:12:31 +02:00
if ( ! c | | ! c - > is_visible ( ) ) {
2020-12-29 03:51:38 +01:00
continue ;
}
if ( c - > is_set_as_top_level ( ) ) {
continue ;
}
if ( c = = h_scroll | | c = = v_scroll ) {
continue ;
}
Size2 minsize = c - > get_combined_minimum_size ( ) ;
2021-04-18 22:38:44 +02:00
Rect2 r = Rect2 ( - Size2 ( get_h_scroll ( ) , get_v_scroll ( ) ) , minsize ) ;
2023-01-07 20:37:21 +01:00
if ( c - > get_h_size_flags ( ) . has_flag ( SIZE_EXPAND ) ) {
2022-07-23 14:53:46 +02:00
r . size . width = MAX ( size . width , minsize . width ) ;
2020-12-29 03:51:38 +01:00
}
2023-01-07 20:37:21 +01:00
if ( c - > get_v_size_flags ( ) . has_flag ( SIZE_EXPAND ) ) {
2022-07-23 14:53:46 +02:00
r . size . height = MAX ( size . height , minsize . height ) ;
2020-12-29 03:51:38 +01:00
}
r . position + = ofs ;
if ( rtl & & v_scroll - > is_visible_in_tree ( ) & & v_scroll - > get_parent ( ) = = this ) {
r . position . x + = v_scroll - > get_minimum_size ( ) . x ;
}
r . position = r . position . floor ( ) ;
fit_child_in_rect ( c , r ) ;
}
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2020-12-29 03:51:38 +01:00
}
2014-02-10 02:10:30 +01:00
void ScrollContainer : : _notification ( int p_what ) {
2022-02-15 18:06:48 +01:00
switch ( p_what ) {
2022-08-29 11:04:31 +02:00
case NOTIFICATION_ENTER_TREE :
case NOTIFICATION_THEME_CHANGED :
2022-02-15 18:06:48 +01:00
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED :
2022-08-29 11:04:31 +02:00
case NOTIFICATION_TRANSLATION_CHANGED : {
2022-02-15 18:06:48 +01:00
_updating_scrollbars = true ;
2023-12-18 15:46:56 +01:00
callable_mp ( this , & ScrollContainer : : _update_scrollbar_position ) . call_deferred ( ) ;
2022-02-15 18:06:48 +01:00
} break ;
case NOTIFICATION_READY : {
Viewport * viewport = get_viewport ( ) ;
2023-09-09 17:52:40 +02:00
ERR_FAIL_NULL ( viewport ) ;
2022-02-15 18:06:48 +01:00
viewport - > connect ( " gui_focus_changed " , callable_mp ( this , & ScrollContainer : : _gui_focus_changed ) ) ;
2022-07-23 14:53:46 +02:00
_reposition_children ( ) ;
2022-02-15 18:06:48 +01:00
} break ;
case NOTIFICATION_SORT_CHILDREN : {
2022-07-23 14:53:46 +02:00
_reposition_children ( ) ;
2022-02-15 18:06:48 +01:00
} break ;
case NOTIFICATION_DRAW : {
2022-09-06 19:09:32 +02:00
draw_style_box ( theme_cache . panel_style , Rect2 ( Vector2 ( ) , get_size ( ) ) ) ;
2022-02-15 18:06:48 +01:00
} break ;
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS : {
if ( drag_touching ) {
if ( drag_touching_deaccel ) {
Vector2 pos = Vector2 ( h_scroll - > get_value ( ) , v_scroll - > get_value ( ) ) ;
pos + = drag_speed * get_physics_process_delta_time ( ) ;
bool turnoff_h = false ;
bool turnoff_v = false ;
if ( pos . x < 0 ) {
pos . x = 0 ;
turnoff_h = true ;
}
if ( pos . x > ( h_scroll - > get_max ( ) - h_scroll - > get_page ( ) ) ) {
pos . x = h_scroll - > get_max ( ) - h_scroll - > get_page ( ) ;
turnoff_h = true ;
}
if ( pos . y < 0 ) {
pos . y = 0 ;
turnoff_v = true ;
}
if ( pos . y > ( v_scroll - > get_max ( ) - v_scroll - > get_page ( ) ) ) {
pos . y = v_scroll - > get_max ( ) - v_scroll - > get_page ( ) ;
turnoff_v = true ;
}
if ( horizontal_scroll_mode ! = SCROLL_MODE_DISABLED ) {
h_scroll - > set_value ( pos . x ) ;
}
if ( vertical_scroll_mode ! = SCROLL_MODE_DISABLED ) {
v_scroll - > set_value ( pos . y ) ;
}
float sgn_x = drag_speed . x < 0 ? - 1 : 1 ;
float val_x = Math : : abs ( drag_speed . x ) ;
val_x - = 1000 * get_physics_process_delta_time ( ) ;
if ( val_x < 0 ) {
turnoff_h = true ;
}
float sgn_y = drag_speed . y < 0 ? - 1 : 1 ;
float val_y = Math : : abs ( drag_speed . y ) ;
val_y - = 1000 * get_physics_process_delta_time ( ) ;
if ( val_y < 0 ) {
turnoff_v = true ;
}
drag_speed = Vector2 ( sgn_x * val_x , sgn_y * val_y ) ;
if ( turnoff_h & & turnoff_v ) {
_cancel_drag ( ) ;
}
2014-02-10 02:10:30 +01:00
2022-02-15 18:06:48 +01:00
} else {
if ( time_since_motion = = 0 | | time_since_motion > 0.1 ) {
Vector2 diff = drag_accum - last_drag_accum ;
last_drag_accum = drag_accum ;
drag_speed = diff / get_physics_process_delta_time ( ) ;
}
2014-02-10 02:10:30 +01:00
2022-02-15 18:06:48 +01:00
time_since_motion + = get_physics_process_delta_time ( ) ;
2014-02-10 02:10:30 +01:00
}
}
2022-02-15 18:06:48 +01:00
} break ;
2014-02-10 02:10:30 +01:00
}
2022-02-15 18:06:48 +01:00
}
2014-02-10 02:10:30 +01:00
void ScrollContainer : : update_scrollbars ( ) {
Size2 size = get_size ( ) ;
2022-09-06 19:09:32 +02:00
size - = theme_cache . panel_style - > get_minimum_size ( ) ;
2014-02-10 02:10:30 +01:00
2022-07-23 14:53:46 +02:00
Size2 hmin = h_scroll - > get_combined_minimum_size ( ) ;
Size2 vmin = v_scroll - > get_combined_minimum_size ( ) ;
2014-02-10 02:10:30 +01:00
2022-07-23 14:53:46 +02:00
h_scroll - > set_visible ( horizontal_scroll_mode = = SCROLL_MODE_SHOW_ALWAYS | | ( horizontal_scroll_mode = = SCROLL_MODE_AUTO & & largest_child_min_size . width > size . width ) ) ;
v_scroll - > set_visible ( vertical_scroll_mode = = SCROLL_MODE_SHOW_ALWAYS | | ( vertical_scroll_mode = = SCROLL_MODE_AUTO & & largest_child_min_size . height > size . height ) ) ;
2014-02-10 02:10:30 +01:00
2022-07-23 14:53:46 +02:00
h_scroll - > set_max ( largest_child_min_size . width ) ;
h_scroll - > set_page ( ( v_scroll - > is_visible ( ) & & v_scroll - > get_parent ( ) = = this ) ? size . width - vmin . width : size . width ) ;
2019-09-01 18:31:32 +02:00
2022-07-23 14:53:46 +02:00
v_scroll - > set_max ( largest_child_min_size . height ) ;
v_scroll - > set_page ( ( h_scroll - > is_visible ( ) & & h_scroll - > get_parent ( ) = = this ) ? size . height - hmin . height : size . height ) ;
2020-01-14 22:19:12 +01:00
// Avoid scrollbar overlapping.
2022-07-23 14:53:46 +02:00
h_scroll - > set_anchor_and_offset ( SIDE_RIGHT , ANCHOR_END , ( v_scroll - > is_visible ( ) & & v_scroll - > get_parent ( ) = = this ) ? - vmin . width : 0 ) ;
v_scroll - > set_anchor_and_offset ( SIDE_BOTTOM , ANCHOR_END , ( h_scroll - > is_visible ( ) & & h_scroll - > get_parent ( ) = = this ) ? - hmin . height : 0 ) ;
2014-02-10 02:10:30 +01:00
}
void ScrollContainer : : _scroll_moved ( float ) {
queue_sort ( ) ;
} ;
2021-04-18 22:38:44 +02:00
void ScrollContainer : : set_h_scroll ( int p_pos ) {
h_scroll - > set_value ( p_pos ) ;
_cancel_drag ( ) ;
}
int ScrollContainer : : get_h_scroll ( ) const {
return h_scroll - > get_value ( ) ;
}
void ScrollContainer : : set_v_scroll ( int p_pos ) {
v_scroll - > set_value ( p_pos ) ;
_cancel_drag ( ) ;
}
int ScrollContainer : : get_v_scroll ( ) const {
return v_scroll - > get_value ( ) ;
}
2023-01-03 10:36:44 +01:00
void ScrollContainer : : set_horizontal_custom_step ( float p_custom_step ) {
h_scroll - > set_custom_step ( p_custom_step ) ;
}
float ScrollContainer : : get_horizontal_custom_step ( ) const {
return h_scroll - > get_custom_step ( ) ;
}
void ScrollContainer : : set_vertical_custom_step ( float p_custom_step ) {
v_scroll - > set_custom_step ( p_custom_step ) ;
}
float ScrollContainer : : get_vertical_custom_step ( ) const {
return v_scroll - > get_custom_step ( ) ;
}
2021-12-07 17:15:18 +01:00
void ScrollContainer : : set_horizontal_scroll_mode ( ScrollMode p_mode ) {
if ( horizontal_scroll_mode = = p_mode ) {
2020-01-16 02:38:32 +01:00
return ;
}
2014-02-10 02:10:30 +01:00
2021-12-07 17:15:18 +01:00
horizontal_scroll_mode = p_mode ;
2021-12-06 14:02:34 +01:00
update_minimum_size ( ) ;
2014-02-10 02:10:30 +01:00
queue_sort ( ) ;
}
2021-12-07 17:15:18 +01:00
ScrollContainer : : ScrollMode ScrollContainer : : get_horizontal_scroll_mode ( ) const {
return horizontal_scroll_mode ;
2014-02-10 02:10:30 +01:00
}
2021-12-07 17:15:18 +01:00
void ScrollContainer : : set_vertical_scroll_mode ( ScrollMode p_mode ) {
if ( vertical_scroll_mode = = p_mode ) {
2020-01-16 02:38:32 +01:00
return ;
}
2014-02-10 02:10:30 +01:00
2021-12-07 17:15:18 +01:00
vertical_scroll_mode = p_mode ;
2021-12-06 14:02:34 +01:00
update_minimum_size ( ) ;
2014-02-10 02:10:30 +01:00
queue_sort ( ) ;
}
2021-12-07 17:15:18 +01:00
ScrollContainer : : ScrollMode ScrollContainer : : get_vertical_scroll_mode ( ) const {
return vertical_scroll_mode ;
2014-02-10 02:10:30 +01:00
}
2018-02-27 16:37:20 +01:00
int ScrollContainer : : get_deadzone ( ) const {
return deadzone ;
}
void ScrollContainer : : set_deadzone ( int p_deadzone ) {
deadzone = p_deadzone ;
}
2019-12-24 00:03:24 +01:00
bool ScrollContainer : : is_following_focus ( ) const {
return follow_focus ;
}
2019-12-30 23:42:39 +01:00
void ScrollContainer : : set_follow_focus ( bool p_follow ) {
2019-12-24 00:03:24 +01:00
follow_focus = p_follow ;
}
2024-02-17 19:03:21 +01:00
PackedStringArray ScrollContainer : : get_configuration_warnings ( ) const {
PackedStringArray warnings = Container : : get_configuration_warnings ( ) ;
2020-05-14 22:59:27 +02:00
2017-01-09 19:50:08 +01:00
int found = 0 ;
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 ) ) ;
2020-05-14 16:41:43 +02:00
if ( ! c ) {
2017-01-09 19:50:08 +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 ( ) ) {
2017-01-09 19:50:08 +01:00
continue ;
2020-05-14 16:41:43 +02:00
}
if ( c = = h_scroll | | c = = v_scroll ) {
2017-01-09 19:50:08 +01:00
continue ;
2020-05-14 16:41:43 +02:00
}
2017-01-09 19:50:08 +01:00
found + + ;
}
2020-05-14 16:41:43 +02:00
if ( found ! = 1 ) {
2022-03-28 15:24:14 +02:00
warnings . push_back ( RTR ( " ScrollContainer is intended to work with a single child control. \n Use a container as child (VBox, HBox, etc.), or a Control and set the custom minimum size manually. " ) ) ;
2020-05-14 16:41:43 +02:00
}
2020-10-29 11:01:28 +01:00
return warnings ;
2017-01-09 19:50:08 +01:00
}
2021-11-30 17:46:36 +01:00
HScrollBar * ScrollContainer : : get_h_scroll_bar ( ) {
2018-06-07 17:46:14 +02:00
return h_scroll ;
}
2021-11-30 17:46:36 +01:00
VScrollBar * ScrollContainer : : get_v_scroll_bar ( ) {
2018-06-07 17:46:14 +02:00
return v_scroll ;
}
2014-02-10 02:10:30 +01:00
void ScrollContainer : : _bind_methods ( ) {
2018-01-17 10:43:23 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_h_scroll " , " value " ) , & ScrollContainer : : set_h_scroll ) ;
2017-02-13 12:47:24 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_h_scroll " ) , & ScrollContainer : : get_h_scroll ) ;
2021-04-18 22:38:44 +02:00
2018-01-17 10:43:23 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_v_scroll " , " value " ) , & ScrollContainer : : set_v_scroll ) ;
2017-02-13 12:47:24 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_v_scroll " ) , & ScrollContainer : : get_v_scroll ) ;
2021-04-18 22:38:44 +02:00
2023-01-03 10:36:44 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_horizontal_custom_step " , " value " ) , & ScrollContainer : : set_horizontal_custom_step ) ;
ClassDB : : bind_method ( D_METHOD ( " get_horizontal_custom_step " ) , & ScrollContainer : : get_horizontal_custom_step ) ;
ClassDB : : bind_method ( D_METHOD ( " set_vertical_custom_step " , " value " ) , & ScrollContainer : : set_vertical_custom_step ) ;
ClassDB : : bind_method ( D_METHOD ( " get_vertical_custom_step " ) , & ScrollContainer : : get_vertical_custom_step ) ;
2021-12-07 17:15:18 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_horizontal_scroll_mode " , " enable " ) , & ScrollContainer : : set_horizontal_scroll_mode ) ;
ClassDB : : bind_method ( D_METHOD ( " get_horizontal_scroll_mode " ) , & ScrollContainer : : get_horizontal_scroll_mode ) ;
2021-04-18 22:38:44 +02:00
2021-12-07 17:15:18 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_vertical_scroll_mode " , " enable " ) , & ScrollContainer : : set_vertical_scroll_mode ) ;
ClassDB : : bind_method ( D_METHOD ( " get_vertical_scroll_mode " ) , & ScrollContainer : : get_vertical_scroll_mode ) ;
2021-04-18 22:38:44 +02:00
2018-02-27 16:37:20 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_deadzone " , " deadzone " ) , & ScrollContainer : : set_deadzone ) ;
ClassDB : : bind_method ( D_METHOD ( " get_deadzone " ) , & ScrollContainer : : get_deadzone ) ;
2021-04-18 22:38:44 +02:00
2019-12-24 00:03:24 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_follow_focus " , " enabled " ) , & ScrollContainer : : set_follow_focus ) ;
ClassDB : : bind_method ( D_METHOD ( " is_following_focus " ) , & ScrollContainer : : is_following_focus ) ;
2018-02-27 16:37:20 +01:00
2021-11-30 17:46:36 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_h_scroll_bar " ) , & ScrollContainer : : get_h_scroll_bar ) ;
ClassDB : : bind_method ( D_METHOD ( " get_v_scroll_bar " ) , & ScrollContainer : : get_v_scroll_bar ) ;
2021-05-15 05:19:23 +02:00
ClassDB : : bind_method ( D_METHOD ( " ensure_control_visible " , " control " ) , & ScrollContainer : : ensure_control_visible ) ;
2018-06-07 17:46:14 +02:00
2018-02-27 16:37:20 +01:00
ADD_SIGNAL ( MethodInfo ( " scroll_started " ) ) ;
ADD_SIGNAL ( MethodInfo ( " scroll_ended " ) ) ;
2017-03-05 16:44:50 +01:00
2019-12-24 00:03:24 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " follow_focus " ) , " set_follow_focus " , " is_following_focus " ) ;
2017-01-04 05:16:14 +01:00
ADD_GROUP ( " Scroll " , " scroll_ " ) ;
2022-05-20 07:24:41 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " scroll_horizontal " , PROPERTY_HINT_NONE , " suffix:px " ) , " set_h_scroll " , " get_h_scroll " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " scroll_vertical " , PROPERTY_HINT_NONE , " suffix:px " ) , " set_v_scroll " , " get_v_scroll " ) ;
2023-01-03 10:36:44 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " scroll_horizontal_custom_step " , PROPERTY_HINT_RANGE , " -1,4096,suffix:px " ) , " set_horizontal_custom_step " , " get_horizontal_custom_step " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " scroll_vertical_custom_step " , PROPERTY_HINT_RANGE , " -1,4096,suffix:px " ) , " set_vertical_custom_step " , " get_vertical_custom_step " ) ;
2021-12-07 17:15:18 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " horizontal_scroll_mode " , PROPERTY_HINT_ENUM , " Disabled,Auto,Always Show,Never Show " ) , " set_horizontal_scroll_mode " , " get_horizontal_scroll_mode " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " vertical_scroll_mode " , PROPERTY_HINT_ENUM , " Disabled,Auto,Always Show,Never Show " ) , " set_vertical_scroll_mode " , " get_vertical_scroll_mode " ) ;
2018-02-27 16:37:20 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " scroll_deadzone " ) , " set_deadzone " , " get_deadzone " ) ;
2021-12-07 17:15:18 +01:00
BIND_ENUM_CONSTANT ( SCROLL_MODE_DISABLED ) ;
BIND_ENUM_CONSTANT ( SCROLL_MODE_AUTO ) ;
BIND_ENUM_CONSTANT ( SCROLL_MODE_SHOW_ALWAYS ) ;
BIND_ENUM_CONSTANT ( SCROLL_MODE_SHOW_NEVER ) ;
2023-09-08 21:00:10 +02:00
BIND_THEME_ITEM_CUSTOM ( Theme : : DATA_TYPE_STYLEBOX , ScrollContainer , panel_style , " panel " ) ;
2018-02-27 16:37:20 +01:00
GLOBAL_DEF ( " gui/common/default_scroll_deadzone " , 0 ) ;
2014-02-10 02:10:30 +01:00
} ;
ScrollContainer : : ScrollContainer ( ) {
h_scroll = memnew ( HScrollBar ) ;
h_scroll - > set_name ( " _h_scroll " ) ;
2021-08-25 15:49:30 +02:00
add_child ( h_scroll , false , INTERNAL_MODE_BACK ) ;
2020-02-21 18:28:45 +01:00
h_scroll - > connect ( " value_changed " , callable_mp ( this , & ScrollContainer : : _scroll_moved ) ) ;
2014-02-10 02:10:30 +01:00
v_scroll = memnew ( VScrollBar ) ;
v_scroll - > set_name ( " _v_scroll " ) ;
2021-08-25 15:49:30 +02:00
add_child ( v_scroll , false , INTERNAL_MODE_BACK ) ;
2020-02-21 18:28:45 +01:00
v_scroll - > connect ( " value_changed " , callable_mp ( this , & ScrollContainer : : _scroll_moved ) ) ;
2014-02-10 02:10:30 +01:00
2018-02-27 16:37:20 +01:00
deadzone = GLOBAL_GET ( " gui/common/default_scroll_deadzone " ) ;
2017-01-09 19:50:08 +01:00
set_clip_contents ( true ) ;
2014-02-10 02:10:30 +01:00
} ;