2014-02-10 01:10:30 +00:00
/*************************************************************************/
/* scroll_container.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 12:16:55 +00:00
/* https://godotengine.org */
2014-02-10 01:10:30 +00:00
/*************************************************************************/
2020-01-01 10:16:22 +00:00
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
2014-02-10 01:10:30 +00: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-04 23:50:27 +00:00
2014-02-10 01:10:30 +00:00
# include "scroll_container.h"
2018-09-11 16:13:45 +00:00
# include "core/os/os.h"
2020-03-04 01:51:12 +00:00
# include "scene/main/window.h"
2018-09-26 14:35:32 +00:00
2014-02-10 01:10:30 +00:00
bool ScrollContainer : : clips_input ( ) const {
return true ;
}
Size2 ScrollContainer : : get_minimum_size ( ) const {
2020-03-12 12:37:40 +00:00
Ref < StyleBox > sb = get_theme_stylebox ( " bg " ) ;
2016-03-12 13:44:12 +00:00
Size2 min_size ;
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < get_child_count ( ) ; i + + ) {
2017-08-24 20:58:51 +00:00
Control * c = Object : : cast_to < Control > ( get_child ( i ) ) ;
2020-05-14 14:41:43 +00:00
if ( ! c ) {
2016-03-12 13:44:12 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2020-10-01 07:17:33 +00:00
if ( c - > is_set_as_top_level ( ) ) {
2016-03-12 13:44:12 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
if ( c = = h_scroll | | c = = v_scroll ) {
2016-03-12 13:44:12 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2016-03-12 13:44:12 +00:00
Size2 minsize = c - > get_combined_minimum_size ( ) ;
if ( ! scroll_h ) {
min_size . x = MAX ( min_size . x , minsize . x ) ;
}
if ( ! scroll_v ) {
min_size . y = MAX ( min_size . y , minsize . y ) ;
}
}
2017-01-13 13:45:50 +00:00
if ( h_scroll - > is_visible_in_tree ( ) ) {
2017-03-05 15:44:50 +00:00
min_size . y + = h_scroll - > get_minimum_size ( ) . y ;
2016-03-12 13:44:12 +00:00
}
2017-01-13 13:45:50 +00:00
if ( v_scroll - > is_visible_in_tree ( ) ) {
2017-03-05 15:44:50 +00:00
min_size . x + = v_scroll - > get_minimum_size ( ) . x ;
2016-03-12 13:44:12 +00:00
}
2018-05-15 20:12:35 +00:00
min_size + = sb - > get_minimum_size ( ) ;
2016-03-12 13:44:12 +00:00
return min_size ;
2018-05-15 20:12:35 +00:00
}
2014-02-10 01:10:30 +00:00
void ScrollContainer : : _cancel_drag ( ) {
2018-04-11 07:28:14 +00:00
set_physics_process_internal ( false ) ;
2017-03-05 15:44:50 +00:00
drag_touching_deaccel = false ;
drag_touching = false ;
drag_speed = Vector2 ( ) ;
drag_accum = Vector2 ( ) ;
last_drag_accum = Vector2 ( ) ;
drag_from = Vector2 ( ) ;
2018-02-27 15:37:20 +00:00
if ( beyond_deadzone ) {
emit_signal ( " scroll_ended " ) ;
propagate_notification ( NOTIFICATION_SCROLL_END ) ;
beyond_deadzone = false ;
}
2014-02-10 01:10:30 +00:00
}
2017-05-20 15:38:03 +00:00
void ScrollContainer : : _gui_input ( const Ref < InputEvent > & p_gui_input ) {
2019-05-21 16:53:29 +00:00
double prev_v_scroll = v_scroll - > get_value ( ) ;
double prev_h_scroll = h_scroll - > get_value ( ) ;
2017-05-20 15:38:03 +00:00
Ref < InputEventMouseButton > mb = p_gui_input ;
2014-02-10 01:10:30 +00:00
2017-05-20 15:38:03 +00:00
if ( mb . is_valid ( ) ) {
if ( mb - > get_button_index ( ) = = BUTTON_WHEEL_UP & & mb - > is_pressed ( ) ) {
// only horizontal is enabled, scroll horizontally
2019-05-21 16:53:29 +00:00
if ( h_scroll - > is_visible ( ) & & ( ! v_scroll - > is_visible ( ) | | mb - > get_shift ( ) ) ) {
2017-05-20 15:38:03 +00:00
h_scroll - > set_value ( h_scroll - > get_value ( ) - h_scroll - > get_page ( ) / 8 * mb - > get_factor ( ) ) ;
} else if ( v_scroll - > is_visible_in_tree ( ) ) {
v_scroll - > set_value ( v_scroll - > get_value ( ) - v_scroll - > get_page ( ) / 8 * mb - > get_factor ( ) ) ;
2014-02-10 01:10:30 +00:00
}
2017-05-20 15:38:03 +00:00
}
2014-02-10 01:10:30 +00:00
2017-05-20 15:38:03 +00:00
if ( mb - > get_button_index ( ) = = BUTTON_WHEEL_DOWN & & mb - > is_pressed ( ) ) {
// only horizontal is enabled, scroll horizontally
2019-05-21 16:53:29 +00:00
if ( h_scroll - > is_visible ( ) & & ( ! v_scroll - > is_visible ( ) | | mb - > get_shift ( ) ) ) {
2017-05-20 15:38:03 +00:00
h_scroll - > set_value ( h_scroll - > get_value ( ) + h_scroll - > get_page ( ) / 8 * mb - > get_factor ( ) ) ;
} else if ( v_scroll - > is_visible ( ) ) {
v_scroll - > set_value ( v_scroll - > get_value ( ) + v_scroll - > get_page ( ) / 8 * mb - > get_factor ( ) ) ;
2017-02-21 22:45:31 +00:00
}
2017-05-20 15:38:03 +00:00
}
2017-02-21 22:45:31 +00:00
2017-05-20 15:38:03 +00:00
if ( mb - > get_button_index ( ) = = BUTTON_WHEEL_LEFT & & mb - > is_pressed ( ) ) {
if ( h_scroll - > is_visible_in_tree ( ) ) {
h_scroll - > set_value ( h_scroll - > get_value ( ) - h_scroll - > get_page ( ) * mb - > get_factor ( ) / 8 ) ;
2017-02-21 22:45:31 +00:00
}
2017-05-20 15:38:03 +00:00
}
2017-02-21 22:45:31 +00:00
2017-05-20 15:38:03 +00:00
if ( mb - > get_button_index ( ) = = BUTTON_WHEEL_RIGHT & & mb - > is_pressed ( ) ) {
if ( h_scroll - > is_visible_in_tree ( ) ) {
h_scroll - > set_value ( h_scroll - > get_value ( ) + h_scroll - > get_page ( ) * mb - > get_factor ( ) / 8 ) ;
2014-02-10 01:10:30 +00:00
}
2017-05-20 15:38:03 +00:00
}
2014-02-10 01:10:30 +00:00
2020-05-14 14:41:43 +00:00
if ( v_scroll - > get_value ( ) ! = prev_v_scroll | | h_scroll - > get_value ( ) ! = prev_h_scroll ) {
2019-05-21 16:53:29 +00:00
accept_event ( ) ; //accept event if scroll changed
2020-05-14 14:41:43 +00:00
}
2019-05-21 16:53:29 +00:00
2020-05-14 14:41:43 +00:00
if ( ! DisplayServer : : get_singleton ( ) - > screen_is_touchscreen ( DisplayServer : : get_singleton ( ) - > window_get_current_screen ( get_viewport ( ) - > get_window_id ( ) ) ) ) {
2017-05-20 15:38:03 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2020-05-14 14:41:43 +00:00
if ( mb - > get_button_index ( ) ! = BUTTON_LEFT ) {
2017-05-20 15:38:03 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2017-05-20 15:38:03 +00:00
if ( mb - > is_pressed ( ) ) {
if ( drag_touching ) {
2018-02-27 15:37:20 +00:00
_cancel_drag ( ) ;
2017-05-20 15:38:03 +00:00
}
2014-02-10 01:10:30 +00:00
2019-06-26 13:08:25 +00:00
drag_speed = Vector2 ( ) ;
drag_accum = Vector2 ( ) ;
last_drag_accum = Vector2 ( ) ;
drag_from = Vector2 ( h_scroll - > get_value ( ) , v_scroll - > get_value ( ) ) ;
2020-03-03 13:36:29 +00:00
drag_touching = ! DisplayServer : : get_singleton ( ) - > screen_is_touchscreen ( DisplayServer : : get_singleton ( ) - > window_get_current_screen ( get_viewport ( ) - > get_window_id ( ) ) ) ;
2019-06-26 13:08:25 +00:00
drag_touching_deaccel = false ;
beyond_deadzone = false ;
time_since_motion = 0 ;
if ( drag_touching ) {
set_physics_process_internal ( true ) ;
2017-05-20 15:38:03 +00:00
time_since_motion = 0 ;
}
2014-02-10 01:10:30 +00:00
2017-05-20 15:38:03 +00:00
} else {
if ( drag_touching ) {
if ( drag_speed = = Vector2 ( ) ) {
2018-02-27 15:37:20 +00:00
_cancel_drag ( ) ;
2017-05-20 15:38:03 +00:00
} else {
drag_touching_deaccel = true ;
2014-02-10 01:10:30 +00:00
}
}
2017-05-20 15:38:03 +00:00
}
}
2014-02-10 01:10:30 +00:00
2017-05-20 15:38:03 +00:00
Ref < InputEventMouseMotion > mm = p_gui_input ;
2014-02-10 01:10:30 +00:00
2017-05-20 15:38:03 +00:00
if ( mm . is_valid ( ) ) {
if ( drag_touching & & ! drag_touching_deaccel ) {
Vector2 motion = Vector2 ( mm - > get_relative ( ) . x , mm - > get_relative ( ) . y ) ;
drag_accum - = motion ;
2018-02-27 15:37:20 +00:00
2018-09-26 14:35:32 +00:00
if ( beyond_deadzone | | ( scroll_h & & Math : : abs ( drag_accum . x ) > deadzone ) | | ( scroll_v & & Math : : abs ( drag_accum . y ) > deadzone ) ) {
2018-02-27 15:37:20 +00:00
if ( ! beyond_deadzone ) {
propagate_notification ( NOTIFICATION_SCROLL_BEGIN ) ;
emit_signal ( " scroll_started " ) ;
beyond_deadzone = true ;
// resetting drag_accum here ensures smooth scrolling after reaching deadzone
drag_accum = - motion ;
}
Vector2 diff = drag_from + drag_accum ;
2020-05-14 14:41:43 +00:00
if ( scroll_h ) {
2018-02-27 15:37:20 +00:00
h_scroll - > set_value ( diff . x ) ;
2020-05-14 14:41:43 +00:00
} else {
2018-02-27 15:37:20 +00:00
drag_accum . x = 0 ;
2020-05-14 14:41:43 +00:00
}
if ( scroll_v ) {
2018-02-27 15:37:20 +00:00
v_scroll - > set_value ( diff . y ) ;
2020-05-14 14:41:43 +00:00
} else {
2018-02-27 15:37:20 +00:00
drag_accum . y = 0 ;
2020-05-14 14:41:43 +00:00
}
2018-02-27 15:37:20 +00:00
time_since_motion = 0 ;
}
2017-05-20 15:38:03 +00:00
}
2014-02-10 01:10:30 +00:00
}
2017-11-01 20:49:39 +00:00
Ref < InputEventPanGesture > pan_gesture = p_gui_input ;
if ( pan_gesture . is_valid ( ) ) {
if ( h_scroll - > is_visible_in_tree ( ) ) {
h_scroll - > set_value ( h_scroll - > get_value ( ) + h_scroll - > get_page ( ) * pan_gesture - > get_delta ( ) . x / 8 ) ;
}
if ( v_scroll - > is_visible_in_tree ( ) ) {
v_scroll - > set_value ( v_scroll - > get_value ( ) + v_scroll - > get_page ( ) * pan_gesture - > get_delta ( ) . y / 8 ) ;
}
}
2019-05-21 16:53:29 +00:00
2020-05-14 14:41:43 +00:00
if ( v_scroll - > get_value ( ) ! = prev_v_scroll | | h_scroll - > get_value ( ) ! = prev_h_scroll ) {
2019-05-21 16:53:29 +00:00
accept_event ( ) ; //accept event if scroll changed
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
}
2020-01-16 03:36:31 +00:00
void ScrollContainer : : _update_scrollbar_position ( ) {
2020-09-03 11:22:16 +00:00
if ( ! _updating_scrollbars ) {
return ;
}
2020-01-16 03:36:31 +00:00
Size2 hmin = h_scroll - > get_combined_minimum_size ( ) ;
Size2 vmin = v_scroll - > get_combined_minimum_size ( ) ;
2020-12-22 16:24:29 +00: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 03:36:31 +00:00
2020-12-22 16:24:29 +00: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 03:36:31 +00:00
h_scroll - > raise ( ) ;
v_scroll - > raise ( ) ;
2020-09-03 11:22:16 +00:00
_updating_scrollbars = false ;
2020-01-16 03:36:31 +00:00
}
2019-12-11 13:29:36 +00:00
void ScrollContainer : : _ensure_focused_visible ( Control * p_control ) {
2019-12-23 23:03:24 +00:00
if ( ! follow_focus ) {
return ;
}
2019-12-11 13:29:36 +00:00
if ( is_a_parent_of ( p_control ) ) {
2019-12-16 23:51:07 +00:00
Rect2 global_rect = get_global_rect ( ) ;
Rect2 other_rect = p_control - > get_global_rect ( ) ;
2019-12-11 13:29:36 +00:00
float right_margin = 0 ;
if ( v_scroll - > is_visible ( ) ) {
right_margin + = v_scroll - > get_size ( ) . x ;
}
float bottom_margin = 0 ;
if ( h_scroll - > is_visible ( ) ) {
bottom_margin + = h_scroll - > get_size ( ) . y ;
}
2019-12-16 23:51:07 +00:00
float diff = 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_v_scroll ( get_v_scroll ( ) + ( diff - global_rect . position . y ) ) ;
2020-09-03 11:22:16 +00:00
if ( is_layout_rtl ( ) ) {
diff = MAX ( MIN ( other_rect . position . x , global_rect . position . x ) , other_rect . position . x + other_rect . size . x - global_rect . size . x ) ;
} else {
diff = MAX ( MIN ( other_rect . position . x , global_rect . position . x ) , other_rect . position . x + other_rect . size . x - global_rect . size . x + right_margin ) ;
}
2019-12-16 23:51:07 +00:00
set_h_scroll ( get_h_scroll ( ) + ( diff - global_rect . position . x ) ) ;
2019-12-11 13:29:36 +00:00
}
}
2014-02-10 01:10:30 +00:00
void ScrollContainer : : _notification ( int p_what ) {
2020-09-03 11:22:16 +00:00
if ( p_what = = NOTIFICATION_ENTER_TREE | | p_what = = NOTIFICATION_THEME_CHANGED | | p_what = = NOTIFICATION_LAYOUT_DIRECTION_CHANGED | | p_what = = NOTIFICATION_TRANSLATION_CHANGED ) {
_updating_scrollbars = true ;
2020-01-16 03:36:31 +00:00
call_deferred ( " _update_scrollbar_position " ) ;
2014-02-10 01:10:30 +00:00
} ;
2019-12-11 13:29:36 +00:00
if ( p_what = = NOTIFICATION_READY ) {
2020-02-21 17:28:45 +00:00
get_viewport ( ) - > connect ( " gui_focus_changed " , callable_mp ( this , & ScrollContainer : : _ensure_focused_visible ) ) ;
2019-12-11 13:29:36 +00:00
}
2017-03-05 15:44:50 +00:00
if ( p_what = = NOTIFICATION_SORT_CHILDREN ) {
2014-02-10 01:10:30 +00:00
child_max_size = Size2 ( 0 , 0 ) ;
Size2 size = get_size ( ) ;
2018-05-15 20:12:35 +00:00
Point2 ofs ;
2020-03-12 12:37:40 +00:00
Ref < StyleBox > sb = get_theme_stylebox ( " bg " ) ;
2018-05-15 20:12:35 +00:00
size - = sb - > get_minimum_size ( ) ;
ofs + = sb - > get_offset ( ) ;
2020-09-03 11:22:16 +00:00
bool rtl = is_layout_rtl ( ) ;
2018-05-15 20:12:35 +00:00
2020-05-14 14:41:43 +00:00
if ( h_scroll - > is_visible_in_tree ( ) & & h_scroll - > get_parent ( ) = = this ) { //scrolls may have been moved out for reasons
2017-03-05 15:44:50 +00:00
size . y - = h_scroll - > get_minimum_size ( ) . y ;
2020-05-14 14:41:43 +00:00
}
2016-03-12 13:44:12 +00:00
2020-05-14 14:41:43 +00:00
if ( v_scroll - > is_visible_in_tree ( ) & & v_scroll - > get_parent ( ) = = this ) { //scrolls may have been moved out for reasons
2018-11-16 01:47:43 +00:00
size . x - = v_scroll - > get_minimum_size ( ) . x ;
2020-05-14 14:41:43 +00:00
}
2016-03-12 13:44:12 +00:00
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < get_child_count ( ) ; i + + ) {
2017-08-24 20:58:51 +00:00
Control * c = Object : : cast_to < Control > ( get_child ( i ) ) ;
2020-05-14 14:41:43 +00:00
if ( ! c ) {
2014-02-10 01:10:30 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2020-10-01 07:17:33 +00:00
if ( c - > is_set_as_top_level ( ) ) {
2014-02-10 01:10:30 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
if ( c = = h_scroll | | c = = v_scroll ) {
2014-02-10 01:10:30 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
Size2 minsize = c - > get_combined_minimum_size ( ) ;
child_max_size . x = MAX ( child_max_size . x , minsize . x ) ;
child_max_size . y = MAX ( child_max_size . y , minsize . y ) ;
2017-03-05 15:44:50 +00:00
Rect2 r = Rect2 ( - scroll , minsize ) ;
if ( ! scroll_h | | ( ! h_scroll - > is_visible_in_tree ( ) & & c - > get_h_size_flags ( ) & SIZE_EXPAND ) ) {
2017-06-03 22:25:13 +00:00
r . position . x = 0 ;
2020-05-14 14:41:43 +00:00
if ( c - > get_h_size_flags ( ) & SIZE_EXPAND ) {
2017-03-05 15:44:50 +00:00
r . size . width = MAX ( size . width , minsize . width ) ;
2020-05-14 14:41:43 +00:00
} else {
2017-03-05 15:44:50 +00:00
r . size . width = minsize . width ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
if ( ! scroll_v | | ( ! v_scroll - > is_visible_in_tree ( ) & & c - > get_v_size_flags ( ) & SIZE_EXPAND ) ) {
2017-06-03 22:25:13 +00:00
r . position . y = 0 ;
2020-05-14 14:41:43 +00:00
if ( c - > get_v_size_flags ( ) & SIZE_EXPAND ) {
2017-03-05 15:44:50 +00:00
r . size . height = MAX ( size . height , minsize . height ) ;
2020-05-14 14:41:43 +00:00
} else {
2017-03-05 15:44:50 +00:00
r . size . height = minsize . height ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
}
2018-05-15 20:12:35 +00:00
r . position + = ofs ;
2020-09-03 11:22:16 +00:00
if ( rtl & & v_scroll - > is_visible_in_tree ( ) & & v_scroll - > get_parent ( ) = = this ) {
r . position . x + = v_scroll - > get_minimum_size ( ) . x ;
}
2020-12-17 11:22:04 +00:00
r . position = r . position . floor ( ) ;
2017-03-05 15:44:50 +00:00
fit_child_in_rect ( c , r ) ;
2014-02-10 01:10:30 +00:00
}
2020-01-14 21:19:12 +00:00
2014-02-10 01:10:30 +00:00
update ( ) ;
} ;
if ( p_what = = NOTIFICATION_DRAW ) {
2020-03-12 12:37:40 +00:00
Ref < StyleBox > sb = get_theme_stylebox ( " bg " ) ;
2018-05-15 20:12:35 +00:00
draw_style_box ( sb , Rect2 ( Vector2 ( ) , get_size ( ) ) ) ;
2014-02-10 01:10:30 +00:00
update_scrollbars ( ) ;
}
2018-04-11 07:28:14 +00:00
if ( p_what = = NOTIFICATION_INTERNAL_PHYSICS_PROCESS ) {
2017-03-05 15:44:50 +00:00
if ( drag_touching ) {
2014-02-10 01:10:30 +00:00
if ( drag_touching_deaccel ) {
2017-03-05 15:44:50 +00:00
Vector2 pos = Vector2 ( h_scroll - > get_value ( ) , v_scroll - > get_value ( ) ) ;
2017-09-30 14:19:07 +00:00
pos + = drag_speed * get_physics_process_delta_time ( ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
bool turnoff_h = false ;
bool turnoff_v = false ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( pos . x < 0 ) {
pos . x = 0 ;
turnoff_h = true ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
if ( pos . x > ( h_scroll - > get_max ( ) - h_scroll - > get_page ( ) ) ) {
pos . x = h_scroll - > get_max ( ) - h_scroll - > get_page ( ) ;
turnoff_h = true ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
if ( pos . y < 0 ) {
pos . y = 0 ;
turnoff_v = true ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
if ( pos . y > ( v_scroll - > get_max ( ) - v_scroll - > get_page ( ) ) ) {
pos . y = v_scroll - > get_max ( ) - v_scroll - > get_page ( ) ;
turnoff_v = true ;
2014-02-10 01:10:30 +00:00
}
2020-05-14 14:41:43 +00:00
if ( scroll_h ) {
2017-01-04 04:16:14 +00:00
h_scroll - > set_value ( pos . x ) ;
2020-05-14 14:41:43 +00:00
}
if ( scroll_v ) {
2017-01-04 04:16:14 +00:00
v_scroll - > set_value ( pos . y ) ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
float sgn_x = drag_speed . x < 0 ? - 1 : 1 ;
2014-02-10 01:10:30 +00:00
float val_x = Math : : abs ( drag_speed . x ) ;
2017-09-30 14:19:07 +00:00
val_x - = 1000 * get_physics_process_delta_time ( ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( val_x < 0 ) {
turnoff_h = true ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
float sgn_y = drag_speed . y < 0 ? - 1 : 1 ;
2014-02-10 01:10:30 +00:00
float val_y = Math : : abs ( drag_speed . y ) ;
2017-09-30 14:19:07 +00:00
val_y - = 1000 * get_physics_process_delta_time ( ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
if ( val_y < 0 ) {
turnoff_v = true ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
drag_speed = Vector2 ( sgn_x * val_x , sgn_y * val_y ) ;
2014-02-10 01:10:30 +00:00
if ( turnoff_h & & turnoff_v ) {
2018-02-27 15:37:20 +00:00
_cancel_drag ( ) ;
2014-02-10 01:10:30 +00:00
}
} else {
2017-03-05 15:44:50 +00:00
if ( time_since_motion = = 0 | | time_since_motion > 0.1 ) {
2014-02-10 01:10:30 +00:00
Vector2 diff = drag_accum - last_drag_accum ;
2017-03-05 15:44:50 +00:00
last_drag_accum = drag_accum ;
2017-09-30 14:19:07 +00:00
drag_speed = diff / get_physics_process_delta_time ( ) ;
2014-02-10 01:10:30 +00:00
}
2017-09-30 14:19:07 +00:00
time_since_motion + = get_physics_process_delta_time ( ) ;
2014-02-10 01:10:30 +00:00
}
}
}
} ;
void ScrollContainer : : update_scrollbars ( ) {
Size2 size = get_size ( ) ;
2020-03-12 12:37:40 +00:00
Ref < StyleBox > sb = get_theme_stylebox ( " bg " ) ;
2018-05-15 20:12:35 +00:00
size - = sb - > get_minimum_size ( ) ;
2014-02-10 01:10:30 +00:00
2018-08-28 13:41:33 +00:00
Size2 hmin ;
Size2 vmin ;
2020-01-14 21:19:12 +00:00
if ( scroll_h ) {
hmin = h_scroll - > get_combined_minimum_size ( ) ;
}
if ( scroll_v ) {
vmin = v_scroll - > get_combined_minimum_size ( ) ;
}
2014-02-10 01:10:30 +00:00
Size2 min = child_max_size ;
2020-01-14 21:19:12 +00:00
bool hide_scroll_v = ! scroll_v | | min . height < = size . height ;
bool hide_scroll_h = ! scroll_h | | min . width < = size . width ;
2019-09-01 16:31:32 +00:00
2020-11-24 20:30:55 +00:00
v_scroll - > set_max ( min . height ) ;
2019-09-01 16:31:32 +00:00
if ( hide_scroll_v ) {
2020-12-01 02:52:37 +00:00
v_scroll - > set_page ( size . height ) ;
2014-02-10 01:10:30 +00:00
v_scroll - > hide ( ) ;
2017-03-05 15:44:50 +00:00
scroll . y = 0 ;
2014-02-10 01:10:30 +00:00
} else {
v_scroll - > show ( ) ;
2019-09-01 16:31:32 +00:00
if ( hide_scroll_h ) {
v_scroll - > set_page ( size . height ) ;
} else {
v_scroll - > set_page ( size . height - hmin . height ) ;
}
2017-03-05 15:44:50 +00:00
scroll . y = v_scroll - > get_value ( ) ;
2014-02-10 01:10:30 +00:00
}
2020-11-24 20:30:55 +00:00
h_scroll - > set_max ( min . width ) ;
2019-09-01 16:31:32 +00:00
if ( hide_scroll_h ) {
2020-12-01 02:52:37 +00:00
h_scroll - > set_page ( size . width ) ;
2014-02-10 01:10:30 +00:00
h_scroll - > hide ( ) ;
2017-03-05 15:44:50 +00:00
scroll . x = 0 ;
2014-02-10 01:10:30 +00:00
} else {
h_scroll - > show ( ) ;
2019-09-01 16:31:32 +00:00
if ( hide_scroll_v ) {
h_scroll - > set_page ( size . width ) ;
} else {
h_scroll - > set_page ( size . width - vmin . width ) ;
}
2017-03-05 15:44:50 +00:00
scroll . x = h_scroll - > get_value ( ) ;
2014-02-10 01:10:30 +00:00
}
2020-01-14 21:19:12 +00:00
// Avoid scrollbar overlapping.
2020-12-22 16:24:29 +00:00
h_scroll - > set_anchor_and_offset ( SIDE_RIGHT , ANCHOR_END , hide_scroll_v ? 0 : - vmin . width ) ;
v_scroll - > set_anchor_and_offset ( SIDE_BOTTOM , ANCHOR_END , hide_scroll_h ? 0 : - hmin . height ) ;
2014-02-10 01:10:30 +00:00
}
void ScrollContainer : : _scroll_moved ( float ) {
2017-03-05 15:44:50 +00:00
scroll . x = h_scroll - > get_value ( ) ;
scroll . y = v_scroll - > get_value ( ) ;
2014-02-10 01:10:30 +00:00
queue_sort ( ) ;
update ( ) ;
} ;
void ScrollContainer : : set_enable_h_scroll ( bool p_enable ) {
2020-01-16 01:38:32 +00:00
if ( scroll_h = = p_enable ) {
return ;
}
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
scroll_h = p_enable ;
2020-01-16 01:38:32 +00:00
minimum_size_changed ( ) ;
2014-02-10 01:10:30 +00:00
queue_sort ( ) ;
}
2017-03-05 15:44:50 +00:00
bool ScrollContainer : : is_h_scroll_enabled ( ) const {
2014-02-10 01:10:30 +00:00
return scroll_h ;
}
void ScrollContainer : : set_enable_v_scroll ( bool p_enable ) {
2020-01-16 01:38:32 +00:00
if ( scroll_v = = p_enable ) {
return ;
}
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
scroll_v = p_enable ;
2020-01-16 01:38:32 +00:00
minimum_size_changed ( ) ;
2014-02-10 01:10:30 +00:00
queue_sort ( ) ;
}
2017-03-05 15:44:50 +00:00
bool ScrollContainer : : is_v_scroll_enabled ( ) const {
2014-02-10 01:10:30 +00:00
return scroll_v ;
}
int ScrollContainer : : get_v_scroll ( ) const {
2017-01-04 04:16:14 +00:00
return v_scroll - > get_value ( ) ;
2014-02-10 01:10:30 +00:00
}
2020-05-14 12:29:06 +00:00
2014-02-10 01:10:30 +00:00
void ScrollContainer : : set_v_scroll ( int p_pos ) {
2017-01-04 04:16:14 +00:00
v_scroll - > set_value ( p_pos ) ;
2014-02-10 01:10:30 +00:00
_cancel_drag ( ) ;
}
int ScrollContainer : : get_h_scroll ( ) const {
2017-01-04 04:16:14 +00:00
return h_scroll - > get_value ( ) ;
2014-02-10 01:10:30 +00:00
}
2020-05-14 12:29:06 +00:00
2014-02-10 01:10:30 +00:00
void ScrollContainer : : set_h_scroll ( int p_pos ) {
2017-01-04 04:16:14 +00:00
h_scroll - > set_value ( p_pos ) ;
2014-02-10 01:10:30 +00:00
_cancel_drag ( ) ;
}
2018-02-27 15:37:20 +00:00
int ScrollContainer : : get_deadzone ( ) const {
return deadzone ;
}
void ScrollContainer : : set_deadzone ( int p_deadzone ) {
deadzone = p_deadzone ;
}
2019-12-23 23:03:24 +00:00
bool ScrollContainer : : is_following_focus ( ) const {
return follow_focus ;
}
2019-12-30 22:42:39 +00:00
void ScrollContainer : : set_follow_focus ( bool p_follow ) {
2019-12-23 23:03:24 +00:00
follow_focus = p_follow ;
}
2017-01-09 18:50:08 +00:00
String ScrollContainer : : get_configuration_warning ( ) const {
2020-05-14 20:59:27 +00:00
String warning = Container : : get_configuration_warning ( ) ;
2017-03-05 15:44:50 +00:00
int found = 0 ;
2017-01-09 18:50:08 +00:00
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < get_child_count ( ) ; i + + ) {
2017-08-24 20:58:51 +00:00
Control * c = Object : : cast_to < Control > ( get_child ( i ) ) ;
2020-05-14 14:41:43 +00:00
if ( ! c ) {
2017-01-09 18:50:08 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2020-10-01 07:17:33 +00:00
if ( c - > is_set_as_top_level ( ) ) {
2017-01-09 18:50:08 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
if ( c = = h_scroll | | c = = v_scroll ) {
2017-01-09 18:50:08 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2017-01-09 18:50:08 +00:00
found + + ;
}
2020-05-14 14:41:43 +00:00
if ( found ! = 1 ) {
2020-12-15 12:04:21 +00:00
if ( ! warning . is_empty ( ) ) {
2020-05-14 20:59:27 +00:00
warning + = " \n \n " ;
}
warning + = TTR ( " 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 14:41:43 +00:00
}
2020-05-14 20:59:27 +00:00
return warning ;
2017-01-09 18:50:08 +00:00
}
2018-06-07 15:46:14 +00:00
HScrollBar * ScrollContainer : : get_h_scrollbar ( ) {
return h_scroll ;
}
VScrollBar * ScrollContainer : : get_v_scrollbar ( ) {
return v_scroll ;
}
2014-02-10 01:10:30 +00:00
void ScrollContainer : : _bind_methods ( ) {
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " _gui_input " ) , & ScrollContainer : : _gui_input ) ;
ClassDB : : bind_method ( D_METHOD ( " set_enable_h_scroll " , " enable " ) , & ScrollContainer : : set_enable_h_scroll ) ;
ClassDB : : bind_method ( D_METHOD ( " is_h_scroll_enabled " ) , & ScrollContainer : : is_h_scroll_enabled ) ;
ClassDB : : bind_method ( D_METHOD ( " set_enable_v_scroll " , " enable " ) , & ScrollContainer : : set_enable_v_scroll ) ;
ClassDB : : bind_method ( D_METHOD ( " is_v_scroll_enabled " ) , & ScrollContainer : : is_v_scroll_enabled ) ;
2020-01-16 03:36:31 +00:00
ClassDB : : bind_method ( D_METHOD ( " _update_scrollbar_position " ) , & ScrollContainer : : _update_scrollbar_position ) ;
2018-01-17 09:43:23 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_h_scroll " , " value " ) , & ScrollContainer : : set_h_scroll ) ;
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_h_scroll " ) , & ScrollContainer : : get_h_scroll ) ;
2018-01-17 09:43:23 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_v_scroll " , " value " ) , & ScrollContainer : : set_v_scroll ) ;
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_v_scroll " ) , & ScrollContainer : : get_v_scroll ) ;
2018-02-27 15:37:20 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_deadzone " , " deadzone " ) , & ScrollContainer : : set_deadzone ) ;
ClassDB : : bind_method ( D_METHOD ( " get_deadzone " ) , & ScrollContainer : : get_deadzone ) ;
2019-12-23 23:03:24 +00: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 15:37:20 +00:00
2018-06-07 15:46:14 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_h_scrollbar " ) , & ScrollContainer : : get_h_scrollbar ) ;
ClassDB : : bind_method ( D_METHOD ( " get_v_scrollbar " ) , & ScrollContainer : : get_v_scrollbar ) ;
2018-02-27 15:37:20 +00:00
ADD_SIGNAL ( MethodInfo ( " scroll_started " ) ) ;
ADD_SIGNAL ( MethodInfo ( " scroll_ended " ) ) ;
2017-03-05 15:44:50 +00:00
2019-12-23 23:03:24 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " follow_focus " ) , " set_follow_focus " , " is_following_focus " ) ;
2017-03-05 15:44:50 +00:00
ADD_GROUP ( " Scroll " , " scroll_ " ) ;
2018-01-11 22:35:12 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " scroll_horizontal_enabled " ) , " set_enable_h_scroll " , " is_h_scroll_enabled " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " scroll_horizontal " ) , " set_h_scroll " , " get_h_scroll " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " scroll_vertical_enabled " ) , " set_enable_v_scroll " , " is_v_scroll_enabled " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " scroll_vertical " ) , " set_v_scroll " , " get_v_scroll " ) ;
2018-02-27 15:37:20 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " scroll_deadzone " ) , " set_deadzone " , " get_deadzone " ) ;
GLOBAL_DEF ( " gui/common/default_scroll_deadzone " , 0 ) ;
2014-02-10 01:10:30 +00:00
} ;
ScrollContainer : : ScrollContainer ( ) {
h_scroll = memnew ( HScrollBar ) ;
h_scroll - > set_name ( " _h_scroll " ) ;
add_child ( h_scroll ) ;
2020-02-21 17:28:45 +00:00
h_scroll - > connect ( " value_changed " , callable_mp ( this , & ScrollContainer : : _scroll_moved ) ) ;
2014-02-10 01:10:30 +00:00
v_scroll = memnew ( VScrollBar ) ;
v_scroll - > set_name ( " _v_scroll " ) ;
add_child ( v_scroll ) ;
2020-02-21 17:28:45 +00:00
v_scroll - > connect ( " value_changed " , callable_mp ( this , & ScrollContainer : : _scroll_moved ) ) ;
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
drag_speed = Vector2 ( ) ;
drag_touching = false ;
drag_touching_deaccel = false ;
2018-02-27 15:37:20 +00:00
beyond_deadzone = false ;
2017-03-05 15:44:50 +00:00
scroll_h = true ;
scroll_v = true ;
2014-02-10 01:10:30 +00:00
2018-02-27 15:37:20 +00:00
deadzone = GLOBAL_GET ( " gui/common/default_scroll_deadzone " ) ;
2019-12-23 23:03:24 +00:00
follow_focus = false ;
2018-02-27 15:37:20 +00:00
2017-01-09 18:50:08 +00:00
set_clip_contents ( true ) ;
2014-02-10 01:10:30 +00:00
} ;