From 433de7f1a86191d912d8cd5e2ed53e76916170fe Mon Sep 17 00:00:00 2001 From: bruvzg <7645683+bruvzg@users.noreply.github.com> Date: Mon, 11 Mar 2024 16:28:58 +0200 Subject: [PATCH] [RTL] Use "visible characters" property for inline object visibility --- doc/classes/TextServer.xml | 16 +++++++++ doc/classes/TextServerExtension.xml | 14 ++++++++ modules/text_server_adv/text_server_adv.cpp | 40 ++++++++++++++++++--- modules/text_server_adv/text_server_adv.h | 5 ++- modules/text_server_fb/text_server_fb.cpp | 37 ++++++++++++++++--- modules/text_server_fb/text_server_fb.h | 5 ++- scene/gui/rich_text_label.cpp | 10 ++++++ servers/text/text_server_extension.cpp | 14 ++++++++ servers/text/text_server_extension.h | 4 +++ servers/text_server.cpp | 2 ++ servers/text_server.h | 2 ++ 11 files changed, 137 insertions(+), 12 deletions(-) diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml index 9a17fb3def57..bea503865cd0 100644 --- a/doc/classes/TextServer.xml +++ b/doc/classes/TextServer.xml @@ -1367,6 +1367,22 @@ Breaks text to the lines and columns. Returns character ranges for each segment. + + + + + + Returns the glyph index of the inline object. + + + + + + + + Returns the character range of the inline object. + + diff --git a/doc/classes/TextServerExtension.xml b/doc/classes/TextServerExtension.xml index 5beb5797dbdd..e95b22048402 100644 --- a/doc/classes/TextServerExtension.xml +++ b/doc/classes/TextServerExtension.xml @@ -1180,6 +1180,20 @@ + + + + + + + + + + + + + + diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index 3da19aaee8ca..4822c29613a8 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -4000,7 +4000,7 @@ void TextServerAdvanced::full_copy(ShapedTextDataAdvanced *p_shaped) { ShapedTextDataAdvanced *parent = shaped_owner.get_or_null(p_shaped->parent); for (const KeyValue &E : parent->objects) { - if (E.value.pos >= p_shaped->start && E.value.pos < p_shaped->end) { + if (E.value.start >= p_shaped->start && E.value.start < p_shaped->end) { p_shaped->objects[E.key] = E.value; } } @@ -4300,7 +4300,8 @@ bool TextServerAdvanced::_shaped_text_add_object(const RID &p_shaped, const Vari ShapedTextDataAdvanced::EmbeddedObject obj; obj.inline_align = p_inline_align; obj.rect.size = p_size; - obj.pos = span.start; + obj.start = span.start; + obj.end = span.end; obj.baseline = p_baseline; sd->spans.push_back(span); @@ -4335,7 +4336,7 @@ bool TextServerAdvanced::_shaped_text_resize_object(const RID &p_shaped, const V Variant key; if (gl.count == 1) { for (const KeyValue &E : sd->objects) { - if (E.value.pos == gl.start) { + if (E.value.start == gl.start) { key = E.key; break; } @@ -4386,7 +4387,7 @@ void TextServerAdvanced::_realign(ShapedTextDataAdvanced *p_sd) const { double full_ascent = p_sd->ascent; double full_descent = p_sd->descent; for (KeyValue &E : p_sd->objects) { - if ((E.value.pos >= p_sd->start) && (E.value.pos < p_sd->end)) { + if ((E.value.start >= p_sd->start) && (E.value.start < p_sd->end)) { if (p_sd->orientation == ORIENTATION_HORIZONTAL) { switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) { case INLINE_ALIGNMENT_TO_TOP: { @@ -4598,7 +4599,7 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S bool find_embedded = false; if (gl.count == 1) { for (const KeyValue &E : p_sd->objects) { - if (E.value.pos == gl.start) { + if (E.value.start == gl.start) { find_embedded = true; key = E.key; p_new_sd->objects[key] = E.value; @@ -6437,6 +6438,35 @@ Rect2 TextServerAdvanced::_shaped_text_get_object_rect(const RID &p_shaped, cons return sd->objects[p_key].rect; } +Vector2i TextServerAdvanced::_shaped_text_get_object_range(const RID &p_shaped, const Variant &p_key) const { + const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); + ERR_FAIL_NULL_V(sd, Vector2i()); + + MutexLock lock(sd->mutex); + ERR_FAIL_COND_V(!sd->objects.has(p_key), Vector2i()); + return Vector2i(sd->objects[p_key].start, sd->objects[p_key].end); +} + +int64_t TextServerAdvanced::_shaped_text_get_object_glyph(const RID &p_shaped, const Variant &p_key) const { + const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); + ERR_FAIL_NULL_V(sd, -1); + + MutexLock lock(sd->mutex); + ERR_FAIL_COND_V(!sd->objects.has(p_key), -1); + if (!sd->valid) { + const_cast(this)->_shaped_text_shape(p_shaped); + } + const ShapedTextDataAdvanced::EmbeddedObject &obj = sd->objects[p_key]; + int sd_size = sd->glyphs.size(); + const Glyph *sd_glyphs = sd->glyphs.ptr(); + for (int i = 0; i < sd_size; i++) { + if (obj.start == sd_glyphs[i].start) { + return i; + } + } + return -1; +} + Size2 TextServerAdvanced::_shaped_text_get_size(const RID &p_shaped) const { const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_NULL_V(sd, Size2()); diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h index 3f04c17d9cb3..9f70fdb0ef68 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -475,7 +475,8 @@ class TextServerAdvanced : public TextServerExtension { Vector spans; struct EmbeddedObject { - int pos = 0; + int start = -1; + int end = -1; InlineAlignment inline_align = INLINE_ALIGNMENT_CENTER; Rect2 rect; double baseline = 0; @@ -957,6 +958,8 @@ class TextServerAdvanced : public TextServerExtension { MODBIND1RC(Array, shaped_text_get_objects, const RID &); MODBIND2RC(Rect2, shaped_text_get_object_rect, const RID &, const Variant &); + MODBIND2RC(Vector2i, shaped_text_get_object_range, const RID &, const Variant &); + MODBIND2RC(int64_t, shaped_text_get_object_glyph, const RID &, const Variant &); MODBIND1RC(Size2, shaped_text_get_size, const RID &); MODBIND1RC(double, shaped_text_get_ascent, const RID &); diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index 7e34de0bbc67..10a73d3db185 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -2874,7 +2874,7 @@ void TextServerFallback::full_copy(ShapedTextDataFallback *p_shaped) { ShapedTextDataFallback *parent = shaped_owner.get_or_null(p_shaped->parent); for (const KeyValue &E : parent->objects) { - if (E.value.pos >= p_shaped->start && E.value.pos < p_shaped->end) { + if (E.value.start >= p_shaped->start && E.value.start < p_shaped->end) { p_shaped->objects[E.key] = E.value; } } @@ -3170,7 +3170,8 @@ bool TextServerFallback::_shaped_text_add_object(const RID &p_shaped, const Vari ShapedTextDataFallback::EmbeddedObject obj; obj.inline_align = p_inline_align; obj.rect.size = p_size; - obj.pos = span.start; + obj.start = span.start; + obj.end = span.end; obj.baseline = p_baseline; sd->spans.push_back(span); @@ -3205,7 +3206,7 @@ bool TextServerFallback::_shaped_text_resize_object(const RID &p_shaped, const V Variant key; if (gl.count == 1) { for (const KeyValue &E : sd->objects) { - if (E.value.pos == gl.start) { + if (E.value.start == gl.start) { key = E.key; break; } @@ -3254,7 +3255,7 @@ void TextServerFallback::_realign(ShapedTextDataFallback *p_sd) const { double full_ascent = p_sd->ascent; double full_descent = p_sd->descent; for (KeyValue &E : p_sd->objects) { - if ((E.value.pos >= p_sd->start) && (E.value.pos < p_sd->end)) { + if ((E.value.start >= p_sd->start) && (E.value.start < p_sd->end)) { if (p_sd->orientation == ORIENTATION_HORIZONTAL) { switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) { case INLINE_ALIGNMENT_TO_TOP: { @@ -3375,7 +3376,7 @@ RID TextServerFallback::_shaped_text_substr(const RID &p_shaped, int64_t p_start bool find_embedded = false; if (gl.count == 1) { for (const KeyValue &E : sd->objects) { - if (E.value.pos == gl.start) { + if (E.value.start == gl.start) { find_embedded = true; key = E.key; new_sd->objects[key] = E.value; @@ -4288,6 +4289,32 @@ Rect2 TextServerFallback::_shaped_text_get_object_rect(const RID &p_shaped, cons return sd->objects[p_key].rect; } +Vector2i TextServerFallback::_shaped_text_get_object_range(const RID &p_shaped, const Variant &p_key) const { + const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); + ERR_FAIL_NULL_V(sd, Vector2i()); + + MutexLock lock(sd->mutex); + ERR_FAIL_COND_V(!sd->objects.has(p_key), Vector2i()); + return Vector2i(sd->objects[p_key].start, sd->objects[p_key].end); +} + +int64_t TextServerFallback::_shaped_text_get_object_glyph(const RID &p_shaped, const Variant &p_key) const { + const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); + ERR_FAIL_NULL_V(sd, -1); + + MutexLock lock(sd->mutex); + ERR_FAIL_COND_V(!sd->objects.has(p_key), -1); + const ShapedTextDataFallback::EmbeddedObject &obj = sd->objects[p_key]; + int sd_size = sd->glyphs.size(); + const Glyph *sd_glyphs = sd->glyphs.ptr(); + for (int i = 0; i < sd_size; i++) { + if (obj.start == sd_glyphs[i].start) { + return i; + } + } + return -1; +} + Size2 TextServerFallback::_shaped_text_get_size(const RID &p_shaped) const { const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_NULL_V(sd, Size2()); diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h index 9cdf20f3fab9..401c02a8ac33 100644 --- a/modules/text_server_fb/text_server_fb.h +++ b/modules/text_server_fb/text_server_fb.h @@ -421,7 +421,8 @@ class TextServerFallback : public TextServerExtension { Vector spans; struct EmbeddedObject { - int pos = 0; + int start = -1; + int end = -1; InlineAlignment inline_align = INLINE_ALIGNMENT_CENTER; Rect2 rect; double baseline = 0; @@ -825,6 +826,8 @@ class TextServerFallback : public TextServerExtension { MODBIND1RC(Array, shaped_text_get_objects, const RID &); MODBIND2RC(Rect2, shaped_text_get_object_rect, const RID &, const Variant &); + MODBIND2RC(Vector2i, shaped_text_get_object_range, const RID &, const Variant &); + MODBIND2RC(int64_t, shaped_text_get_object_glyph, const RID &, const Variant &); MODBIND1RC(Size2, shaped_text_get_size, const RID &); MODBIND1RC(double, shaped_text_get_ascent, const RID &); diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 5fe26aac0696..cb7ccb583e8f 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -935,6 +935,16 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o for (int i = 0; i < objects.size(); i++) { Item *it = items.get_or_null(objects[i]); if (it != nullptr) { + Vector2i obj_range = TS->shaped_text_get_object_range(rid, objects[i]); + if (trim_chars && l.char_offset + obj_range.y > visible_characters) { + continue; + } + if (trim_glyphs_ltr || trim_glyphs_rtl) { + int obj_glyph = r_processed_glyphs + TS->shaped_text_get_object_glyph(rid, objects[i]); + if ((trim_glyphs_ltr && (obj_glyph >= visible_glyphs)) || (trim_glyphs_rtl && (obj_glyph < total_glyphs - visible_glyphs))) { + continue; + } + } Rect2 rect = TS->shaped_text_get_object_rect(rid, objects[i]); //draw_rect(rect, Color(1,0,0), false, 2); //DEBUG_RECTS switch (it->type) { diff --git a/servers/text/text_server_extension.cpp b/servers/text/text_server_extension.cpp index d5080e586d18..c8e87cc7fcd8 100644 --- a/servers/text/text_server_extension.cpp +++ b/servers/text/text_server_extension.cpp @@ -293,6 +293,8 @@ void TextServerExtension::_bind_methods() { GDVIRTUAL_BIND(_shaped_text_get_objects, "shaped"); GDVIRTUAL_BIND(_shaped_text_get_object_rect, "shaped", "key"); + GDVIRTUAL_BIND(_shaped_text_get_object_range, "shaped", "key"); + GDVIRTUAL_BIND(_shaped_text_get_object_glyph, "shaped", "key"); GDVIRTUAL_BIND(_shaped_text_get_size, "shaped"); GDVIRTUAL_BIND(_shaped_text_get_ascent, "shaped"); @@ -1284,6 +1286,18 @@ Rect2 TextServerExtension::shaped_text_get_object_rect(const RID &p_shaped, cons return ret; } +Vector2i TextServerExtension::shaped_text_get_object_range(const RID &p_shaped, const Variant &p_key) const { + Vector2i ret; + GDVIRTUAL_CALL(_shaped_text_get_object_range, p_shaped, p_key, ret); + return ret; +} + +int64_t TextServerExtension::shaped_text_get_object_glyph(const RID &p_shaped, const Variant &p_key) const { + int64_t ret = -1; + GDVIRTUAL_CALL(_shaped_text_get_object_glyph, p_shaped, p_key, ret); + return ret; +} + Size2 TextServerExtension::shaped_text_get_size(const RID &p_shaped) const { Size2 ret; GDVIRTUAL_CALL(_shaped_text_get_size, p_shaped, ret); diff --git a/servers/text/text_server_extension.h b/servers/text/text_server_extension.h index 4f9ea55d33db..bb786407893f 100644 --- a/servers/text/text_server_extension.h +++ b/servers/text/text_server_extension.h @@ -486,8 +486,12 @@ class TextServerExtension : public TextServer { virtual Array shaped_text_get_objects(const RID &p_shaped) const override; virtual Rect2 shaped_text_get_object_rect(const RID &p_shaped, const Variant &p_key) const override; + virtual Vector2i shaped_text_get_object_range(const RID &p_shaped, const Variant &p_key) const override; + virtual int64_t shaped_text_get_object_glyph(const RID &p_shaped, const Variant &p_key) const override; GDVIRTUAL1RC(Array, _shaped_text_get_objects, RID); GDVIRTUAL2RC(Rect2, _shaped_text_get_object_rect, RID, const Variant &); + GDVIRTUAL2RC(Vector2i, _shaped_text_get_object_range, RID, const Variant &); + GDVIRTUAL2RC(int64_t, _shaped_text_get_object_glyph, RID, const Variant &); virtual Size2 shaped_text_get_size(const RID &p_shaped) const override; virtual double shaped_text_get_ascent(const RID &p_shaped) const override; diff --git a/servers/text_server.cpp b/servers/text_server.cpp index d1dadbc8399a..9cba96802625 100644 --- a/servers/text_server.cpp +++ b/servers/text_server.cpp @@ -443,6 +443,8 @@ void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("shaped_text_get_objects", "shaped"), &TextServer::shaped_text_get_objects); ClassDB::bind_method(D_METHOD("shaped_text_get_object_rect", "shaped", "key"), &TextServer::shaped_text_get_object_rect); + ClassDB::bind_method(D_METHOD("shaped_text_get_object_range", "shaped", "key"), &TextServer::shaped_text_get_object_range); + ClassDB::bind_method(D_METHOD("shaped_text_get_object_glyph", "shaped", "key"), &TextServer::shaped_text_get_object_glyph); ClassDB::bind_method(D_METHOD("shaped_text_get_size", "shaped"), &TextServer::shaped_text_get_size); ClassDB::bind_method(D_METHOD("shaped_text_get_ascent", "shaped"), &TextServer::shaped_text_get_ascent); diff --git a/servers/text_server.h b/servers/text_server.h index dfd6140fde10..55c7d8a88eab 100644 --- a/servers/text_server.h +++ b/servers/text_server.h @@ -490,6 +490,8 @@ class TextServer : public RefCounted { virtual Array shaped_text_get_objects(const RID &p_shaped) const = 0; virtual Rect2 shaped_text_get_object_rect(const RID &p_shaped, const Variant &p_key) const = 0; + virtual Vector2i shaped_text_get_object_range(const RID &p_shaped, const Variant &p_key) const = 0; + virtual int64_t shaped_text_get_object_glyph(const RID &p_shaped, const Variant &p_key) const = 0; virtual Size2 shaped_text_get_size(const RID &p_shaped) const = 0; virtual double shaped_text_get_ascent(const RID &p_shaped) const = 0;