3232
3333#include " core/config/project_settings.h"
3434#include " scene/gui/panel_container.h"
35+ #include " scene/gui/texture_rect.h"
3536#include " scene/main/window.h"
3637#include " scene/theme/theme_db.h"
3738
@@ -343,6 +344,7 @@ void ScrollContainer::ensure_control_visible(Control *p_control) {
343344
344345void ScrollContainer::_reposition_children () {
345346 update_scrollbars ();
347+ update_scroll_hints ();
346348
347349 Rect2 margins = _get_margins ();
348350 Size2 size = get_size ();
@@ -362,10 +364,7 @@ void ScrollContainer::_reposition_children() {
362364
363365 for (int i = 0 ; i < get_child_count (); i++) {
364366 Control *c = as_sortable_control (get_child (i));
365- if (!c) {
366- continue ;
367- }
368- if (c == h_scroll || c == v_scroll || c == focus_panel) {
367+ if (!c || c->is_internal ()) {
369368 continue ;
370369 }
371370 Size2 minsize = c->get_combined_minimum_size ();
@@ -445,7 +444,10 @@ void ScrollContainer::_notification(int p_what) {
445444 DisplayServer::get_singleton ()->accessibility_update_add_action (ae, DisplayServer::AccessibilityAction::ACTION_SET_SCROLL_OFFSET, callable_mp (this , &ScrollContainer::_accessibility_action_scroll_set));
446445 } break ;
447446
448- case NOTIFICATION_THEME_CHANGED:
447+ case NOTIFICATION_THEME_CHANGED: {
448+ update_scroll_hints ();
449+ [[fallthrough]];
450+ }
449451 case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
450452 case NOTIFICATION_TRANSLATION_CHANGED: {
451453 _updating_scrollbars = true ;
@@ -602,6 +604,48 @@ void ScrollContainer::update_scrollbars() {
602604 callable_mp (this , &ScrollContainer::_update_scrollbar_position).call_deferred ();
603605}
604606
607+ void ScrollContainer::update_scroll_hints () {
608+ Size2 size = get_size ();
609+ Rect2 margins = _get_margins ();
610+ Size2 scroll_size = size - margins.position + margins.size ;
611+
612+ float v_scroll_value = v_scroll->get_value ();
613+ bool v_scroll_below_max = v_scroll_value < (largest_child_min_size.height - scroll_size.height - 1 );
614+ bool show_vertical_hints = v_scroll_value > 1 || v_scroll_below_max;
615+
616+ float h_scroll_value = h_scroll->get_value ();
617+ bool h_scroll_below_max = h_scroll_value < (largest_child_min_size.width - scroll_size.width - 1 );
618+ bool show_horizontal_hints = h_scroll_value > 1 || h_scroll_below_max;
619+
620+ if (show_vertical_hints) {
621+ scroll_hint_top_left->set_texture (theme_cache.scroll_hint_vertical );
622+ scroll_hint_top_left->set_visible (!show_horizontal_hints && (scroll_hint_mode == SCROLL_HINT_MODE_ALL || scroll_hint_mode == SCROLL_HINT_MODE_TOP_AND_LEFT) && v_scroll_value > 1 );
623+ scroll_hint_top_left->set_offsets_preset (PRESET_TOP_WIDE, LayoutPresetMode::PRESET_MODE_MINSIZE);
624+
625+ scroll_hint_bottom_right->set_flip_h (false );
626+ scroll_hint_bottom_right->set_flip_v (true );
627+ scroll_hint_bottom_right->set_texture (theme_cache.scroll_hint_vertical );
628+ scroll_hint_bottom_right->set_visible (!show_horizontal_hints && (scroll_hint_mode == SCROLL_HINT_MODE_ALL || scroll_hint_mode == SCROLL_HINT_MODE_BOTTOM_AND_RIGHT) && v_scroll_below_max);
629+ scroll_hint_bottom_right->set_offsets_preset (PRESET_BOTTOM_WIDE, LayoutPresetMode::PRESET_MODE_MINSIZE);
630+ } else {
631+ bool rtl = is_layout_rtl ();
632+
633+ scroll_hint_top_left->set_texture (theme_cache.scroll_hint_horizontal );
634+ scroll_hint_top_left->set_visible (!show_vertical_hints && (scroll_hint_mode == SCROLL_HINT_MODE_ALL || (rtl ? scroll_hint_mode == SCROLL_HINT_MODE_BOTTOM_AND_RIGHT : scroll_hint_mode == SCROLL_HINT_MODE_TOP_AND_LEFT)) && h_scroll_value > 1 );
635+ scroll_hint_top_left->set_offsets_preset (rtl ? PRESET_RIGHT_WIDE : PRESET_LEFT_WIDE, LayoutPresetMode::PRESET_MODE_MINSIZE);
636+ if (rtl) {
637+ // TODO: Find out why this is even necessary.
638+ scroll_hint_top_left->set_position (Point2 (0 , scroll_hint_top_left->get_position ().y ));
639+ }
640+
641+ scroll_hint_bottom_right->set_flip_h (true );
642+ scroll_hint_bottom_right->set_flip_v (false );
643+ scroll_hint_bottom_right->set_texture (theme_cache.scroll_hint_horizontal );
644+ scroll_hint_bottom_right->set_visible (!show_vertical_hints && (scroll_hint_mode == SCROLL_HINT_MODE_ALL || (rtl ? scroll_hint_mode == SCROLL_HINT_MODE_TOP_AND_LEFT : scroll_hint_mode == SCROLL_HINT_MODE_BOTTOM_AND_RIGHT)) && h_scroll_below_max);
645+ scroll_hint_bottom_right->set_offsets_preset (rtl ? PRESET_LEFT_WIDE : PRESET_RIGHT_WIDE, LayoutPresetMode::PRESET_MODE_MINSIZE);
646+ }
647+ }
648+
605649void ScrollContainer::_scroll_moved (float ) {
606650 queue_sort ();
607651}
@@ -676,6 +720,32 @@ void ScrollContainer::set_deadzone(int p_deadzone) {
676720 deadzone = p_deadzone;
677721}
678722
723+ void ScrollContainer::set_scroll_hint_mode (ScrollHintMode p_mode) {
724+ if (scroll_hint_mode == p_mode) {
725+ return ;
726+ }
727+
728+ scroll_hint_mode = p_mode;
729+ update_scroll_hints ();
730+ }
731+
732+ ScrollContainer::ScrollHintMode ScrollContainer::get_scroll_hint_mode () const {
733+ return scroll_hint_mode;
734+ }
735+
736+ void ScrollContainer::set_tile_scroll_hint (bool p_enable) {
737+ if (tile_scroll_hint != p_enable) {
738+ return ;
739+ }
740+
741+ tile_scroll_hint = p_enable;
742+ update_scroll_hints ();
743+ }
744+
745+ bool ScrollContainer::is_scroll_hint_tiled () {
746+ return tile_scroll_hint;
747+ }
748+
679749bool ScrollContainer::is_following_focus () const {
680750 return follow_focus;
681751}
@@ -691,10 +761,7 @@ PackedStringArray ScrollContainer::get_configuration_warnings() const {
691761
692762 for (int i = 0 ; i < get_child_count (); i++) {
693763 Control *c = as_sortable_control (get_child (i), SortableVisibilityMode::VISIBLE);
694- if (!c) {
695- continue ;
696- }
697- if (c == h_scroll || c == v_scroll || c == focus_panel) {
764+ if (!c || c->is_internal ()) {
698765 continue ;
699766 }
700767
@@ -742,6 +809,12 @@ void ScrollContainer::_bind_methods() {
742809 ClassDB::bind_method (D_METHOD (" set_deadzone" , " deadzone" ), &ScrollContainer::set_deadzone);
743810 ClassDB::bind_method (D_METHOD (" get_deadzone" ), &ScrollContainer::get_deadzone);
744811
812+ ClassDB::bind_method (D_METHOD (" set_scroll_hint_mode" , " scroll_hint_mode" ), &ScrollContainer::set_scroll_hint_mode);
813+ ClassDB::bind_method (D_METHOD (" get_scroll_hint_mode" ), &ScrollContainer::get_scroll_hint_mode);
814+
815+ ClassDB::bind_method (D_METHOD (" set_tile_scroll_hint" , " tile_scroll_hint" ), &ScrollContainer::set_tile_scroll_hint);
816+ ClassDB::bind_method (D_METHOD (" is_scroll_hint_tiled" ), &ScrollContainer::is_scroll_hint_tiled);
817+
745818 ClassDB::bind_method (D_METHOD (" set_follow_focus" , " enabled" ), &ScrollContainer::set_follow_focus);
746819 ClassDB::bind_method (D_METHOD (" is_following_focus" ), &ScrollContainer::is_following_focus);
747820
@@ -758,7 +831,7 @@ void ScrollContainer::_bind_methods() {
758831 ADD_PROPERTY (PropertyInfo (Variant::BOOL, " follow_focus" ), " set_follow_focus" , " is_following_focus" );
759832 ADD_PROPERTY (PropertyInfo (Variant::BOOL, " draw_focus_border" ), " set_draw_focus_border" , " get_draw_focus_border" );
760833
761- ADD_GROUP (" Scroll " , " scroll_ " );
834+ ADD_GROUP (" Scrollbar " , " " );
762835 ADD_PROPERTY (PropertyInfo (Variant::INT, " scroll_horizontal" , PROPERTY_HINT_NONE, " suffix:px" ), " set_h_scroll" , " get_h_scroll" );
763836 ADD_PROPERTY (PropertyInfo (Variant::INT, " scroll_vertical" , PROPERTY_HINT_NONE, " suffix:px" ), " set_v_scroll" , " get_v_scroll" );
764837 ADD_PROPERTY (PropertyInfo (Variant::FLOAT, " scroll_horizontal_custom_step" , PROPERTY_HINT_RANGE, " -1,4096,suffix:px" ), " set_horizontal_custom_step" , " get_horizontal_custom_step" );
@@ -767,18 +840,30 @@ void ScrollContainer::_bind_methods() {
767840 ADD_PROPERTY (PropertyInfo (Variant::INT, " vertical_scroll_mode" , PROPERTY_HINT_ENUM, " Disabled,Auto,Always Show,Never Show,Reserve" ), " set_vertical_scroll_mode" , " get_vertical_scroll_mode" );
768841 ADD_PROPERTY (PropertyInfo (Variant::INT, " scroll_deadzone" ), " set_deadzone" , " get_deadzone" );
769842
843+ ADD_GROUP (" Scroll Hint" , " " );
844+ ADD_PROPERTY (PropertyInfo (Variant::INT, " scroll_hint_mode" , PROPERTY_HINT_ENUM, " Disabled,All,Top and Left,Bottom and Right" ), " set_scroll_hint_mode" , " get_scroll_hint_mode" );
845+ ADD_PROPERTY (PropertyInfo (Variant::BOOL, " tile_scroll_hint" ), " set_tile_scroll_hint" , " is_scroll_hint_tiled" );
846+
770847 BIND_ENUM_CONSTANT (SCROLL_MODE_DISABLED);
771848 BIND_ENUM_CONSTANT (SCROLL_MODE_AUTO);
772849 BIND_ENUM_CONSTANT (SCROLL_MODE_SHOW_ALWAYS);
773850 BIND_ENUM_CONSTANT (SCROLL_MODE_SHOW_NEVER);
774851 BIND_ENUM_CONSTANT (SCROLL_MODE_RESERVE);
775852
853+ BIND_ENUM_CONSTANT (SCROLL_HINT_MODE_DISABLED);
854+ BIND_ENUM_CONSTANT (SCROLL_HINT_MODE_ALL);
855+ BIND_ENUM_CONSTANT (SCROLL_HINT_MODE_TOP_AND_LEFT);
856+ BIND_ENUM_CONSTANT (SCROLL_HINT_MODE_BOTTOM_AND_RIGHT);
857+
776858 BIND_THEME_ITEM (Theme::DATA_TYPE_CONSTANT, ScrollContainer, scrollbar_h_separation);
777859 BIND_THEME_ITEM (Theme::DATA_TYPE_CONSTANT, ScrollContainer, scrollbar_v_separation);
778860
779861 BIND_THEME_ITEM_CUSTOM (Theme::DATA_TYPE_STYLEBOX, ScrollContainer, panel_style, " panel" );
780862 BIND_THEME_ITEM_CUSTOM (Theme::DATA_TYPE_STYLEBOX, ScrollContainer, focus_style, " focus" );
781863
864+ BIND_THEME_ITEM (Theme::DATA_TYPE_ICON, ScrollContainer, scroll_hint_vertical);
865+ BIND_THEME_ITEM (Theme::DATA_TYPE_ICON, ScrollContainer, scroll_hint_horizontal);
866+
782867 GLOBAL_DEF (" gui/common/default_scroll_deadzone" , 0 );
783868}
784869
@@ -802,6 +887,16 @@ bool ScrollContainer::child_has_focus() {
802887}
803888
804889ScrollContainer::ScrollContainer () {
890+ scroll_hint_top_left = memnew (TextureRect);
891+ scroll_hint_top_left->set_mouse_filter (MOUSE_FILTER_IGNORE);
892+ scroll_hint_top_left->hide ();
893+ add_child (scroll_hint_top_left, false , INTERNAL_MODE_BACK);
894+
895+ scroll_hint_bottom_right = memnew (TextureRect);
896+ scroll_hint_bottom_right->set_mouse_filter (MOUSE_FILTER_IGNORE);
897+ scroll_hint_bottom_right->hide ();
898+ add_child (scroll_hint_bottom_right, false , INTERNAL_MODE_BACK);
899+
805900 h_scroll = memnew (HScrollBar);
806901 h_scroll->set_name (" _h_scroll" );
807902 add_child (h_scroll, false , INTERNAL_MODE_BACK);
0 commit comments