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);