From b3e970dde8c07168ef3927b3cd5dd157b403b281 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Thu, 12 Dec 2024 12:06:09 -0500 Subject: [PATCH] Adds `get_selection_line_offset` to `RichTextLabel` This new method allow you to get the line offset of the current selection (returns -1 if nothing is selected.) This is useful if you want to pop up a control or menu above the currently selected text. Previously there was no accurate way to get this information. The logic is moved from the implementation of `scroll_to_selection` verbatim, and that method has been adjusted to avoid repetition. --- doc/classes/RichTextLabel.xml | 6 +++++ scene/gui/rich_text_label.cpp | 48 +++++++++++++++++++++-------------- scene/gui/rich_text_label.h | 1 + 3 files changed, 36 insertions(+), 19 deletions(-) diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml index d75d3043e02..b982808ff0e 100644 --- a/doc/classes/RichTextLabel.xml +++ b/doc/classes/RichTextLabel.xml @@ -190,6 +190,12 @@ Returns the current selection first character index if a selection is active, [code]-1[/code] otherwise. Does not include BBCodes. + + + + Returns the current selection vertical line offset if a selection is active, [code]-1.0[/code] otherwise. + + diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index be54ef7c94e..99a95910475 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -5449,25 +5449,8 @@ void RichTextLabel::append_text(const String &p_bbcode) { } void RichTextLabel::scroll_to_selection() { - if (selection.active && selection.from_frame && selection.from_line >= 0 && selection.from_line < (int)selection.from_frame->lines.size()) { - // Selected frame paragraph offset. - float line_offset = selection.from_frame->lines[selection.from_line].offset.y; - - // Add wrapped line offset. - for (int i = 0; i < selection.from_frame->lines[selection.from_line].text_buf->get_line_count(); i++) { - Vector2i range = selection.from_frame->lines[selection.from_line].text_buf->get_line_range(i); - if (range.x <= selection.from_char && range.y >= selection.from_char) { - break; - } - line_offset += selection.from_frame->lines[selection.from_line].text_buf->get_line_ascent(i) + selection.from_frame->lines[selection.from_line].text_buf->get_line_descent(i) + theme_cache.line_separation; - } - - // Add nested frame (e.g. table cell) offset. - ItemFrame *it = selection.from_frame; - while (it->parent_frame != nullptr) { - line_offset += it->parent_frame->lines[it->line].offset.y; - it = it->parent_frame; - } + float line_offset = get_selection_line_offset(); + if (line_offset != -1.0) { vscroll->set_value(line_offset); } } @@ -5978,6 +5961,32 @@ int RichTextLabel::get_selection_to() const { return selection.to_frame->lines[selection.to_line].char_offset + selection.to_char - 1; } +float RichTextLabel::get_selection_line_offset() const { + if (selection.active && selection.from_frame && selection.from_line >= 0 && selection.from_line < (int)selection.from_frame->lines.size()) { + // Selected frame paragraph offset. + float line_offset = selection.from_frame->lines[selection.from_line].offset.y; + + // Add wrapped line offset. + for (int i = 0; i < selection.from_frame->lines[selection.from_line].text_buf->get_line_count(); i++) { + Vector2i range = selection.from_frame->lines[selection.from_line].text_buf->get_line_range(i); + if (range.x <= selection.from_char && range.y >= selection.from_char) { + break; + } + line_offset += selection.from_frame->lines[selection.from_line].text_buf->get_line_ascent(i) + selection.from_frame->lines[selection.from_line].text_buf->get_line_descent(i) + theme_cache.line_separation; + } + + // Add nested frame (e.g. table cell) offset. + ItemFrame *it = selection.from_frame; + while (it->parent_frame != nullptr) { + line_offset += it->parent_frame->lines[it->line].offset.y; + it = it->parent_frame; + } + return line_offset; + } + + return -1.0; +} + void RichTextLabel::set_text(const String &p_bbcode) { // Allow clearing the tag stack. if (!p_bbcode.is_empty() && text == p_bbcode) { @@ -6412,6 +6421,7 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("get_selection_from"), &RichTextLabel::get_selection_from); ClassDB::bind_method(D_METHOD("get_selection_to"), &RichTextLabel::get_selection_to); + ClassDB::bind_method(D_METHOD("get_selection_line_offset"), &RichTextLabel::get_selection_line_offset); ClassDB::bind_method(D_METHOD("select_all"), &RichTextLabel::select_all); ClassDB::bind_method(D_METHOD("get_selected_text"), &RichTextLabel::get_selected_text); diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index d9aac546ca0..12ae17ad4ad 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -787,6 +787,7 @@ public: bool is_selection_enabled() const; int get_selection_from() const; int get_selection_to() const; + float get_selection_line_offset() const; String get_selected_text() const; void select_all(); void selection_copy();