From 45a564f4f83bf707d782af2c13f2e3e10e7b42b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pa=CC=84vels=20Nadtoc=CC=8Cajevs?= <7645683+bruvzg@users.noreply.github.com> Date: Tue, 8 Jul 2025 11:12:23 +0300 Subject: [PATCH] [RTL] Add option to scroll follow visible characters. --- doc/classes/RichTextLabel.xml | 3 +++ scene/gui/rich_text_label.cpp | 34 ++++++++++++++++++++++++++++++++++ scene/gui/rich_text_label.h | 6 ++++++ 3 files changed, 43 insertions(+) diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml index c79880a5ceb..3479dabb96a 100644 --- a/doc/classes/RichTextLabel.xml +++ b/doc/classes/RichTextLabel.xml @@ -729,6 +729,9 @@ If [code]true[/code], the window scrolls down to display new content automatically. + + If [code]true[/code], the window scrolls to display the last visible line when [member visible_characters] or [member visible_ratio] is changed. + If [code]true[/code], the label allows text selection. diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 9e5d253bb99..78bcfe23123 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -2516,6 +2516,9 @@ void RichTextLabel::_notification(int p_what) { ofs.y += main->lines[from_line].text_buf->get_size().y + (main->lines[from_line].text_buf->get_line_count() - 1) * (theme_cache.line_separation + vsep) + (theme_cache.paragraph_separation + vsep); from_line++; } + if (scroll_follow_visible_characters && scroll_active) { + vscroll->set_visible(follow_vc_pos > 0); + } if (has_focus() && get_tree()->is_accessibility_enabled()) { RID ae; if (keyboard_focus_frame && keyboard_focus_item) { @@ -5019,6 +5022,31 @@ bool RichTextLabel::is_scroll_following() const { return scroll_follow; } +void RichTextLabel::_update_follow_vc() { + if (!scroll_follow_visible_characters) { + return; + } + int vc = (visible_characters < 0 ? get_total_character_count() : MIN(visible_characters, get_total_character_count())) - 1; + int voff = get_character_line(vc) + 1; + if (voff <= get_line_count() - 1) { + follow_vc_pos = get_line_offset(voff) - _get_text_rect().size.y; + } else { + follow_vc_pos = vscroll->get_max(); + } + vscroll->scroll_to(follow_vc_pos); +} + +void RichTextLabel::set_scroll_follow_visible_characters(bool p_follow) { + if (scroll_follow_visible_characters != p_follow) { + scroll_follow_visible_characters = p_follow; + _update_follow_vc(); + } +} + +bool RichTextLabel::is_scroll_following_visible_characters() const { + return scroll_follow_visible_characters; +} + void RichTextLabel::parse_bbcode(const String &p_bbcode) { clear(); append_text(p_bbcode); @@ -7207,6 +7235,7 @@ void RichTextLabel::set_visible_ratio(float p_ratio) { total_height = _calculate_line_vertical_offset(main->lines[i]); } } + _update_follow_vc(); queue_redraw(); } } @@ -7397,6 +7426,9 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("set_scroll_active", "active"), &RichTextLabel::set_scroll_active); ClassDB::bind_method(D_METHOD("is_scroll_active"), &RichTextLabel::is_scroll_active); + ClassDB::bind_method(D_METHOD("set_scroll_follow_visible_characters", "follow"), &RichTextLabel::set_scroll_follow_visible_characters); + ClassDB::bind_method(D_METHOD("is_scroll_following_visible_characters"), &RichTextLabel::is_scroll_following_visible_characters); + ClassDB::bind_method(D_METHOD("set_scroll_follow", "follow"), &RichTextLabel::set_scroll_follow); ClassDB::bind_method(D_METHOD("is_scroll_following"), &RichTextLabel::is_scroll_following); @@ -7501,6 +7533,7 @@ void RichTextLabel::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fit_content"), "set_fit_content", "is_fit_content_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_active"), "set_scroll_active", "is_scroll_active"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_following"), "set_scroll_follow", "is_scroll_following"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_following_visible_characters"), "set_scroll_follow_visible_characters", "is_scroll_following_visible_characters"); ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_trim_flags", PROPERTY_HINT_FLAGS, vformat("Trim Spaces After Break:%d,Trim Spaces Before Break:%d", TextServer::BREAK_TRIM_START_EDGE_SPACES, TextServer::BREAK_TRIM_END_EDGE_SPACES)), "set_autowrap_trim_flags", "get_autowrap_trim_flags"); ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_size", PROPERTY_HINT_RANGE, "0,24,1"), "set_tab_size", "get_tab_size"); @@ -7682,6 +7715,7 @@ void RichTextLabel::set_visible_characters(int p_visible) { total_height = _calculate_line_vertical_offset(main->lines[i]); } } + _update_follow_vc(); queue_redraw(); } } diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index e84d3d2c508..b90df07d42d 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -527,6 +527,8 @@ private: bool scroll_visible = false; bool scroll_follow = false; + bool scroll_follow_visible_characters = false; + int follow_vc_pos = 0; bool scroll_following = false; bool scroll_active = true; int scroll_w = 0; @@ -554,6 +556,7 @@ private: HashMap ac_element_bounds_cache; + void _update_follow_vc(); void _invalidate_accessibility(); void _invalidate_current_line(ItemFrame *p_frame); @@ -839,6 +842,9 @@ public: void set_scroll_follow(bool p_follow); bool is_scroll_following() const; + void set_scroll_follow_visible_characters(bool p_follow); + bool is_scroll_following_visible_characters() const; + void set_tab_size(int p_spaces); int get_tab_size() const;