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;