diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml index aed041c5ad1..4b43e7d8307 100644 --- a/doc/classes/TextServer.xml +++ b/doc/classes/TextServer.xml @@ -1181,6 +1181,14 @@ Returns number of text spans added using [method shaped_text_add_string] or [method shaped_text_add_object]. + + + + + + Returns text embedded object key. + + diff --git a/doc/classes/TextServerExtension.xml b/doc/classes/TextServerExtension.xml index 3c27404f8e3..e719125d99e 100644 --- a/doc/classes/TextServerExtension.xml +++ b/doc/classes/TextServerExtension.xml @@ -1290,6 +1290,15 @@ Returns number of text spans added using [method _shaped_text_add_string] or [method _shaped_text_add_object]. + + + + + + [b]Required.[/b] + Returns text embedded object key. + + diff --git a/editor/gui/editor_spin_slider.cpp b/editor/gui/editor_spin_slider.cpp index a073a2338b0..aa3a504721e 100644 --- a/editor/gui/editor_spin_slider.cpp +++ b/editor/gui/editor_spin_slider.cpp @@ -369,7 +369,7 @@ void EditorSpinSlider::_draw_spin_slider() { } if (glyphs[i].font_rid != RID()) { TS->font_draw_glyph(glyphs[i].font_rid, ci, glyphs[i].font_size, text_ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, color); - } else if ((glyphs[i].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) { + } else if (((glyphs[i].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) && ((glyphs[i].flags & TextServer::GRAPHEME_IS_EMBEDDED_OBJECT) != TextServer::GRAPHEME_IS_EMBEDDED_OBJECT)) { TS->draw_hex_code_box(ci, glyphs[i].font_size, text_ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, color); } } diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index 3322300ddaf..efbe32d6b52 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -4165,15 +4165,14 @@ void TextServerAdvanced::full_copy(ShapedTextDataAdvanced *p_shaped) { } } - for (int i = 0; i < parent->spans.size(); i++) { + for (int i = p_shaped->first_span; i <= p_shaped->last_span; i++) { ShapedTextDataAdvanced::Span span = parent->spans[i]; - if (span.start >= p_shaped->end || span.end <= p_shaped->start) { - continue; - } span.start = MAX(p_shaped->start, span.start); span.end = MIN(p_shaped->end, span.end); p_shaped->spans.push_back(span); } + p_shaped->first_span = 0; + p_shaped->last_span = 0; p_shaped->parent = RID(); } @@ -4199,6 +4198,8 @@ void TextServerAdvanced::_shaped_text_clear(const RID &p_shaped) { sd->end = 0; sd->text = String(); sd->spans.clear(); + sd->first_span = 0; + sd->last_span = 0; sd->objects.clear(); sd->bidi_override.clear(); invalidate(sd, true); @@ -4383,19 +4384,48 @@ TextServer::Orientation TextServerAdvanced::_shaped_text_get_orientation(const R int64_t TextServerAdvanced::_shaped_get_span_count(const RID &p_shaped) const { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_NULL_V(sd, 0); - return sd->spans.size(); + + if (sd->parent != RID()) { + return sd->last_span - sd->first_span + 1; + } else { + return sd->spans.size(); + } } Variant TextServerAdvanced::_shaped_get_span_meta(const RID &p_shaped, int64_t p_index) const { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_NULL_V(sd, Variant()); - ERR_FAIL_INDEX_V(p_index, sd->spans.size(), Variant()); - return sd->spans[p_index].meta; + if (sd->parent != RID()) { + ShapedTextDataAdvanced *parent_sd = shaped_owner.get_or_null(sd->parent); + ERR_FAIL_COND_V(!parent_sd->valid.is_set(), Variant()); + ERR_FAIL_INDEX_V(p_index + sd->first_span, parent_sd->spans.size(), Variant()); + return parent_sd->spans[p_index + sd->first_span].meta; + } else { + ERR_FAIL_INDEX_V(p_index, sd->spans.size(), Variant()); + return sd->spans[p_index].meta; + } +} + +Variant TextServerAdvanced::_shaped_get_span_embedded_object(const RID &p_shaped, int64_t p_index) const { + ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); + ERR_FAIL_NULL_V(sd, Variant()); + if (sd->parent != RID()) { + ShapedTextDataAdvanced *parent_sd = shaped_owner.get_or_null(sd->parent); + ERR_FAIL_COND_V(!parent_sd->valid.is_set(), Variant()); + ERR_FAIL_INDEX_V(p_index + sd->first_span, parent_sd->spans.size(), Variant()); + return parent_sd->spans[p_index + sd->first_span].embedded_key; + } else { + ERR_FAIL_INDEX_V(p_index, sd->spans.size(), Variant()); + return sd->spans[p_index].embedded_key; + } } void TextServerAdvanced::_shaped_set_span_update_font(const RID &p_shaped, int64_t p_index, const TypedArray &p_fonts, int64_t p_size, const Dictionary &p_opentype_features) { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_NULL(sd); + if (sd->parent != RID()) { + full_copy(sd); + } ERR_FAIL_INDEX(p_index, sd->spans.size()); ShapedTextDataAdvanced::Span &span = sd->spans.ptrw()[p_index]; @@ -4489,18 +4519,22 @@ bool TextServerAdvanced::_shaped_text_resize_object(const RID &p_shaped, const V sd->width = 0; sd->upos = 0; sd->uthk = 0; + + Vector &spans = sd->spans; + if (sd->parent != RID()) { + ShapedTextDataAdvanced *parent_sd = shaped_owner.get_or_null(sd->parent); + ERR_FAIL_COND_V(!parent_sd->valid.is_set(), false); + spans = parent_sd->spans; + } + int sd_size = sd->glyphs.size(); + int span_size = spans.size(); for (int i = 0; i < sd_size; i++) { Glyph gl = sd->glyphs[i]; Variant key; - if (gl.count == 1) { - for (const KeyValue &E : sd->objects) { - if (E.value.start == gl.start) { - key = E.key; - break; - } - } + if ((gl.flags & GRAPHEME_IS_EMBEDDED_OBJECT) == GRAPHEME_IS_EMBEDDED_OBJECT && gl.span_index + sd->first_span >= 0 && gl.span_index + sd->first_span < span_size) { + key = spans[gl.span_index + sd->first_span].embedded_key; } if (key != Variant()) { if (sd->orientation == ORIENTATION_HORIZONTAL) { @@ -4671,6 +4705,20 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S p_new_sd->utf16 = p_new_sd->text.utf16(); p_new_sd->script_iter = memnew(ScriptIterator(p_new_sd->text, 0, p_new_sd->text.length())); + int span_size = p_sd->spans.size(); + + p_new_sd->first_span = 0; + p_new_sd->last_span = span_size - 1; + for (int i = 0; i < span_size; i++) { + const ShapedTextDataAdvanced::Span &span = p_sd->spans[i]; + if (span.end <= p_start) { + p_new_sd->first_span = i + 1; + } else if (span.start >= p_start + p_length) { + p_new_sd->last_span = i - 1; + break; + } + } + int sd_size = p_sd->glyphs.size(); const Glyph *sd_glyphs = p_sd->glyphs.ptr(); for (int ov = 0; ov < p_sd->bidi_override.size(); ov++) { @@ -4749,31 +4797,27 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S if ((sd_glyphs[j].start >= bidi_run_start) && (sd_glyphs[j].end <= bidi_run_end)) { // Copy glyphs. Glyph gl = sd_glyphs[j]; + if (gl.span_index >= 0) { + gl.span_index -= p_new_sd->first_span; + } if (gl.end == p_start + p_length && ((gl.flags & GRAPHEME_IS_SOFT_HYPHEN) == GRAPHEME_IS_SOFT_HYPHEN)) { uint32_t index = font_get_glyph_index(gl.font_rid, gl.font_size, 0x00ad, 0); float w = font_get_glyph_advance(gl.font_rid, gl.font_size, index)[(p_new_sd->orientation == ORIENTATION_HORIZONTAL) ? 0 : 1]; gl.index = index; gl.advance = w; } - Variant key; - bool find_embedded = false; - if (gl.count == 1) { - for (const KeyValue &E : p_sd->objects) { - if (E.value.start == gl.start) { - find_embedded = true; - key = E.key; - p_new_sd->objects[key] = E.value; - break; + if ((gl.flags & GRAPHEME_IS_EMBEDDED_OBJECT) == GRAPHEME_IS_EMBEDDED_OBJECT && gl.span_index >= 0 && gl.span_index < span_size) { + Variant key = p_sd->spans[gl.span_index].embedded_key; + if (key != Variant()) { + ShapedTextDataAdvanced::EmbeddedObject obj = p_sd->objects[key]; + if (p_new_sd->orientation == ORIENTATION_HORIZONTAL) { + obj.rect.position.x = p_new_sd->width; + p_new_sd->width += obj.rect.size.x; + } else { + obj.rect.position.y = p_new_sd->width; + p_new_sd->width += obj.rect.size.y; } - } - } - if (find_embedded) { - if (p_new_sd->orientation == ORIENTATION_HORIZONTAL) { - p_new_sd->objects[key].rect.position.x = p_new_sd->width; - p_new_sd->width += p_new_sd->objects[key].rect.size.x; - } else { - p_new_sd->objects[key].rect.position.y = p_new_sd->width; - p_new_sd->width += p_new_sd->objects[key].rect.size.y; + p_new_sd->objects[key] = obj; } } else { if (gl.font_rid.is_valid()) { @@ -5226,7 +5270,8 @@ void TextServerAdvanced::_shaped_text_overrun_trim_to_width(const RID &p_shaped_ spans = parent_sd->spans; } - if (spans.size() == 0) { + int span_size = spans.size(); + if (span_size == 0) { return; } @@ -5237,7 +5282,7 @@ void TextServerAdvanced::_shaped_text_overrun_trim_to_width(const RID &p_shaped_ // Find usable fonts, if fonts from the last glyph do not have required chars. RID dot_gl_font_rid = sd_glyphs[sd_size - 1].font_rid; if (!_font_has_char(dot_gl_font_rid, sd->el_char)) { - const Array &fonts = spans[spans.size() - 1].fonts; + const Array &fonts = spans[span_size - 1].fonts; for (int i = 0; i < fonts.size(); i++) { if (_font_has_char(fonts[i], sd->el_char)) { dot_gl_font_rid = fonts[i]; @@ -5247,7 +5292,7 @@ void TextServerAdvanced::_shaped_text_overrun_trim_to_width(const RID &p_shaped_ } if (!found_el_char && OS::get_singleton()->has_feature("system_fonts") && fonts.size() > 0 && _font_is_allow_system_fallback(fonts[0])) { const char32_t u32str[] = { sd->el_char, 0 }; - RID rid = _find_sys_font_for_text(fonts[0], String(), spans[spans.size() - 1].language, u32str); + RID rid = _find_sys_font_for_text(fonts[0], String(), spans[span_size - 1].language, u32str); if (rid.is_valid()) { dot_gl_font_rid = rid; found_el_char = true; @@ -5260,7 +5305,7 @@ void TextServerAdvanced::_shaped_text_overrun_trim_to_width(const RID &p_shaped_ bool found_dot_char = false; dot_gl_font_rid = sd_glyphs[sd_size - 1].font_rid; if (!_font_has_char(dot_gl_font_rid, '.')) { - const Array &fonts = spans[spans.size() - 1].fonts; + const Array &fonts = spans[span_size - 1].fonts; for (int i = 0; i < fonts.size(); i++) { if (_font_has_char(fonts[i], '.')) { dot_gl_font_rid = fonts[i]; @@ -5269,7 +5314,7 @@ void TextServerAdvanced::_shaped_text_overrun_trim_to_width(const RID &p_shaped_ } } if (!found_dot_char && OS::get_singleton()->has_feature("system_fonts") && fonts.size() > 0 && _font_is_allow_system_fallback(fonts[0])) { - RID rid = _find_sys_font_for_text(fonts[0], String(), spans[spans.size() - 1].language, "."); + RID rid = _find_sys_font_for_text(fonts[0], String(), spans[span_size - 1].language, "."); if (rid.is_valid()) { dot_gl_font_rid = rid; } @@ -5278,7 +5323,7 @@ void TextServerAdvanced::_shaped_text_overrun_trim_to_width(const RID &p_shaped_ } RID whitespace_gl_font_rid = sd_glyphs[sd_size - 1].font_rid; if (!_font_has_char(whitespace_gl_font_rid, ' ')) { - const Array &fonts = spans[spans.size() - 1].fonts; + const Array &fonts = spans[span_size - 1].fonts; for (int i = 0; i < fonts.size(); i++) { if (_font_has_char(fonts[i], ' ')) { whitespace_gl_font_rid = fonts[i]; @@ -5437,7 +5482,8 @@ void TextServerAdvanced::_update_chars(ShapedTextDataAdvanced *p_sd) const { spans = parent_sd->spans; } - while (i < spans.size()) { + int span_size = spans.size(); + while (i < span_size) { if (spans[i].start > p_sd->end) { break; } @@ -5448,7 +5494,7 @@ void TextServerAdvanced::_update_chars(ShapedTextDataAdvanced *p_sd) const { int r_start = MAX(0, spans[i].start - p_sd->start); String language = spans[i].language; - while (i + 1 < spans.size() && language == spans[i + 1].language) { + while (i + 1 < span_size && language == spans[i + 1].language) { i++; } int r_end = MIN(spans[i].end - p_sd->start, p_sd->text.length()); @@ -5510,10 +5556,11 @@ bool TextServerAdvanced::_shaped_text_update_breaks(const RID &p_shaped) { sd->break_inserts = 0; UErrorCode err = U_ZERO_ERROR; int i = 0; - while (i < sd->spans.size()) { + int span_size = sd->spans.size(); + while (i < span_size) { String language = sd->spans[i].language; int r_start = sd->spans[i].start; - while (i + 1 < sd->spans.size() && language == sd->spans[i + 1].language) { + while (i + 1 < span_size && language == sd->spans[i + 1].language) { i++; } int r_end = sd->spans[i].end; @@ -5637,6 +5684,7 @@ bool TextServerAdvanced::_shaped_text_update_breaks(const RID &p_shaped) { } } Glyph gl; + gl.span_index = sd_glyphs[i].span_index; gl.start = sd_glyphs[i].start; gl.end = sd_glyphs[i].end; gl.count = 1; @@ -5885,6 +5933,7 @@ bool TextServerAdvanced::_shaped_text_update_justification_ops(const RID &p_shap } // Inject virtual space for alignment. Glyph gl; + gl.span_index = sd_glyphs[i].span_index; gl.start = sd_glyphs[i].start; gl.end = sd_glyphs[i].end; gl.count = 1; @@ -6031,6 +6080,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star for (int i = fb_from; i != fb_to; i += fb_delta) { if (p_sd->preserve_invalid || (p_sd->preserve_control && is_control(p_sd->text[i]))) { Glyph gl; + gl.span_index = p_span; gl.start = i + p_sd->start; gl.end = i + 1 + p_sd->start; gl.count = 1; @@ -6187,6 +6237,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star Glyph &gl = w[i]; gl = Glyph(); + gl.span_index = p_span; gl.start = glyph_info[i].cluster; gl.end = end; gl.count = 0; @@ -6489,6 +6540,7 @@ bool TextServerAdvanced::_shaped_text_shape(const RID &p_shaped) { gl.start = span.start; gl.end = span.end; gl.count = 1; + gl.span_index = k; gl.flags = GRAPHEME_IS_VALID | GRAPHEME_IS_EMBEDDED_OBJECT; if (sd->orientation == ORIENTATION_HORIZONTAL) { gl.advance = sd->objects[span.embedded_key].rect.size.x; diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h index c63389b1c63..60599ff3e0c 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -479,6 +479,8 @@ class TextServerAdvanced : public TextServerExtension { Variant meta; }; Vector spans; + int first_span = 0; // First span in the parent ShapedTextData. + int last_span = 0; struct EmbeddedObject { int start = -1; @@ -941,6 +943,7 @@ public: MODBIND1RC(int64_t, shaped_get_span_count, const RID &); MODBIND2RC(Variant, shaped_get_span_meta, const RID &, int64_t); + MODBIND2RC(Variant, shaped_get_span_embedded_object, const RID &, int64_t); MODBIND5(shaped_set_span_update_font, const RID &, int64_t, const TypedArray &, int64_t, const Dictionary &); MODBIND3RC(RID, shaped_text_substr, const RID &, int64_t, int64_t); diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index 540ba19cace..0ce7fcefdb6 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -3045,15 +3045,14 @@ void TextServerFallback::full_copy(ShapedTextDataFallback *p_shaped) { } } - for (int k = 0; k < parent->spans.size(); k++) { - ShapedTextDataFallback::Span span = parent->spans[k]; - if (span.start >= p_shaped->end || span.end <= p_shaped->start) { - continue; - } + for (int i = p_shaped->first_span; i <= p_shaped->last_span; i++) { + ShapedTextDataFallback::Span span = parent->spans[i]; span.start = MAX(p_shaped->start, span.start); span.end = MIN(p_shaped->end, span.end); p_shaped->spans.push_back(span); } + p_shaped->first_span = 0; + p_shaped->last_span = 0; p_shaped->parent = RID(); } @@ -3079,6 +3078,8 @@ void TextServerFallback::_shaped_text_clear(const RID &p_shaped) { sd->end = 0; sd->text = String(); sd->spans.clear(); + sd->first_span = 0; + sd->last_span = 0; sd->objects.clear(); invalidate(sd); } @@ -3231,19 +3232,48 @@ int64_t TextServerFallback::_shaped_text_get_spacing(const RID &p_shaped, Spacin int64_t TextServerFallback::_shaped_get_span_count(const RID &p_shaped) const { ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_NULL_V(sd, 0); - return sd->spans.size(); + + if (sd->parent != RID()) { + return sd->last_span - sd->first_span + 1; + } else { + return sd->spans.size(); + } } Variant TextServerFallback::_shaped_get_span_meta(const RID &p_shaped, int64_t p_index) const { ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_NULL_V(sd, Variant()); - ERR_FAIL_INDEX_V(p_index, sd->spans.size(), Variant()); - return sd->spans[p_index].meta; + if (sd->parent != RID()) { + ShapedTextDataFallback *parent_sd = shaped_owner.get_or_null(sd->parent); + ERR_FAIL_COND_V(!parent_sd->valid.is_set(), Variant()); + ERR_FAIL_INDEX_V(p_index + sd->first_span, parent_sd->spans.size(), Variant()); + return parent_sd->spans[p_index + sd->first_span].meta; + } else { + ERR_FAIL_INDEX_V(p_index, sd->spans.size(), Variant()); + return sd->spans[p_index].meta; + } +} + +Variant TextServerFallback::_shaped_get_span_embedded_object(const RID &p_shaped, int64_t p_index) const { + ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); + ERR_FAIL_NULL_V(sd, Variant()); + if (sd->parent != RID()) { + ShapedTextDataFallback *parent_sd = shaped_owner.get_or_null(sd->parent); + ERR_FAIL_COND_V(!parent_sd->valid.is_set(), Variant()); + ERR_FAIL_INDEX_V(p_index + sd->first_span, parent_sd->spans.size(), Variant()); + return parent_sd->spans[p_index + sd->first_span].embedded_key; + } else { + ERR_FAIL_INDEX_V(p_index, sd->spans.size(), Variant()); + return sd->spans[p_index].embedded_key; + } } void TextServerFallback::_shaped_set_span_update_font(const RID &p_shaped, int64_t p_index, const TypedArray &p_fonts, int64_t p_size, const Dictionary &p_opentype_features) { ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_NULL(sd); + if (sd->parent != RID()) { + full_copy(sd); + } ERR_FAIL_INDEX(p_index, sd->spans.size()); ShapedTextDataFallback::Span &span = sd->spans.ptrw()[p_index]; @@ -3365,18 +3395,22 @@ bool TextServerFallback::_shaped_text_resize_object(const RID &p_shaped, const V sd->width = 0; sd->upos = 0; sd->uthk = 0; + + Vector &spans = sd->spans; + if (sd->parent != RID()) { + ShapedTextDataFallback *parent_sd = shaped_owner.get_or_null(sd->parent); + ERR_FAIL_COND_V(!parent_sd->valid.is_set(), false); + spans = parent_sd->spans; + } + int sd_size = sd->glyphs.size(); + int span_size = spans.size(); for (int i = 0; i < sd_size; i++) { Glyph gl = sd->glyphs[i]; Variant key; - if (gl.count == 1) { - for (const KeyValue &E : sd->objects) { - if (E.value.start == gl.start) { - key = E.key; - break; - } - } + if ((gl.flags & GRAPHEME_IS_EMBEDDED_OBJECT) == GRAPHEME_IS_EMBEDDED_OBJECT && gl.span_index + sd->first_span >= 0 && gl.span_index + sd->first_span < span_size) { + key = spans[gl.span_index + sd->first_span].embedded_key; } if (key != Variant()) { if (sd->orientation == ORIENTATION_HORIZONTAL) { @@ -3528,35 +3562,46 @@ RID TextServerFallback::_shaped_text_substr(const RID &p_shaped, int64_t p_start if (p_length > 0) { new_sd->text = sd->text.substr(p_start - sd->start, p_length); + + int span_size = sd->spans.size(); + + new_sd->first_span = 0; + new_sd->last_span = span_size - 1; + for (int i = 0; i < span_size; i++) { + const ShapedTextDataFallback::Span &span = sd->spans[i]; + if (span.end <= p_start) { + new_sd->first_span = i + 1; + } else if (span.start >= p_start + p_length) { + new_sd->last_span = i - 1; + break; + } + } + int sd_size = sd->glyphs.size(); const Glyph *sd_glyphs = sd->glyphs.ptr(); for (int i = 0; i < sd_size; i++) { if ((sd_glyphs[i].start >= new_sd->start) && (sd_glyphs[i].end <= new_sd->end)) { Glyph gl = sd_glyphs[i]; + if (gl.span_index >= 0) { + gl.span_index -= new_sd->first_span; + } if (gl.end == p_start + p_length && ((gl.flags & GRAPHEME_IS_SOFT_HYPHEN) == GRAPHEME_IS_SOFT_HYPHEN)) { gl.index = 0x00ad; gl.advance = font_get_glyph_advance(gl.font_rid, gl.font_size, 0x00ad).x; } - Variant key; - bool find_embedded = false; - if (gl.count == 1) { - for (const KeyValue &E : sd->objects) { - if (E.value.start == gl.start) { - find_embedded = true; - key = E.key; - new_sd->objects[key] = E.value; - break; + if ((gl.flags & GRAPHEME_IS_EMBEDDED_OBJECT) == GRAPHEME_IS_EMBEDDED_OBJECT && gl.span_index >= 0 && gl.span_index < span_size) { + Variant key = sd->spans[gl.span_index].embedded_key; + if (key != Variant()) { + ShapedTextDataFallback::EmbeddedObject obj = sd->objects[key]; + if (new_sd->orientation == ORIENTATION_HORIZONTAL) { + obj.rect.position.x = new_sd->width; + new_sd->width += obj.rect.size.x; + } else { + obj.rect.position.y = new_sd->width; + new_sd->width += obj.rect.size.y; } - } - } - if (find_embedded) { - if (new_sd->orientation == ORIENTATION_HORIZONTAL) { - new_sd->objects[key].rect.position.x = new_sd->width; - new_sd->width += new_sd->objects[key].rect.size.x; - } else { - new_sd->objects[key].rect.position.y = new_sd->width; - new_sd->width += new_sd->objects[key].rect.size.y; + new_sd->objects[key] = obj; } } else { if (gl.font_rid.is_valid()) { @@ -4041,7 +4086,8 @@ void TextServerFallback::_shaped_text_overrun_trim_to_width(const RID &p_shaped_ spans = parent_sd->spans; } - if (spans.size() == 0) { + int span_size = spans.size(); + if (span_size == 0) { return; } @@ -4052,7 +4098,7 @@ void TextServerFallback::_shaped_text_overrun_trim_to_width(const RID &p_shaped_ // Find usable fonts, if fonts from the last glyph do not have required chars. RID dot_gl_font_rid = sd_glyphs[sd_size - 1].font_rid; if (!_font_has_char(dot_gl_font_rid, sd->el_char)) { - const Array &fonts = spans[spans.size() - 1].fonts; + const Array &fonts = spans[span_size - 1].fonts; for (int i = 0; i < fonts.size(); i++) { if (_font_has_char(fonts[i], sd->el_char)) { dot_gl_font_rid = fonts[i]; @@ -4062,7 +4108,7 @@ void TextServerFallback::_shaped_text_overrun_trim_to_width(const RID &p_shaped_ } if (!found_el_char && OS::get_singleton()->has_feature("system_fonts") && fonts.size() > 0 && _font_is_allow_system_fallback(fonts[0])) { const char32_t u32str[] = { sd->el_char, 0 }; - RID rid = _find_sys_font_for_text(fonts[0], String(), spans[spans.size() - 1].language, u32str); + RID rid = _find_sys_font_for_text(fonts[0], String(), spans[span_size - 1].language, u32str); if (rid.is_valid()) { dot_gl_font_rid = rid; found_el_char = true; @@ -4075,7 +4121,7 @@ void TextServerFallback::_shaped_text_overrun_trim_to_width(const RID &p_shaped_ bool found_dot_char = false; dot_gl_font_rid = sd_glyphs[sd_size - 1].font_rid; if (!_font_has_char(dot_gl_font_rid, '.')) { - const Array &fonts = spans[spans.size() - 1].fonts; + const Array &fonts = spans[span_size - 1].fonts; for (int i = 0; i < fonts.size(); i++) { if (_font_has_char(fonts[i], '.')) { dot_gl_font_rid = fonts[i]; @@ -4084,7 +4130,7 @@ void TextServerFallback::_shaped_text_overrun_trim_to_width(const RID &p_shaped_ } } if (!found_dot_char && OS::get_singleton()->has_feature("system_fonts") && fonts.size() > 0 && _font_is_allow_system_fallback(fonts[0])) { - RID rid = _find_sys_font_for_text(fonts[0], String(), spans[spans.size() - 1].language, "."); + RID rid = _find_sys_font_for_text(fonts[0], String(), spans[span_size - 1].language, "."); if (rid.is_valid()) { dot_gl_font_rid = rid; } @@ -4093,7 +4139,7 @@ void TextServerFallback::_shaped_text_overrun_trim_to_width(const RID &p_shaped_ } RID whitespace_gl_font_rid = sd_glyphs[sd_size - 1].font_rid; if (!_font_has_char(whitespace_gl_font_rid, ' ')) { - const Array &fonts = spans[spans.size() - 1].fonts; + const Array &fonts = spans[span_size - 1].fonts; for (int i = 0; i < fonts.size(); i++) { if (_font_has_char(fonts[i], ' ')) { whitespace_gl_font_rid = fonts[i]; @@ -4265,6 +4311,7 @@ bool TextServerFallback::_shaped_text_shape(const RID &p_shaped) { sd->width += sd->objects[span.embedded_key].rect.size.y; } Glyph gl; + gl.span_index = i; gl.start = span.start; gl.end = span.end; gl.count = 1; @@ -4281,6 +4328,7 @@ bool TextServerFallback::_shaped_text_shape(const RID &p_shaped) { RID prev_font; for (int j = span.start; j < span.end; j++) { Glyph gl; + gl.span_index = i; gl.start = j; gl.end = j + 1; gl.count = 1; diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h index 7f12ad593b6..5f2b1e644c1 100644 --- a/modules/text_server_fb/text_server_fb.h +++ b/modules/text_server_fb/text_server_fb.h @@ -425,6 +425,8 @@ class TextServerFallback : public TextServerExtension { Variant meta; }; Vector spans; + int first_span = 0; // First span in the parent ShapedTextData. + int last_span = 0; struct EmbeddedObject { int start = -1; @@ -809,6 +811,7 @@ public: MODBIND1RC(int64_t, shaped_get_span_count, const RID &); MODBIND2RC(Variant, shaped_get_span_meta, const RID &, int64_t); + MODBIND2RC(Variant, shaped_get_span_embedded_object, const RID &, int64_t); MODBIND5(shaped_set_span_update_font, const RID &, int64_t, const TypedArray &, int64_t, const Dictionary &); MODBIND3RC(RID, shaped_text_substr, const RID &, int64_t, int64_t); diff --git a/scene/3d/label_3d.cpp b/scene/3d/label_3d.cpp index 6b3510a72a1..5d0b36c2af3 100644 --- a/scene/3d/label_3d.cpp +++ b/scene/3d/label_3d.cpp @@ -344,7 +344,7 @@ void Label3D::_generate_glyph_surfaces(const Glyph &p_glyph, Vector2 &r_offset, gl_uv = TS->font_get_glyph_uv_rect(p_glyph.font_rid, Vector2i(p_glyph.font_size, p_outline_size), p_glyph.index); texs = TS->font_get_glyph_texture_size(p_glyph.font_rid, Vector2i(p_glyph.font_size, p_outline_size), p_glyph.index); } - } else { + } else if (((p_glyph.flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) && ((p_glyph.flags & TextServer::GRAPHEME_IS_EMBEDDED_OBJECT) != TextServer::GRAPHEME_IS_EMBEDDED_OBJECT)) { gl_sz = TS->get_hex_code_box_size(p_glyph.font_size, p_glyph.index) * pixel_size; gl_of = Vector2(0, -gl_sz.y); } diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index 42b4e56b484..8ef9538ee37 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -311,7 +311,7 @@ void Label::_update_visible() { inline void draw_glyph(const Glyph &p_gl, const RID &p_canvas, const Color &p_font_color, const Vector2 &p_ofs) { if (p_gl.font_rid != RID()) { TS->font_draw_glyph(p_gl.font_rid, p_canvas, p_gl.font_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off), p_gl.index, p_font_color); - } else { + } else if (((p_gl.flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) && ((p_gl.flags & TextServer::GRAPHEME_IS_EMBEDDED_OBJECT) != TextServer::GRAPHEME_IS_EMBEDDED_OBJECT)) { TS->draw_hex_code_box(p_canvas, p_gl.font_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off), p_gl.index, p_font_color); } } diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index e302927692d..d3ee374595c 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -549,7 +549,7 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref if (font_size_it && font_size_it->font_size > 0) { font_size = font_size_it->font_size; } - l.text_buf->add_string(String::chr(0x200B), font, font_size); + l.text_buf->add_string(String::chr(0x200B), font, font_size, String(), it->rid); txt += "\n"; l.char_count++; remaining_characters--; @@ -791,7 +791,6 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o MutexLock lock(l.text_buf->get_mutex()); Item *it_from = l.from; - Item *it_to = (p_line + 1 < (int)p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr; if (it_from == nullptr) { return 0; @@ -1030,9 +1029,26 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o float box_start = 0.0; Color last_color = Color(0, 0, 0, 0); + Item *it = it_from; + int span = -1; for (int i = 0; i < gl_size; i++) { bool selected = selection.active && (sel_start != -1) && (glyphs[i].start >= sel_start) && (glyphs[i].end <= sel_end); - Item *it = _get_item_at_pos(it_from, it_to, glyphs[i].start); + if (glyphs[i].span_index != span) { + span = glyphs[i].span_index; + if (span >= 0) { + if ((glyphs[i].flags & TextServer::GRAPHEME_IS_EMBEDDED_OBJECT) == TextServer::GRAPHEME_IS_EMBEDDED_OBJECT) { + Item *new_it = items.get_or_null(TS->shaped_get_span_embedded_object(rid, span)); + if (new_it) { + it = new_it; + } + } else { + Item *new_it = items.get_or_null(TS->shaped_get_span_meta(rid, span)); + if (new_it) { + it = new_it; + } + } + } + } Color font_color = (step == DRAW_STEP_SHADOW || step == DRAW_STEP_OUTLINE || step == DRAW_STEP_TEXT) ? _find_color(it, p_base_color) : Color(); int outline_size = (step == DRAW_STEP_OUTLINE) ? _find_outline_size(it, p_outline_size) : 0; diff --git a/servers/text/text_server_dummy.h b/servers/text/text_server_dummy.h index 1a945ac221f..0549917737a 100644 --- a/servers/text/text_server_dummy.h +++ b/servers/text/text_server_dummy.h @@ -99,6 +99,7 @@ public: 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 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(); } virtual void shaped_set_span_update_font(const RID &p_shaped, int64_t p_index, const TypedArray &p_fonts, int64_t p_size, const Dictionary &p_opentype_features) override {} virtual RID shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) const override { return RID(); } virtual RID shaped_text_get_parent(const RID &p_shaped) const override { return RID(); } diff --git a/servers/text/text_server_extension.cpp b/servers/text/text_server_extension.cpp index 1c0d518e75a..cf281361725 100644 --- a/servers/text/text_server_extension.cpp +++ b/servers/text/text_server_extension.cpp @@ -264,6 +264,7 @@ void TextServerExtension::_bind_methods() { GDVIRTUAL_BIND(_shaped_get_span_count, "shaped"); GDVIRTUAL_BIND(_shaped_get_span_meta, "shaped", "index"); + GDVIRTUAL_BIND(_shaped_get_span_embedded_object, "shaped", "index"); GDVIRTUAL_BIND(_shaped_set_span_update_font, "shaped", "index", "fonts", "size", "opentype_features"); GDVIRTUAL_BIND(_shaped_text_substr, "shaped", "start", "length"); @@ -1167,11 +1168,17 @@ int64_t TextServerExtension::shaped_get_span_count(const RID &p_shaped) const { } Variant TextServerExtension::shaped_get_span_meta(const RID &p_shaped, int64_t p_index) const { - Variant ret = false; + Variant ret; GDVIRTUAL_CALL(_shaped_get_span_meta, p_shaped, p_index, ret); return ret; } +Variant TextServerExtension::shaped_get_span_embedded_object(const RID &p_shaped, int64_t p_index) const { + Variant ret; + GDVIRTUAL_CALL(_shaped_get_span_embedded_object, p_shaped, p_index, ret); + return ret; +} + void TextServerExtension::shaped_set_span_update_font(const RID &p_shaped, int64_t p_index, const TypedArray &p_fonts, int64_t p_size, const Dictionary &p_opentype_features) { GDVIRTUAL_CALL(_shaped_set_span_update_font, p_shaped, p_index, p_fonts, p_size, p_opentype_features); } diff --git a/servers/text/text_server_extension.h b/servers/text/text_server_extension.h index bd803be8aad..4539db229f3 100644 --- a/servers/text/text_server_extension.h +++ b/servers/text/text_server_extension.h @@ -437,9 +437,11 @@ public: virtual int64_t shaped_get_span_count(const RID &p_shaped) const override; virtual Variant shaped_get_span_meta(const RID &p_shaped, int64_t p_index) const override; + virtual Variant shaped_get_span_embedded_object(const RID &p_shaped, int64_t p_index) const override; virtual void shaped_set_span_update_font(const RID &p_shaped, int64_t p_index, const TypedArray &p_fonts, int64_t p_size, const Dictionary &p_opentype_features = Dictionary()) override; GDVIRTUAL1RC_REQUIRED(int64_t, _shaped_get_span_count, RID); GDVIRTUAL2RC_REQUIRED(Variant, _shaped_get_span_meta, RID, int64_t); + GDVIRTUAL2RC_REQUIRED(Variant, _shaped_get_span_embedded_object, RID, int64_t); GDVIRTUAL5_REQUIRED(_shaped_set_span_update_font, RID, int64_t, const TypedArray &, int64_t, const Dictionary &); virtual RID shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) const override; diff --git a/servers/text_server.cpp b/servers/text_server.cpp index d2cf4674ae0..d7db3c7e5aa 100644 --- a/servers/text_server.cpp +++ b/servers/text_server.cpp @@ -420,6 +420,7 @@ void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("shaped_get_span_count", "shaped"), &TextServer::shaped_get_span_count); ClassDB::bind_method(D_METHOD("shaped_get_span_meta", "shaped", "index"), &TextServer::shaped_get_span_meta); + ClassDB::bind_method(D_METHOD("shaped_get_span_embedded_object", "shaped", "index"), &TextServer::shaped_get_span_embedded_object); ClassDB::bind_method(D_METHOD("shaped_set_span_update_font", "shaped", "index", "fonts", "size", "opentype_features"), &TextServer::shaped_set_span_update_font, DEFVAL(Dictionary())); ClassDB::bind_method(D_METHOD("shaped_text_substr", "shaped", "start", "length"), &TextServer::shaped_text_substr); diff --git a/servers/text_server.h b/servers/text_server.h index ba3fdaa35e9..e108ca92182 100644 --- a/servers/text_server.h +++ b/servers/text_server.h @@ -464,6 +464,7 @@ public: virtual int64_t shaped_get_span_count(const RID &p_shaped) const = 0; virtual Variant shaped_get_span_meta(const RID &p_shaped, int64_t p_index) const = 0; + virtual Variant shaped_get_span_embedded_object(const RID &p_shaped, int64_t p_index) const = 0; virtual void shaped_set_span_update_font(const RID &p_shaped, int64_t p_index, const TypedArray &p_fonts, int64_t p_size, const Dictionary &p_opentype_features = Dictionary()) = 0; virtual RID shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) const = 0; // Copy shaped substring (e.g. line break) without reshaping, but correctly reordered, preservers range. @@ -578,9 +579,11 @@ struct Glyph { float advance = 0.f; // Advance to the next glyph along baseline(x for horizontal layout, y for vertical). RID font_rid; // Font resource. - int font_size = 0; // Font size; + int font_size = 0; // Font size. int32_t index = 0; // Glyph index (font specific) or UTF-32 codepoint (for the invalid glyphs). + int span_index = -1; + bool operator==(const Glyph &p_a) const; bool operator!=(const Glyph &p_a) const;