From 0d19e18b00f3b1d619094b0794090276871c43de 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, 9 Sep 2025 10:35:16 +0300
Subject: [PATCH] [RTL] Use separate paragraph copy for the partially visible
paragraphs.
---
doc/classes/TextLine.xml | 13 ++
doc/classes/TextParagraph.xml | 13 ++
doc/classes/TextServer.xml | 15 ++
doc/classes/TextServerExtension.xml | 15 ++
modules/text_server_adv/text_server_adv.cpp | 47 +++++-
modules/text_server_adv/text_server_adv.h | 2 +
modules/text_server_fb/text_server_fb.cpp | 43 +++++-
modules/text_server_fb/text_server_fb.h | 2 +
scene/gui/rich_text_label.cpp | 159 ++++++++++++++------
scene/gui/rich_text_label.h | 1 +
scene/resources/text_line.cpp | 24 +++
scene/resources/text_line.h | 2 +
scene/resources/text_paragraph.cpp | 33 ++++
scene/resources/text_paragraph.h | 3 +
servers/text/text_server.cpp | 2 +
servers/text/text_server.h | 2 +
servers/text/text_server_dummy.h | 2 +
servers/text/text_server_extension.cpp | 14 ++
servers/text/text_server_extension.h | 5 +
19 files changed, 346 insertions(+), 51 deletions(-)
diff --git a/doc/classes/TextLine.xml b/doc/classes/TextLine.xml
index d343e37e7fd..c52552f71d8 100644
--- a/doc/classes/TextLine.xml
+++ b/doc/classes/TextLine.xml
@@ -58,6 +58,12 @@
Draw text into a canvas item at a given position, with [param color]. [param pos] specifies the top left corner of the bounding box. If [param oversampling] is greater than zero, it is used as font oversampling factor, otherwise viewport oversampling settings are used.
+
+
+
+ Duplicates this [TextLine].
+
+
@@ -119,6 +125,13 @@
Returns size of the bounding box of the text.
+
+
+
+
+ Returns [code]true[/code] if an object with [param key] is embedded in this line.
+
+
diff --git a/doc/classes/TextParagraph.xml b/doc/classes/TextParagraph.xml
index 7a73203401e..756efa0529f 100644
--- a/doc/classes/TextParagraph.xml
+++ b/doc/classes/TextParagraph.xml
@@ -110,6 +110,12 @@
Draw outlines of all lines of the text and drop cap into a canvas item at a given position, with [param color]. [param pos] specifies the top left corner of the bounding box. If [param oversampling] is greater than zero, it is used as font oversampling factor, otherwise viewport oversampling settings are used.
+
+
+
+ Duplicates this [TextParagraph].
+
+
@@ -235,6 +241,13 @@
Returns the size of the bounding box of the paragraph.
+
+
+
+
+ Returns [code]true[/code] if an object with [param key] is embedded in this shaped text buffer.
+
+
diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml
index 56c4d533697..44ef7311d38 100644
--- a/doc/classes/TextServer.xml
+++ b/doc/classes/TextServer.xml
@@ -1418,6 +1418,13 @@
[param clip_l] and [param clip_r] are offsets relative to [param pos], going to the right in horizontal layout and downward in vertical layout. If [param clip_l] is not negative, glyphs starting before the offset are clipped. If [param clip_r] is not negative, glyphs ending after the offset are clipped.
+
+
+
+
+ Duplicates shaped text buffer.
+
+
@@ -1687,6 +1694,14 @@
Breaks text into words and returns array of character ranges. Use [param grapheme_flags] to set what characters are used for breaking.
+
+
+
+
+
+ Returns [code]true[/code] if an object with [param key] is embedded in this shaped text buffer.
+
+
diff --git a/doc/classes/TextServerExtension.xml b/doc/classes/TextServerExtension.xml
index 6eb9244691d..e89836c47b4 100644
--- a/doc/classes/TextServerExtension.xml
+++ b/doc/classes/TextServerExtension.xml
@@ -1388,6 +1388,13 @@
Draw the outline of the shaped text into a canvas item at a given position, with [param color]. [param pos] specifies the leftmost point of the baseline (for horizontal layout) or topmost point of the baseline (for vertical layout). If [param oversampling] is greater than zero, it is used as font oversampling factor, otherwise viewport oversampling settings are used.
+
+
+
+
+ Duplicates shaped text buffer.
+
+
@@ -1655,6 +1662,14 @@
Breaks text into words and returns array of character ranges. Use [param grapheme_flags] to set what characters are used for breaking.
+
+
+
+
+
+ Returns [code]true[/code] if an object with [param key] is embedded in this shaped text buffer.
+
+
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index 8375b36b7af..f052e88bd6c 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -4446,7 +4446,7 @@ void TextServerAdvanced::full_copy(ShapedTextDataAdvanced *p_shaped) {
}
}
- for (int i = p_shaped->first_span; i <= p_shaped->last_span; i++) {
+ for (int i = MAX(0, p_shaped->first_span); i <= MIN(p_shaped->last_span, parent->spans.size() - 1); i++) {
ShapedTextDataAdvanced::Span span = parent->spans[i];
span.start = MAX(p_shaped->start, span.start);
span.end = MIN(p_shaped->end, span.end);
@@ -4486,6 +4486,43 @@ void TextServerAdvanced::_shaped_text_clear(const RID &p_shaped) {
invalidate(sd, true);
}
+RID TextServerAdvanced::_shaped_text_duplicate(const RID &p_shaped) {
+ _THREAD_SAFE_METHOD_
+
+ const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_NULL_V(sd, RID());
+
+ MutexLock lock(sd->mutex);
+
+ ShapedTextDataAdvanced *new_sd = memnew(ShapedTextDataAdvanced);
+ new_sd->parent = p_shaped;
+ new_sd->start = sd->start;
+ new_sd->end = sd->end;
+ new_sd->text = sd->text;
+ new_sd->hb_buffer = hb_buffer_create();
+ new_sd->utf16 = new_sd->text.utf16();
+ new_sd->script_iter = memnew(ScriptIterator(new_sd->text, 0, new_sd->text.length()));
+ new_sd->orientation = sd->orientation;
+ new_sd->direction = sd->direction;
+ new_sd->custom_punct = sd->custom_punct;
+ new_sd->para_direction = sd->para_direction;
+ new_sd->base_para_direction = sd->base_para_direction;
+ new_sd->line_breaks_valid = sd->line_breaks_valid;
+ new_sd->justification_ops_valid = sd->justification_ops_valid;
+ new_sd->sort_valid = false;
+ new_sd->upos = sd->upos;
+ new_sd->uthk = sd->uthk;
+ new_sd->runs.clear();
+ new_sd->runs_dirty = true;
+ for (int i = 0; i < TextServer::SPACING_MAX; i++) {
+ new_sd->extra_spacing[i] = sd->extra_spacing[i];
+ }
+ full_copy(new_sd);
+ new_sd->valid.clear();
+
+ return shaped_owner.make_rid(new_sd);
+}
+
void TextServerAdvanced::_shaped_text_set_direction(const RID &p_shaped, TextServer::Direction p_direction) {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_MSG(p_direction == DIRECTION_INHERITED, "Invalid text direction.");
@@ -4998,6 +5035,14 @@ String TextServerAdvanced::_shaped_get_text(const RID &p_shaped) const {
return sd->text;
}
+bool TextServerAdvanced::_shaped_text_has_object(const RID &p_shaped, const Variant &p_key) const {
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_NULL_V(sd, false);
+
+ MutexLock lock(sd->mutex);
+ return sd->objects.has(p_key);
+}
+
bool TextServerAdvanced::_shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, double p_baseline) {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_NULL_V(sd, false);
diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h
index 54aa1849280..d4222980a57 100644
--- a/modules/text_server_adv/text_server_adv.h
+++ b/modules/text_server_adv/text_server_adv.h
@@ -1032,6 +1032,7 @@ public:
MODBIND2R(RID, create_shaped_text, Direction, Orientation);
MODBIND1(shaped_text_clear, const RID &);
+ MODBIND1R(RID, shaped_text_duplicate, const RID &);
MODBIND2(shaped_text_set_direction, const RID &, Direction);
MODBIND1RC(Direction, shaped_text_get_direction, const RID &);
@@ -1060,6 +1061,7 @@ public:
MODBIND7R(bool, shaped_text_add_string, const RID &, const String &, const TypedArray &, int64_t, const Dictionary &, const String &, const Variant &);
MODBIND6R(bool, shaped_text_add_object, const RID &, const Variant &, const Size2 &, InlineAlignment, int64_t, double);
MODBIND5R(bool, shaped_text_resize_object, const RID &, const Variant &, const Size2 &, InlineAlignment, double);
+ MODBIND2RC(bool, shaped_text_has_object, const RID &, const Variant &);
MODBIND1RC(String, shaped_get_text, const RID &);
MODBIND1RC(int64_t, shaped_get_span_count, const RID &);
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index 117bdeed1ab..554108faf1c 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -3285,7 +3285,7 @@ void TextServerFallback::full_copy(ShapedTextDataFallback *p_shaped) {
}
}
- for (int i = p_shaped->first_span; i <= p_shaped->last_span; i++) {
+ for (int i = MAX(0, p_shaped->first_span); i <= MIN(p_shaped->last_span, parent->spans.size() - 1); i++) {
ShapedTextDataFallback::Span span = parent->spans[i];
span.start = MAX(p_shaped->start, span.start);
span.end = MIN(p_shaped->end, span.end);
@@ -3324,6 +3324,39 @@ void TextServerFallback::_shaped_text_clear(const RID &p_shaped) {
invalidate(sd);
}
+RID TextServerFallback::_shaped_text_duplicate(const RID &p_shaped) {
+ _THREAD_SAFE_METHOD_
+
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_NULL_V(sd, RID());
+
+ MutexLock lock(sd->mutex);
+
+ ShapedTextDataFallback *new_sd = memnew(ShapedTextDataFallback);
+ new_sd->parent = p_shaped;
+ new_sd->start = sd->start;
+ new_sd->end = sd->end;
+ new_sd->text = sd->text;
+ new_sd->orientation = sd->orientation;
+ new_sd->direction = sd->direction;
+ new_sd->custom_punct = sd->custom_punct;
+ new_sd->para_direction = sd->para_direction;
+ new_sd->line_breaks_valid = sd->line_breaks_valid;
+ new_sd->justification_ops_valid = sd->justification_ops_valid;
+ new_sd->sort_valid = false;
+ new_sd->upos = sd->upos;
+ new_sd->uthk = sd->uthk;
+ new_sd->runs.clear();
+ new_sd->runs_dirty = true;
+ for (int i = 0; i < TextServer::SPACING_MAX; i++) {
+ new_sd->extra_spacing[i] = sd->extra_spacing[i];
+ }
+ full_copy(new_sd);
+ new_sd->valid.clear();
+
+ return shaped_owner.make_rid(new_sd);
+}
+
void TextServerFallback::_shaped_text_set_direction(const RID &p_shaped, TextServer::Direction p_direction) {
ERR_FAIL_COND_MSG(p_direction == DIRECTION_INHERITED, "Invalid text direction.");
if (p_direction == DIRECTION_RTL) {
@@ -3832,6 +3865,14 @@ String TextServerFallback::_shaped_get_text(const RID &p_shaped) const {
return sd->text;
}
+bool TextServerFallback::_shaped_text_has_object(const RID &p_shaped, const Variant &p_key) const {
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_NULL_V(sd, false);
+
+ MutexLock lock(sd->mutex);
+ return sd->objects.has(p_key);
+}
+
bool TextServerFallback::_shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, double p_baseline) {
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_NULL_V(sd, false);
diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h
index b48ffca8d49..7cc02985330 100644
--- a/modules/text_server_fb/text_server_fb.h
+++ b/modules/text_server_fb/text_server_fb.h
@@ -811,6 +811,7 @@ public:
MODBIND2R(RID, create_shaped_text, Direction, Orientation);
MODBIND1(shaped_text_clear, const RID &);
+ MODBIND1R(RID, shaped_text_duplicate, const RID &);
MODBIND2(shaped_text_set_direction, const RID &, Direction);
MODBIND1RC(Direction, shaped_text_get_direction, const RID &);
@@ -839,6 +840,7 @@ public:
MODBIND7R(bool, shaped_text_add_string, const RID &, const String &, const TypedArray &, int64_t, const Dictionary &, const String &, const Variant &);
MODBIND6R(bool, shaped_text_add_object, const RID &, const Variant &, const Size2 &, InlineAlignment, int64_t, double);
MODBIND5R(bool, shaped_text_resize_object, const RID &, const Variant &, const Size2 &, InlineAlignment, double);
+ MODBIND2RC(bool, shaped_text_has_object, const RID &, const Variant &);
MODBIND1RC(String, shaped_get_text, const RID &);
MODBIND1RC(int64_t, shaped_get_span_count, const RID &);
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index e3959681624..91e4c3806d6 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -381,31 +381,62 @@ void RichTextLabel::_update_line_font(ItemFrame *p_frame, int p_line, const Ref<
// List.
_add_list_prefixes(p_frame, p_line, l);
- RID t = l.text_buf->get_rid();
- int spans = TS->shaped_get_span_count(t);
- for (int i = 0; i < spans; i++) {
- Item *it_span = items.get_or_null(TS->shaped_get_span_meta(t, i));
- ItemText *it = reinterpret_cast(it_span);
- if (it) {
- Ref font = p_base_font;
- int font_size = p_base_font_size;
+ {
+ RID t = l.text_buf->get_rid();
+ int spans = TS->shaped_get_span_count(t);
+ for (int i = 0; i < spans; i++) {
+ Item *it_span = items.get_or_null(TS->shaped_get_span_meta(t, i));
+ ItemText *it = reinterpret_cast(it_span);
+ if (it) {
+ Ref font = p_base_font;
+ int font_size = p_base_font_size;
- ItemFont *font_it = _find_font(it);
- if (font_it) {
- if (font_it->font.is_valid()) {
- font = font_it->font;
+ ItemFont *font_it = _find_font(it);
+ if (font_it) {
+ if (font_it->font.is_valid()) {
+ font = font_it->font;
+ }
+ if (font_it->font_size > 0) {
+ font_size = font_it->font_size;
+ }
}
- if (font_it->font_size > 0) {
- font_size = font_it->font_size;
+ ItemFontSize *font_size_it = _find_font_size(it);
+ if (font_size_it && font_size_it->font_size > 0) {
+ font_size = font_size_it->font_size;
}
+ TS->shaped_set_span_update_font(t, i, font->get_rids(), font_size, font->get_opentype_features());
+ } else {
+ TS->shaped_set_span_update_font(t, i, p_base_font->get_rids(), p_base_font_size, p_base_font->get_opentype_features());
}
- ItemFontSize *font_size_it = _find_font_size(it);
- if (font_size_it && font_size_it->font_size > 0) {
- font_size = font_size_it->font_size;
+ }
+ }
+ if (l.text_buf_disp.is_valid()) {
+ RID t = l.text_buf_disp->get_rid();
+ int spans = TS->shaped_get_span_count(t);
+ for (int i = 0; i < spans; i++) {
+ Item *it_span = items.get_or_null(TS->shaped_get_span_meta(t, i));
+ ItemText *it = reinterpret_cast(it_span);
+ if (it) {
+ Ref font = p_base_font;
+ int font_size = p_base_font_size;
+
+ ItemFont *font_it = _find_font(it);
+ if (font_it) {
+ if (font_it->font.is_valid()) {
+ font = font_it->font;
+ }
+ if (font_it->font_size > 0) {
+ font_size = font_it->font_size;
+ }
+ }
+ ItemFontSize *font_size_it = _find_font_size(it);
+ if (font_size_it && font_size_it->font_size > 0) {
+ font_size = font_size_it->font_size;
+ }
+ TS->shaped_set_span_update_font(t, i, font->get_rids(), font_size, font->get_opentype_features());
+ } else {
+ TS->shaped_set_span_update_font(t, i, p_base_font->get_rids(), p_base_font_size, p_base_font->get_opentype_features());
}
- TS->shaped_set_span_update_font(t, i, font->get_rids(), font_size, font->get_opentype_features());
- } else {
- TS->shaped_set_span_update_font(t, i, p_base_font->get_rids(), p_base_font_size, p_base_font->get_opentype_features());
}
}
@@ -449,6 +480,17 @@ float RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Reftab_align(tabs);
}
+ if (l.text_buf_disp.is_valid()) {
+ l.text_buf_disp->set_width(p_width - l.offset.x);
+ if (!tab_stops.is_empty()) {
+ l.text_buf_disp->tab_align(tab_stops);
+ } else if (tab_size > 0) { // Align inline tabs.
+ Vector tabs;
+ tabs.push_back(tab_size * p_base_font->get_char_size(' ', p_base_font_size).width);
+ l.text_buf_disp->tab_align(tabs);
+ }
+ }
+
Item *it_to = (p_line + 1 < (int)p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) {
switch (it->type) {
@@ -458,6 +500,9 @@ float RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Refwidth_in_percent || img->height_in_percent) {
img_size = _get_image_size(img->image, img->width_in_percent ? (p_width * img->rq_size.width / 100.f) : img->rq_size.width, img->height_in_percent ? (p_width * img->rq_size.height / 100.f) : img->rq_size.height, img->region);
l.text_buf->resize_object(it->rid, img_size, img->inline_align);
+ if (l.text_buf_disp.is_valid() && l.text_buf_disp->has_object(it->rid)) {
+ l.text_buf_disp->resize_object(it->rid, img_size, img->inline_align);
+ }
}
} break;
case ITEM_TABLE: {
@@ -489,8 +534,14 @@ float RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Refalign_to_row < 0) ? table->rows_baseline.size() - 1 : table->align_to_row;
if (table->rows_baseline.size() != 0 && row_idx < (int)table->rows_baseline.size()) {
l.text_buf->resize_object(it->rid, Size2(table->total_width, table->total_height), table->inline_align, Math::round(table->rows_baseline[row_idx]));
+ if (l.text_buf_disp.is_valid() && l.text_buf_disp->has_object(it->rid)) {
+ l.text_buf_disp->resize_object(it->rid, Size2(table->total_width, table->total_height), table->inline_align, Math::round(table->rows_baseline[row_idx]));
+ }
} else {
l.text_buf->resize_object(it->rid, Size2(table->total_width, table->total_height), table->inline_align);
+ if (l.text_buf_disp.is_valid() && l.text_buf_disp->has_object(it->rid)) {
+ l.text_buf_disp->resize_object(it->rid, Size2(table->total_width, table->total_height), table->inline_align);
+ }
}
} break;
default:
@@ -528,6 +579,7 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref
// Clear cache.
l.dc_item = nullptr;
+ l.text_buf_disp = Ref();
l.text_buf->clear();
l.text_buf->set_break_flags(autowrap_flags);
l.text_buf->set_justification_flags(_find_jst_flags(l.from));
@@ -555,6 +607,7 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref
// Shape current paragraph.
String txt;
+ String txt_sub;
Item *it_to = (p_line + 1 < (int)p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
int remaining_characters = visible_characters - l.char_offset;
for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) {
@@ -610,14 +663,13 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref
}
String lang = _find_language(it);
String tx = t->text;
- if (visible_chars_behavior == TextServer::VC_CHARS_BEFORE_SHAPING && visible_characters >= 0 && remaining_characters >= 0 && tx.length() > remaining_characters) {
- String first = tx.substr(0, remaining_characters);
- String second = tx.substr(remaining_characters, -1);
- l.text_buf->add_string(first, font, font_size, lang, it->rid);
- l.text_buf->add_string(second, font, font_size, lang, it->rid);
- } else {
- l.text_buf->add_string(tx, font, font_size, lang, it->rid);
+ if (l.text_buf_disp.is_null() && visible_chars_behavior == TextServer::VC_CHARS_BEFORE_SHAPING && visible_characters >= 0 && remaining_characters >= 0 && tx.length() > remaining_characters) {
+ String sub = tx.substr(0, remaining_characters);
+ l.text_buf_disp = l.text_buf->duplicate();
+ l.text_buf_disp->add_string(sub, font, font_size, lang, it->rid);
+ txt_sub = txt + sub;
}
+ l.text_buf->add_string(tx, font, font_size, lang, it->rid);
remaining_characters -= tx.length();
txt += tx;
@@ -698,7 +750,11 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref
}
// Apply BiDi override.
- l.text_buf->set_bidi_override(structured_text_parser(_find_stt(l.from), st_args, txt));
+ TextServer::StructuredTextParser stt = _find_stt(l.from);
+ l.text_buf->set_bidi_override(structured_text_parser(stt, st_args, txt));
+ if (l.text_buf_disp.is_valid()) {
+ l.text_buf_disp->set_bidi_override(structured_text_parser(stt, st_args, txt_sub));
+ }
*r_char_offset = l.char_offset + l.char_count;
@@ -898,13 +954,15 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
l.text_buf->draw_dropcap(ci, p_ofs + ((rtl) ? Vector2() : Vector2(l.offset.x, 0)), l.dc_color);
}
+ const Ref &text_buf = l.text_buf_disp.is_valid() ? l.text_buf_disp : l.text_buf;
+
int line_count = 0;
bool has_visible_chars = false;
// Bottom margin for text clipping.
float v_limit = theme_cache.normal_style->get_margin(SIDE_BOTTOM);
Size2 ctrl_size = get_size();
// Draw text.
- for (int line = 0; line < l.text_buf->get_line_count(); line++) {
+ for (int line = 0; line < text_buf->get_line_count(); line++) {
if (line > 0) {
off.y += (theme_cache.line_separation + p_vsep);
}
@@ -913,14 +971,14 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
break;
}
- double l_height = l.text_buf->get_line_ascent(line) + l.text_buf->get_line_descent(line);
+ double l_height = text_buf->get_line_ascent(line) + text_buf->get_line_descent(line);
if (p_ofs.y + off.y + l_height <= 0) {
off.y += l_height;
continue;
}
- float width = l.text_buf->get_width();
- float length = l.text_buf->get_line_size(line).x;
+ float width = text_buf->get_width();
+ float length = text_buf->get_line_size(line).x;
// Draw line.
if (rtl) {
@@ -936,7 +994,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
}
// Draw text.
- switch (l.text_buf->get_alignment()) {
+ switch (text_buf->get_alignment()) {
case HORIZONTAL_ALIGNMENT_FILL:
case HORIZONTAL_ALIGNMENT_LEFT: {
if (rtl) {
@@ -986,7 +1044,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
}
}
- RID rid = l.text_buf->get_line_rid(line);
+ RID rid = text_buf->get_line_rid(line);
double l_ascent = TS->shaped_text_get_ascent(rid);
Size2 l_size = TS->shaped_text_get_size(rid);
double upos = TS->shaped_text_get_underline_position(rid);
@@ -1627,10 +1685,12 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
Item *table_click_item = nullptr;
int table_click_char = -1;
- for (int line = 0; line < l.text_buf->get_line_count(); line++) {
- RID rid = l.text_buf->get_line_rid(line);
+ const Ref &text_buf = l.text_buf_disp.is_valid() ? l.text_buf_disp : l.text_buf;
- float width = l.text_buf->get_width();
+ for (int line = 0; line < text_buf->get_line_count(); line++) {
+ RID rid = text_buf->get_line_rid(line);
+
+ float width = text_buf->get_width();
float length = TS->shaped_text_get_width(rid);
if (rtl) {
@@ -1645,7 +1705,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
}
}
- switch (l.text_buf->get_alignment()) {
+ switch (text_buf->get_alignment()) {
case HORIZONTAL_ALIGNMENT_FILL:
case HORIZONTAL_ALIGNMENT_LEFT: {
if (rtl) {
@@ -1662,8 +1722,8 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
} break;
}
// Adjust for dropcap.
- int dc_lines = l.text_buf->get_dropcap_lines();
- float h_off = l.text_buf->get_dropcap_size().x;
+ int dc_lines = text_buf->get_dropcap_lines();
+ float h_off = text_buf->get_dropcap_size().x;
if (line <= dc_lines) {
if (rtl) {
off.x -= h_off;
@@ -1824,7 +1884,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
return table_offy;
}
- if (line == l.text_buf->get_line_count() - 1) {
+ if (line == text_buf->get_line_count() - 1) {
off.y += TS->shaped_text_get_descent(rid) + theme_cache.paragraph_separation + p_vsep;
} else {
off.y += TS->shaped_text_get_descent(rid) + theme_cache.line_separation + p_vsep;
@@ -2016,7 +2076,8 @@ void RichTextLabel::_accessibility_update_line(RID p_id, ItemFrame *p_frame, int
float h_off = l.text_buf->get_dropcap_size().x;
// Process text.
- const RID ¶_rid = l.text_buf->get_rid();
+ const Ref &text_buf = l.text_buf_disp.is_valid() ? l.text_buf_disp : l.text_buf;
+ const RID ¶_rid = text_buf->get_rid();
String l_text = TS->shaped_get_text(para_rid).remove_char(0xfffc).strip_edges();
if (l.dc_item) {
@@ -2026,7 +2087,7 @@ void RichTextLabel::_accessibility_update_line(RID p_id, ItemFrame *p_frame, int
if (!l_text.is_empty()) {
Vector2 off;
if (rtl) {
- off.x = p_width - l.offset.x - l.text_buf->get_width();
+ off.x = p_width - l.offset.x - text_buf->get_width();
if (!lrtl && p_frame == main) { // Skip Scrollbar.
off.x -= scroll_w;
}
@@ -2039,7 +2100,7 @@ void RichTextLabel::_accessibility_update_line(RID p_id, ItemFrame *p_frame, int
l.accessibility_text_element = DisplayServer::get_singleton()->accessibility_create_sub_element(line_ae, DisplayServer::AccessibilityRole::ROLE_STATIC_TEXT);
DisplayServer::get_singleton()->accessibility_update_set_value(l.accessibility_text_element, l_text);
- ae_rect = Rect2(p_ofs + off, l.text_buf->get_size());
+ ae_rect = Rect2(p_ofs + off, text_buf->get_size());
DisplayServer::get_singleton()->accessibility_update_set_bounds(l.accessibility_text_element, ae_rect);
ac_element_bounds_cache[l.accessibility_text_element] = ae_rect;
@@ -2049,14 +2110,14 @@ void RichTextLabel::_accessibility_update_line(RID p_id, ItemFrame *p_frame, int
}
Vector2 off;
- for (int line = 0; line < l.text_buf->get_line_count(); line++) {
+ for (int line = 0; line < text_buf->get_line_count(); line++) {
if (line > 0) {
off.y += (theme_cache.line_separation + p_vsep);
}
- const Size2 line_size = l.text_buf->get_line_size(line);
+ const Size2 line_size = text_buf->get_line_size(line);
- float width = l.text_buf->get_width();
+ float width = text_buf->get_width();
float length = line_size.x;
// Process line.
@@ -2074,7 +2135,7 @@ void RichTextLabel::_accessibility_update_line(RID p_id, ItemFrame *p_frame, int
}
// Process text.
- switch (l.text_buf->get_alignment()) {
+ switch (text_buf->get_alignment()) {
case HORIZONTAL_ALIGNMENT_FILL:
case HORIZONTAL_ALIGNMENT_LEFT: {
if (rtl) {
@@ -2099,7 +2160,7 @@ void RichTextLabel::_accessibility_update_line(RID p_id, ItemFrame *p_frame, int
}
}
- const RID &rid = l.text_buf->get_line_rid(line);
+ const RID &rid = text_buf->get_line_rid(line);
Array objects = TS->shaped_text_get_objects(rid);
for (int i = 0; i < objects.size(); i++) {
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index 71eae46eb06..07fc3961da9 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -166,6 +166,7 @@ private:
Color prefix_outline_color = Color(0, 0, 0, 0);
float prefix_width = 0;
Ref text_buf;
+ Ref text_buf_disp;
RID accessibility_line_element;
RID accessibility_text_element;
diff --git a/scene/resources/text_line.cpp b/scene/resources/text_line.cpp
index b402b3c3686..a74359e25fd 100644
--- a/scene/resources/text_line.cpp
+++ b/scene/resources/text_line.cpp
@@ -33,6 +33,7 @@
void TextLine::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear"), &TextLine::clear);
+ ClassDB::bind_method(D_METHOD("duplicate"), &TextLine::duplicate);
ClassDB::bind_method(D_METHOD("set_direction", "direction"), &TextLine::set_direction);
ClassDB::bind_method(D_METHOD("get_direction"), &TextLine::get_direction);
@@ -64,6 +65,7 @@ void TextLine::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_string", "text", "font", "font_size", "language", "meta"), &TextLine::add_string, DEFVAL(""), DEFVAL(Variant()));
ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length", "baseline"), &TextLine::add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1), DEFVAL(0.0));
ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align", "baseline"), &TextLine::resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(0.0));
+ ClassDB::bind_method(D_METHOD("has_object", "key"), &TextLine::has_object);
ClassDB::bind_method(D_METHOD("set_width", "width"), &TextLine::set_width);
ClassDB::bind_method(D_METHOD("get_width"), &TextLine::get_width);
@@ -149,6 +151,23 @@ void TextLine::clear() {
TS->shaped_text_clear(rid);
}
+Ref TextLine::duplicate() const {
+ Ref copy;
+ copy.instantiate();
+ if (rid.is_valid()) {
+ copy->rid = TS->shaped_text_duplicate(rid);
+ }
+ copy->dirty = true;
+ copy->width = width;
+ copy->flags = flags;
+ copy->alignment = alignment;
+ copy->el_char = el_char;
+ copy->overrun_behavior = overrun_behavior;
+ copy->tab_stops = tab_stops;
+
+ return copy;
+}
+
void TextLine::set_preserve_invalid(bool p_enabled) {
TS->shaped_text_set_preserve_invalid(rid, p_enabled);
dirty = true;
@@ -212,6 +231,11 @@ bool TextLine::resize_object(Variant p_key, const Size2 &p_size, InlineAlignment
return TS->shaped_text_resize_object(rid, p_key, p_size, p_inline_align, p_baseline);
}
+bool TextLine::has_object(Variant p_key) const {
+ _shape();
+ return TS->shaped_text_has_object(rid, p_key);
+}
+
Array TextLine::get_objects() const {
return TS->shaped_text_get_objects(rid);
}
diff --git a/scene/resources/text_line.h b/scene/resources/text_line.h
index 23b0fe2633d..6c9f548d9dd 100644
--- a/scene/resources/text_line.h
+++ b/scene/resources/text_line.h
@@ -66,6 +66,7 @@ public:
RID get_rid() const;
void clear();
+ Ref duplicate() const;
void set_direction(TextServer::Direction p_direction);
TextServer::Direction get_direction() const;
@@ -85,6 +86,7 @@ public:
bool add_string(const String &p_text, const Ref &p_font, int p_font_size, const String &p_language = "", const Variant &p_meta = Variant());
bool add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1, float p_baseline = 0.0);
bool resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, float p_baseline = 0.0);
+ bool has_object(Variant p_key) const;
void set_horizontal_alignment(HorizontalAlignment p_alignment);
HorizontalAlignment get_horizontal_alignment() const;
diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp
index dd0c1dc90b8..35d8af36cb3 100644
--- a/scene/resources/text_paragraph.cpp
+++ b/scene/resources/text_paragraph.cpp
@@ -33,6 +33,7 @@
void TextParagraph::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear"), &TextParagraph::clear);
+ ClassDB::bind_method(D_METHOD("duplicate"), &TextParagraph::duplicate);
ClassDB::bind_method(D_METHOD("set_direction", "direction"), &TextParagraph::set_direction);
ClassDB::bind_method(D_METHOD("get_direction"), &TextParagraph::get_direction);
@@ -72,6 +73,7 @@ void TextParagraph::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_string", "text", "font", "font_size", "language", "meta"), &TextParagraph::add_string, DEFVAL(""), DEFVAL(Variant()));
ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length", "baseline"), &TextParagraph::add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1), DEFVAL(0.0));
ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align", "baseline"), &TextParagraph::resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(0.0));
+ ClassDB::bind_method(D_METHOD("has_object", "key"), &TextParagraph::has_object);
ClassDB::bind_method(D_METHOD("set_alignment", "alignment"), &TextParagraph::set_alignment);
ClassDB::bind_method(D_METHOD("get_alignment"), &TextParagraph::get_alignment);
@@ -322,6 +324,31 @@ void TextParagraph::clear() {
TS->shaped_text_clear(dropcap_rid);
}
+Ref TextParagraph::duplicate() const {
+ Ref copy;
+ copy.instantiate();
+ if (dropcap_rid.is_valid()) {
+ copy->dropcap_rid = TS->shaped_text_duplicate(dropcap_rid);
+ }
+ copy->dropcap_lines = dropcap_lines;
+ copy->dropcap_margins = dropcap_margins;
+ if (rid.is_valid()) {
+ copy->rid = TS->shaped_text_duplicate(rid);
+ }
+ copy->lines_dirty = true;
+ copy->line_spacing = line_spacing;
+ copy->width = width;
+ copy->max_lines_visible = max_lines_visible;
+ copy->brk_flags = brk_flags;
+ copy->jst_flags = jst_flags;
+ copy->el_char = el_char;
+ copy->overrun_behavior = overrun_behavior;
+ copy->alignment = alignment;
+ copy->tab_stops = tab_stops;
+
+ return copy;
+}
+
void TextParagraph::set_preserve_invalid(bool p_enabled) {
_THREAD_SAFE_METHOD_
@@ -448,6 +475,12 @@ bool TextParagraph::resize_object(Variant p_key, const Size2 &p_size, InlineAlig
return res;
}
+bool TextParagraph::has_object(Variant p_key) const {
+ _THREAD_SAFE_METHOD_
+
+ return TS->shaped_text_has_object(rid, p_key);
+}
+
void TextParagraph::set_alignment(HorizontalAlignment p_alignment) {
_THREAD_SAFE_METHOD_
diff --git a/scene/resources/text_paragraph.h b/scene/resources/text_paragraph.h
index 2a262ae0e5b..5a5a2c6d2e7 100644
--- a/scene/resources/text_paragraph.h
+++ b/scene/resources/text_paragraph.h
@@ -85,6 +85,8 @@ public:
void clear();
+ Ref duplicate() const;
+
void set_direction(TextServer::Direction p_direction);
TextServer::Direction get_direction() const;
TextServer::Direction get_inferred_direction() const;
@@ -109,6 +111,7 @@ public:
bool add_string(const String &p_text, const Ref &p_font, int p_font_size, const String &p_language = "", const Variant &p_meta = Variant());
bool add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1, float p_baseline = 0.0);
bool resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, float p_baseline = 0.0);
+ bool has_object(Variant p_key) const;
void set_alignment(HorizontalAlignment p_alignment);
HorizontalAlignment get_alignment() const;
diff --git a/servers/text/text_server.cpp b/servers/text/text_server.cpp
index b1afc9668b4..b620ee04147 100644
--- a/servers/text/text_server.cpp
+++ b/servers/text/text_server.cpp
@@ -402,6 +402,7 @@ void TextServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("create_shaped_text", "direction", "orientation"), &TextServer::create_shaped_text, DEFVAL(DIRECTION_AUTO), DEFVAL(ORIENTATION_HORIZONTAL));
ClassDB::bind_method(D_METHOD("shaped_text_clear", "rid"), &TextServer::shaped_text_clear);
+ ClassDB::bind_method(D_METHOD("shaped_text_duplicate", "rid"), &TextServer::shaped_text_duplicate);
ClassDB::bind_method(D_METHOD("shaped_text_set_direction", "shaped", "direction"), &TextServer::shaped_text_set_direction, DEFVAL(DIRECTION_AUTO));
ClassDB::bind_method(D_METHOD("shaped_text_get_direction", "shaped"), &TextServer::shaped_text_get_direction);
@@ -430,6 +431,7 @@ void TextServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("shaped_text_add_string", "shaped", "text", "fonts", "size", "opentype_features", "language", "meta"), &TextServer::shaped_text_add_string, DEFVAL(Dictionary()), DEFVAL(""), DEFVAL(Variant()));
ClassDB::bind_method(D_METHOD("shaped_text_add_object", "shaped", "key", "size", "inline_align", "length", "baseline"), &TextServer::shaped_text_add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1), DEFVAL(0.0));
ClassDB::bind_method(D_METHOD("shaped_text_resize_object", "shaped", "key", "size", "inline_align", "baseline"), &TextServer::shaped_text_resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(0.0));
+ ClassDB::bind_method(D_METHOD("shaped_text_has_object", "shaped", "key"), &TextServer::shaped_text_has_object);
ClassDB::bind_method(D_METHOD("shaped_get_text", "shaped"), &TextServer::shaped_get_text);
ClassDB::bind_method(D_METHOD("shaped_get_span_count", "shaped"), &TextServer::shaped_get_span_count);
diff --git a/servers/text/text_server.h b/servers/text/text_server.h
index 3e4d2a6525e..f04f7ffdf32 100644
--- a/servers/text/text_server.h
+++ b/servers/text/text_server.h
@@ -463,6 +463,7 @@ public:
virtual RID create_shaped_text(Direction p_direction = DIRECTION_AUTO, Orientation p_orientation = ORIENTATION_HORIZONTAL) = 0;
virtual void shaped_text_clear(const RID &p_shaped) = 0;
+ virtual RID shaped_text_duplicate(const RID &p_shaped) = 0;
virtual void shaped_text_set_direction(const RID &p_shaped, Direction p_direction = DIRECTION_AUTO) = 0;
virtual Direction shaped_text_get_direction(const RID &p_shaped) const = 0;
@@ -491,6 +492,7 @@ public:
virtual bool shaped_text_add_string(const RID &p_shaped, const String &p_text, const TypedArray &p_fonts, int64_t p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant()) = 0;
virtual bool shaped_text_add_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int64_t p_length = 1, double p_baseline = 0.0) = 0;
virtual bool shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, double p_baseline = 0.0) = 0;
+ virtual bool shaped_text_has_object(const RID &p_shaped, const Variant &p_key) const = 0;
virtual String shaped_get_text(const RID &p_shaped) const = 0;
virtual int64_t shaped_get_span_count(const RID &p_shaped) const = 0;
diff --git a/servers/text/text_server_dummy.h b/servers/text/text_server_dummy.h
index a1f3a08d5bf..4fc991e4ab8 100644
--- a/servers/text/text_server_dummy.h
+++ b/servers/text/text_server_dummy.h
@@ -94,9 +94,11 @@ public:
virtual RID create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) override { return RID(); }
virtual void shaped_text_clear(const RID &p_shaped) override {}
+ virtual RID shaped_text_duplicate(const RID &p_shaped) override { return RID(); }
virtual bool shaped_text_add_string(const RID &p_shaped, const String &p_text, const TypedArray &p_fonts, int64_t p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) override { return false; }
virtual bool shaped_text_add_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, int64_t p_length, double p_baseline) override { return false; }
virtual bool shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, double p_baseline) override { return false; }
+ virtual bool shaped_text_has_object(const RID &p_shaped, const Variant &p_key) const override { return false; }
virtual int64_t shaped_get_span_count(const RID &p_shaped) const override { return 0; }
virtual Variant shaped_get_span_meta(const RID &p_shaped, int64_t p_index) const override { return Variant(); }
virtual Variant shaped_get_span_embedded_object(const RID &p_shaped, int64_t p_index) const override { return Variant(); }
diff --git a/servers/text/text_server_extension.cpp b/servers/text/text_server_extension.cpp
index 28fd2ff50cc..051632f8186 100644
--- a/servers/text/text_server_extension.cpp
+++ b/servers/text/text_server_extension.cpp
@@ -252,6 +252,7 @@ void TextServerExtension::_bind_methods() {
GDVIRTUAL_BIND(_create_shaped_text, "direction", "orientation");
GDVIRTUAL_BIND(_shaped_text_clear, "shaped");
+ GDVIRTUAL_BIND(_shaped_text_duplicate, "shaped");
GDVIRTUAL_BIND(_shaped_text_set_direction, "shaped", "direction");
GDVIRTUAL_BIND(_shaped_text_get_direction, "shaped");
@@ -280,6 +281,7 @@ void TextServerExtension::_bind_methods() {
GDVIRTUAL_BIND(_shaped_text_add_string, "shaped", "text", "fonts", "size", "opentype_features", "language", "meta");
GDVIRTUAL_BIND(_shaped_text_add_object, "shaped", "key", "size", "inline_align", "length", "baseline");
GDVIRTUAL_BIND(_shaped_text_resize_object, "shaped", "key", "size", "inline_align", "baseline");
+ GDVIRTUAL_BIND(_shaped_text_has_object, "shaped", "key");
GDVIRTUAL_BIND(_shaped_get_text, "shaped");
GDVIRTUAL_BIND(_shaped_get_span_count, "shaped");
@@ -1148,6 +1150,12 @@ void TextServerExtension::shaped_text_clear(const RID &p_shaped) {
GDVIRTUAL_CALL(_shaped_text_clear, p_shaped);
}
+RID TextServerExtension::shaped_text_duplicate(const RID &p_shaped) {
+ RID ret;
+ GDVIRTUAL_CALL(_shaped_text_duplicate, p_shaped, ret);
+ return ret;
+}
+
void TextServerExtension::shaped_text_set_direction(const RID &p_shaped, TextServer::Direction p_direction) {
GDVIRTUAL_CALL(_shaped_text_set_direction, p_shaped, p_direction);
}
@@ -1246,6 +1254,12 @@ bool TextServerExtension::shaped_text_resize_object(const RID &p_shaped, const V
return ret;
}
+bool TextServerExtension::shaped_text_has_object(const RID &p_shaped, const Variant &p_key) const {
+ bool ret = false;
+ GDVIRTUAL_CALL(_shaped_text_has_object, p_shaped, p_key, ret);
+ return ret;
+}
+
String TextServerExtension::shaped_get_text(const RID &p_shaped) const {
String ret;
GDVIRTUAL_CALL(_shaped_get_text, p_shaped, ret);
diff --git a/servers/text/text_server_extension.h b/servers/text/text_server_extension.h
index 34a8081cce7..f4cf2c0cfac 100644
--- a/servers/text/text_server_extension.h
+++ b/servers/text/text_server_extension.h
@@ -413,6 +413,9 @@ public:
virtual void shaped_text_clear(const RID &p_shaped) override;
GDVIRTUAL1_REQUIRED(_shaped_text_clear, RID);
+ virtual RID shaped_text_duplicate(const RID &p_shaped) override;
+ GDVIRTUAL1R_REQUIRED(RID, _shaped_text_duplicate, RID);
+
virtual void shaped_text_set_direction(const RID &p_shaped, Direction p_direction = DIRECTION_AUTO) override;
virtual Direction shaped_text_get_direction(const RID &p_shaped) const override;
virtual Direction shaped_text_get_inferred_direction(const RID &p_shaped) const override;
@@ -456,9 +459,11 @@ public:
virtual bool shaped_text_add_string(const RID &p_shaped, const String &p_text, const TypedArray &p_fonts, int64_t p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant()) override;
virtual bool shaped_text_add_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int64_t p_length = 1, double p_baseline = 0.0) override;
virtual bool shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, double p_baseline = 0.0) override;
+ virtual bool shaped_text_has_object(const RID &p_shaped, const Variant &p_key) const override;
GDVIRTUAL7R_REQUIRED(bool, _shaped_text_add_string, RID, const String &, const TypedArray &, int64_t, const Dictionary &, const String &, const Variant &);
GDVIRTUAL6R_REQUIRED(bool, _shaped_text_add_object, RID, const Variant &, const Size2 &, InlineAlignment, int64_t, double);
GDVIRTUAL5R_REQUIRED(bool, _shaped_text_resize_object, RID, const Variant &, const Size2 &, InlineAlignment, double);
+ GDVIRTUAL2RC_REQUIRED(bool, _shaped_text_has_object, RID, const Variant &);
virtual String shaped_get_text(const RID &p_shaped) const override;
GDVIRTUAL1RC_REQUIRED(String, _shaped_get_text, RID);