[RTL] Add method to get visible content bounding box.

This commit is contained in:
Pāvels Nadtočajevs 2025-07-10 09:56:23 +03:00
parent d7cc121e64
commit 406a22d0da
No known key found for this signature in database
GPG key ID: 8413210218EF35D2
3 changed files with 58 additions and 0 deletions

View file

@ -104,6 +104,7 @@
<return type="int" />
<description>
Returns the height of the content.
[b]Note:[/b] This method always returns the full content size, and is not affected by [member visible_ratio] and [member visible_characters]. To get the visible content size, use [method get_visible_content_rect].
[b]Note:[/b] If [member threaded] is enabled, this method returns a value for the loaded part of the document. Use [method is_finished] or [signal finished] to determine whether document is fully loaded.
</description>
</method>
@ -111,6 +112,7 @@
<return type="int" />
<description>
Returns the width of the content.
[b]Note:[/b] This method always returns the full content size, and is not affected by [member visible_ratio] and [member visible_characters]. To get the visible content size, use [method get_visible_content_rect].
[b]Note:[/b] If [member threaded] is enabled, this method returns a value for the loaded part of the document. Use [method is_finished] or [signal finished] to determine whether document is fully loaded.
</description>
</method>
@ -257,10 +259,44 @@
[b]Warning:[/b] This is a required internal node, removing and freeing it may cause a crash. If you wish to hide it or any of its children, use their [member CanvasItem.visible] property.
</description>
</method>
<method name="get_visible_content_rect" qualifiers="const">
<return type="Rect2i" />
<description>
Returns the bounding rectangle of the visible content.
[b]Note:[/b] This method returns a correct value only after the label has been drawn.
[codeblocks]
[gdscript]
extends RichTextLabel
@export var background_panel: Panel
func _ready():
await draw
background_panel.position = get_visible_content_rect().position
background_panel.size = get_visible_content_rect().size
[/gdscript]
[csharp]
public partial class TestLabel : RichTextLabel
{
[Export]
public Panel BackgroundPanel { get; set; }
public override async void _Ready()
{
await ToSignal(this, Control.SignalName.Draw);
BackgroundGPanel.Position = GetVisibleContentRect().Position;
BackgroundPanel.Size = GetVisibleContentRect().Size;
}
}
[/csharp]
[/codeblocks]
</description>
</method>
<method name="get_visible_line_count" qualifiers="const">
<return type="int" />
<description>
Returns the number of visible lines.
[b]Note:[/b] This method returns a correct value only after the label has been drawn.
[b]Note:[/b] If [member threaded] is enabled, this method returns a value for the loaded part of the document. Use [method is_finished] or [signal finished] to determine whether document is fully loaded.
</description>
</method>
@ -268,6 +304,7 @@
<return type="int" />
<description>
Returns the number of visible paragraphs. A paragraph is considered visible if at least one of its lines is visible.
[b]Note:[/b] This method returns a correct value only after the label has been drawn.
[b]Note:[/b] If [member threaded] is enabled, this method returns a value for the loaded part of the document. Use [method is_finished] or [signal finished] to determine whether document is fully loaded.
</description>
</method>

View file

@ -58,6 +58,14 @@ RichTextLabel::ItemCustomFX::~ItemCustomFX() {
custom_effect.unref();
}
Rect2i _merge_or_copy_rect(const Rect2i &p_a, const Rect2i &p_b) {
if (!p_a.has_area()) {
return p_b;
} else {
return p_a.merge(p_b);
}
}
RichTextLabel::Item *RichTextLabel::_get_next_item(Item *p_item, bool p_free) const {
if (!p_item) {
return nullptr;
@ -986,8 +994,10 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
Size2 pad_size = rect.size.min(img->image->get_size());
Vector2 pad_off = (rect.size - pad_size) / 2;
img->image->draw_rect(ci, Rect2(p_ofs + rect.position + off + pad_off, pad_size), false, img->color);
visible_rect = _merge_or_copy_rect(visible_rect, Rect2(p_ofs + rect.position + off + pad_off, pad_size));
} else {
img->image->draw_rect(ci, Rect2(p_ofs + rect.position + off, rect.size), false, img->color);
visible_rect = _merge_or_copy_rect(visible_rect, Rect2(p_ofs + rect.position + off, rect.size));
}
} break;
case ITEM_TABLE: {
@ -1358,6 +1368,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
if (!skip) {
if (txt_visible) {
has_visible_chars = true;
visible_rect = _merge_or_copy_rect(visible_rect, Rect2i(fx_offset + char_off - Vector2i(0, l_ascent), Point2i(glyphs[i].advance, l_size.y)));
if (step == DRAW_STEP_TEXT) {
if (frid != RID()) {
TS->font_draw_glyph(frid, ci, glyphs[i].font_size, fx_offset + char_off, gl, font_color);
@ -2501,6 +2512,7 @@ void RichTextLabel::_notification(int p_what) {
visible_paragraph_count = 0;
visible_line_count = 0;
visible_rect = Rect2i();
// New cache draw.
Point2 ofs = text_rect.get_position() + Vector2(0, vbegin + main->lines[from_line].offset.y - vofs);
@ -7258,6 +7270,10 @@ int RichTextLabel::get_content_height() const {
return total_height;
}
Rect2i RichTextLabel::get_visible_content_rect() const {
return visible_rect;
}
int RichTextLabel::get_content_width() const {
const_cast<RichTextLabel *>(this)->_validate_line_caches();
@ -7480,6 +7496,8 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_line_height", "line"), &RichTextLabel::get_line_height);
ClassDB::bind_method(D_METHOD("get_line_width", "line"), &RichTextLabel::get_line_width);
ClassDB::bind_method(D_METHOD("get_visible_content_rect"), &RichTextLabel::get_visible_content_rect);
ClassDB::bind_method(D_METHOD("get_line_offset", "line"), &RichTextLabel::get_line_offset);
ClassDB::bind_method(D_METHOD("get_paragraph_offset", "paragraph"), &RichTextLabel::get_paragraph_offset);

View file

@ -536,6 +536,7 @@ private:
int current_char_ofs = 0;
int visible_paragraph_count = 0;
int visible_line_count = 0;
Rect2i visible_rect;
int tab_size = 4;
bool underline_meta = true;
@ -871,6 +872,8 @@ public:
int get_line_height(int p_line) const;
int get_line_width(int p_line) const;
Rect2i get_visible_content_rect() const;
void scroll_to_selection();
VScrollBar *get_v_scroll_bar() { return vscroll; }