Merge pull request #57511 from bruvzg/ts_font_change
Improve performance of the font change.
This commit is contained in:
commit
2daa3ae1fd
22 changed files with 705 additions and 491 deletions
|
@ -26,6 +26,7 @@
|
|||
<argument index="2" name="size" type="int" />
|
||||
<argument index="3" name="opentype_features" type="Dictionary" default="{}" />
|
||||
<argument index="4" name="language" type="String" default="""" />
|
||||
<argument index="5" name="meta" type="Variant" default="null" />
|
||||
<description>
|
||||
Adds text span and font to draw it.
|
||||
</description>
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
<argument index="2" name="size" type="int" />
|
||||
<argument index="3" name="opentype_features" type="Dictionary" default="{}" />
|
||||
<argument index="4" name="language" type="String" default="""" />
|
||||
<argument index="5" name="meta" type="Variant" default="null" />
|
||||
<description>
|
||||
Adds text span and font to draw it.
|
||||
</description>
|
||||
|
|
|
@ -896,6 +896,32 @@
|
|||
[b]Note:[/b] This function is used by during project export, to include TextServer database.
|
||||
</description>
|
||||
</method>
|
||||
<method name="shaped_get_span_count" qualifiers="const">
|
||||
<return type="int" />
|
||||
<argument index="0" name="shaped" type="RID" />
|
||||
<description>
|
||||
Returns number of text spans added using [method shaped_text_add_string] or [method shaped_text_add_object].
|
||||
</description>
|
||||
</method>
|
||||
<method name="shaped_get_span_meta" qualifiers="const">
|
||||
<return type="Variant" />
|
||||
<argument index="0" name="shaped" type="RID" />
|
||||
<argument index="1" name="index" type="int" />
|
||||
<description>
|
||||
Returns text span metadata.
|
||||
</description>
|
||||
</method>
|
||||
<method name="shaped_set_span_update_font">
|
||||
<return type="void" />
|
||||
<argument index="0" name="shaped" type="RID" />
|
||||
<argument index="1" name="index" type="int" />
|
||||
<argument index="2" name="fonts" type="Array" />
|
||||
<argument index="3" name="size" type="int" />
|
||||
<argument index="4" name="opentype_features" type="Dictionary" default="{}" />
|
||||
<description>
|
||||
Changes text span font, font size and OpenType features, without changing the text.
|
||||
</description>
|
||||
</method>
|
||||
<method name="shaped_text_add_object">
|
||||
<return type="bool" />
|
||||
<argument index="0" name="shaped" type="RID" />
|
||||
|
@ -915,6 +941,7 @@
|
|||
<argument index="3" name="size" type="int" />
|
||||
<argument index="4" name="opentype_features" type="Dictionary" default="{}" />
|
||||
<argument index="5" name="language" type="String" default="""" />
|
||||
<argument index="6" name="meta" type="Variant" default="null" />
|
||||
<description>
|
||||
Adds text span and font to draw it to the text buffer.
|
||||
</description>
|
||||
|
|
|
@ -904,6 +904,32 @@
|
|||
[b]Note:[/b] This function is used by during project export, to include TextServer database.
|
||||
</description>
|
||||
</method>
|
||||
<method name="_shaped_get_span_count" qualifiers="virtual const">
|
||||
<return type="int" />
|
||||
<argument index="0" name="shaped" type="RID" />
|
||||
<description>
|
||||
Returns number of text spans added using [method _shaped_text_add_string] or [method _shaped_text_add_object].
|
||||
</description>
|
||||
</method>
|
||||
<method name="_shaped_get_span_meta" qualifiers="virtual const">
|
||||
<return type="Variant" />
|
||||
<argument index="0" name="shaped" type="RID" />
|
||||
<argument index="1" name="index" type="int" />
|
||||
<description>
|
||||
Returns text span metadata.
|
||||
</description>
|
||||
</method>
|
||||
<method name="_shaped_set_span_update_font" qualifiers="virtual">
|
||||
<return type="void" />
|
||||
<argument index="0" name="shaped" type="RID" />
|
||||
<argument index="1" name="index" type="int" />
|
||||
<argument index="2" name="fonts" type="Array" />
|
||||
<argument index="3" name="size" type="int" />
|
||||
<argument index="4" name="opentype_features" type="Dictionary" />
|
||||
<description>
|
||||
Changes text span font, font size and OpenType features, without changing the text.
|
||||
</description>
|
||||
</method>
|
||||
<method name="_shaped_text_add_object" qualifiers="virtual">
|
||||
<return type="bool" />
|
||||
<argument index="0" name="shaped" type="RID" />
|
||||
|
@ -923,6 +949,7 @@
|
|||
<argument index="3" name="size" type="int" />
|
||||
<argument index="4" name="opentype_features" type="Dictionary" />
|
||||
<argument index="5" name="language" type="String" />
|
||||
<argument index="6" name="meta" type="Variant" />
|
||||
<description>
|
||||
Adds text span and font to draw it to the text buffer.
|
||||
</description>
|
||||
|
|
|
@ -2901,7 +2901,7 @@ void TextServerAdvanced::font_set_global_oversampling(float p_oversampling) {
|
|||
List<RID> text_bufs;
|
||||
shaped_owner.get_owned_list(&text_bufs);
|
||||
for (const RID &E : text_bufs) {
|
||||
invalidate(shaped_owner.get_or_null(E));
|
||||
invalidate(shaped_owner.get_or_null(E), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2936,7 +2936,7 @@ int TextServerAdvanced::_convert_pos_inv(const ShapedTextDataAdvanced *p_sd, int
|
|||
return limit;
|
||||
}
|
||||
|
||||
void TextServerAdvanced::invalidate(TextServerAdvanced::ShapedTextDataAdvanced *p_shaped) {
|
||||
void TextServerAdvanced::invalidate(TextServerAdvanced::ShapedTextDataAdvanced *p_shaped, bool p_text) {
|
||||
p_shaped->valid = false;
|
||||
p_shaped->sort_valid = false;
|
||||
p_shaped->line_breaks_valid = false;
|
||||
|
@ -2951,27 +2951,32 @@ void TextServerAdvanced::invalidate(TextServerAdvanced::ShapedTextDataAdvanced *
|
|||
p_shaped->glyphs_logical.clear();
|
||||
p_shaped->overrun_trim_data = TrimData();
|
||||
p_shaped->utf16 = Char16String();
|
||||
if (p_shaped->script_iter != nullptr) {
|
||||
memdelete(p_shaped->script_iter);
|
||||
p_shaped->script_iter = nullptr;
|
||||
}
|
||||
for (int i = 0; i < p_shaped->bidi_iter.size(); i++) {
|
||||
ubidi_close(p_shaped->bidi_iter[i]);
|
||||
}
|
||||
p_shaped->bidi_iter.clear();
|
||||
|
||||
if (p_text) {
|
||||
if (p_shaped->script_iter != nullptr) {
|
||||
memdelete(p_shaped->script_iter);
|
||||
p_shaped->script_iter = nullptr;
|
||||
}
|
||||
p_shaped->break_ops_valid = false;
|
||||
p_shaped->js_ops_valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
void TextServerAdvanced::full_copy(ShapedTextDataAdvanced *p_shaped) {
|
||||
ShapedTextDataAdvanced *parent = shaped_owner.get_or_null(p_shaped->parent);
|
||||
|
||||
for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : parent->objects) {
|
||||
for (const KeyValue<Variant, ShapedTextDataAdvanced::EmbeddedObject> &E : parent->objects) {
|
||||
if (E.value.pos >= p_shaped->start && E.value.pos < p_shaped->end) {
|
||||
p_shaped->objects[E.key] = E.value;
|
||||
}
|
||||
}
|
||||
|
||||
for (int k = 0; k < parent->spans.size(); k++) {
|
||||
ShapedTextDataAdvanced::Span span = parent->spans[k];
|
||||
for (int i = 0; i < parent->spans.size(); i++) {
|
||||
ShapedTextDataAdvanced::Span span = parent->spans[i];
|
||||
if (span.start >= p_shaped->end || span.end <= p_shaped->start) {
|
||||
continue;
|
||||
}
|
||||
|
@ -3004,7 +3009,7 @@ void TextServerAdvanced::shaped_text_clear(RID p_shaped) {
|
|||
sd->spans.clear();
|
||||
sd->objects.clear();
|
||||
sd->bidi_override.clear();
|
||||
invalidate(sd);
|
||||
invalidate(sd, true);
|
||||
}
|
||||
|
||||
void TextServerAdvanced::shaped_text_set_direction(RID p_shaped, TextServer::Direction p_direction) {
|
||||
|
@ -3017,7 +3022,7 @@ void TextServerAdvanced::shaped_text_set_direction(RID p_shaped, TextServer::Dir
|
|||
full_copy(sd);
|
||||
}
|
||||
sd->direction = p_direction;
|
||||
invalidate(sd);
|
||||
invalidate(sd, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3047,7 +3052,7 @@ void TextServerAdvanced::shaped_text_set_custom_punctuation(RID p_shaped, const
|
|||
full_copy(sd);
|
||||
}
|
||||
sd->custom_punct = p_punct;
|
||||
invalidate(sd);
|
||||
invalidate(sd, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3070,7 +3075,7 @@ void TextServerAdvanced::shaped_text_set_bidi_override(RID p_shaped, const Array
|
|||
for (int i = 0; i < p_override.size(); i++) {
|
||||
sd->bidi_override.push_back(p_override[i]);
|
||||
}
|
||||
invalidate(sd);
|
||||
invalidate(sd, false);
|
||||
}
|
||||
|
||||
void TextServerAdvanced::shaped_text_set_orientation(RID p_shaped, TextServer::Orientation p_orientation) {
|
||||
|
@ -3083,7 +3088,7 @@ void TextServerAdvanced::shaped_text_set_orientation(RID p_shaped, TextServer::O
|
|||
full_copy(sd);
|
||||
}
|
||||
sd->orientation = p_orientation;
|
||||
invalidate(sd);
|
||||
invalidate(sd, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3095,7 +3100,7 @@ void TextServerAdvanced::shaped_text_set_preserve_invalid(RID p_shaped, bool p_e
|
|||
ERR_FAIL_COND(sd->parent != RID());
|
||||
if (sd->preserve_invalid != p_enabled) {
|
||||
sd->preserve_invalid = p_enabled;
|
||||
invalidate(sd);
|
||||
invalidate(sd, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3117,7 +3122,7 @@ void TextServerAdvanced::shaped_text_set_preserve_control(RID p_shaped, bool p_e
|
|||
full_copy(sd);
|
||||
}
|
||||
sd->preserve_control = p_enabled;
|
||||
invalidate(sd);
|
||||
invalidate(sd, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3137,7 +3142,41 @@ TextServer::Orientation TextServerAdvanced::shaped_text_get_orientation(RID p_sh
|
|||
return sd->orientation;
|
||||
}
|
||||
|
||||
bool TextServerAdvanced::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
|
||||
int TextServerAdvanced::shaped_get_span_count(RID p_shaped) const {
|
||||
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, 0);
|
||||
return sd->spans.size();
|
||||
}
|
||||
|
||||
Variant TextServerAdvanced::shaped_get_span_meta(RID p_shaped, int p_index) const {
|
||||
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, Variant());
|
||||
ERR_FAIL_INDEX_V(p_index, sd->spans.size(), Variant());
|
||||
return sd->spans[p_index].meta;
|
||||
}
|
||||
|
||||
void TextServerAdvanced::shaped_set_span_update_font(RID p_shaped, int p_index, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features) {
|
||||
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND(!sd);
|
||||
ERR_FAIL_INDEX(p_index, sd->spans.size());
|
||||
|
||||
ShapedTextDataAdvanced::Span &span = sd->spans.write[p_index];
|
||||
bool changed = (span.font_size != p_size) || (span.features != p_opentype_features) || (p_fonts.size() != span.fonts.size());
|
||||
if (!changed) {
|
||||
for (int i = 0; i < p_fonts.size(); i++) {
|
||||
changed = changed || (span.fonts[i] != p_fonts[i]);
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
span.fonts = p_fonts;
|
||||
span.font_size = p_size;
|
||||
span.features = p_opentype_features;
|
||||
|
||||
invalidate(sd, false);
|
||||
}
|
||||
}
|
||||
|
||||
bool TextServerAdvanced::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) {
|
||||
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, false);
|
||||
ERR_FAIL_COND_V(p_size <= 0, false);
|
||||
|
@ -3162,11 +3201,12 @@ bool TextServerAdvanced::shaped_text_add_string(RID p_shaped, const String &p_te
|
|||
span.font_size = p_size;
|
||||
span.language = p_language;
|
||||
span.features = p_opentype_features;
|
||||
span.meta = p_meta;
|
||||
|
||||
sd->spans.push_back(span);
|
||||
sd->text += p_text;
|
||||
sd->end += p_text.length();
|
||||
invalidate(sd);
|
||||
invalidate(sd, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -3196,13 +3236,13 @@ bool TextServerAdvanced::shaped_text_add_object(RID p_shaped, Variant p_key, con
|
|||
sd->text += String::chr(0xfffc).repeat(p_length);
|
||||
sd->end += p_length;
|
||||
sd->objects[p_key] = obj;
|
||||
invalidate(sd);
|
||||
invalidate(sd, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align) {
|
||||
ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, false);
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
|
@ -3222,7 +3262,7 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key,
|
|||
Glyph gl = sd->glyphs[i];
|
||||
Variant key;
|
||||
if (gl.count == 1) {
|
||||
for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
|
||||
for (const KeyValue<Variant, ShapedTextDataAdvanced::EmbeddedObject> &E : sd->objects) {
|
||||
if (E.value.pos == gl.start) {
|
||||
key = E.key;
|
||||
break;
|
||||
|
@ -3262,77 +3302,80 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key,
|
|||
sd->width += gl.advance * gl.repeat;
|
||||
}
|
||||
}
|
||||
|
||||
// Align embedded objects to baseline.
|
||||
float full_ascent = sd->ascent;
|
||||
float full_descent = sd->descent;
|
||||
for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
|
||||
if ((E.value.pos >= sd->start) && (E.value.pos < sd->end)) {
|
||||
if (sd->orientation == ORIENTATION_HORIZONTAL) {
|
||||
switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
|
||||
case INLINE_ALIGNMENT_TO_TOP: {
|
||||
E.value.rect.position.y = -sd->ascent;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_CENTER: {
|
||||
E.value.rect.position.y = (-sd->ascent + sd->descent) / 2;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_BASELINE: {
|
||||
E.value.rect.position.y = 0;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_BOTTOM: {
|
||||
E.value.rect.position.y = sd->descent;
|
||||
} break;
|
||||
}
|
||||
switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
|
||||
case INLINE_ALIGNMENT_BOTTOM_TO: {
|
||||
E.value.rect.position.y -= E.value.rect.size.y;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_CENTER_TO: {
|
||||
E.value.rect.position.y -= E.value.rect.size.y / 2;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TOP_TO: {
|
||||
// NOP
|
||||
} break;
|
||||
}
|
||||
full_ascent = MAX(full_ascent, -E.value.rect.position.y);
|
||||
full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y);
|
||||
} else {
|
||||
switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
|
||||
case INLINE_ALIGNMENT_TO_TOP: {
|
||||
E.value.rect.position.x = -sd->ascent;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_CENTER: {
|
||||
E.value.rect.position.x = (-sd->ascent + sd->descent) / 2;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_BASELINE: {
|
||||
E.value.rect.position.x = 0;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_BOTTOM: {
|
||||
E.value.rect.position.x = sd->descent;
|
||||
} break;
|
||||
}
|
||||
switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
|
||||
case INLINE_ALIGNMENT_BOTTOM_TO: {
|
||||
E.value.rect.position.x -= E.value.rect.size.x;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_CENTER_TO: {
|
||||
E.value.rect.position.x -= E.value.rect.size.x / 2;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TOP_TO: {
|
||||
// NOP
|
||||
} break;
|
||||
}
|
||||
full_ascent = MAX(full_ascent, -E.value.rect.position.x);
|
||||
full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x);
|
||||
}
|
||||
}
|
||||
}
|
||||
sd->ascent = full_ascent;
|
||||
sd->descent = full_descent;
|
||||
_realign(sd);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void TextServerAdvanced::_realign(ShapedTextDataAdvanced *p_sd) const {
|
||||
// Align embedded objects to baseline.
|
||||
float full_ascent = p_sd->ascent;
|
||||
float full_descent = p_sd->descent;
|
||||
for (KeyValue<Variant, ShapedTextDataAdvanced::EmbeddedObject> &E : p_sd->objects) {
|
||||
if ((E.value.pos >= p_sd->start) && (E.value.pos < p_sd->end)) {
|
||||
if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
|
||||
switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
|
||||
case INLINE_ALIGNMENT_TO_TOP: {
|
||||
E.value.rect.position.y = -p_sd->ascent;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_CENTER: {
|
||||
E.value.rect.position.y = (-p_sd->ascent + p_sd->descent) / 2;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_BASELINE: {
|
||||
E.value.rect.position.y = 0;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_BOTTOM: {
|
||||
E.value.rect.position.y = p_sd->descent;
|
||||
} break;
|
||||
}
|
||||
switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
|
||||
case INLINE_ALIGNMENT_BOTTOM_TO: {
|
||||
E.value.rect.position.y -= E.value.rect.size.y;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_CENTER_TO: {
|
||||
E.value.rect.position.y -= E.value.rect.size.y / 2;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TOP_TO: {
|
||||
// NOP
|
||||
} break;
|
||||
}
|
||||
full_ascent = MAX(full_ascent, -E.value.rect.position.y);
|
||||
full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y);
|
||||
} else {
|
||||
switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
|
||||
case INLINE_ALIGNMENT_TO_TOP: {
|
||||
E.value.rect.position.x = -p_sd->ascent;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_CENTER: {
|
||||
E.value.rect.position.x = (-p_sd->ascent + p_sd->descent) / 2;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_BASELINE: {
|
||||
E.value.rect.position.x = 0;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_BOTTOM: {
|
||||
E.value.rect.position.x = p_sd->descent;
|
||||
} break;
|
||||
}
|
||||
switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
|
||||
case INLINE_ALIGNMENT_BOTTOM_TO: {
|
||||
E.value.rect.position.x -= E.value.rect.size.x;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_CENTER_TO: {
|
||||
E.value.rect.position.x -= E.value.rect.size.x / 2;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TOP_TO: {
|
||||
// NOP
|
||||
} break;
|
||||
}
|
||||
full_ascent = MAX(full_ascent, -E.value.rect.position.x);
|
||||
full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x);
|
||||
}
|
||||
}
|
||||
}
|
||||
p_sd->ascent = full_ascent;
|
||||
p_sd->descent = full_descent;
|
||||
}
|
||||
|
||||
RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_length) const {
|
||||
const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, RID());
|
||||
|
@ -3423,7 +3466,7 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S
|
|||
Variant key;
|
||||
bool find_embedded = false;
|
||||
if (gl.count == 1) {
|
||||
for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : p_sd->objects) {
|
||||
for (const KeyValue<Variant, ShapedTextDataAdvanced::EmbeddedObject> &E : p_sd->objects) {
|
||||
if (E.value.pos == gl.start) {
|
||||
find_embedded = true;
|
||||
key = E.key;
|
||||
|
@ -3466,72 +3509,7 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S
|
|||
}
|
||||
}
|
||||
|
||||
// Align embedded objects to baseline.
|
||||
float full_ascent = p_new_sd->ascent;
|
||||
float full_descent = p_new_sd->descent;
|
||||
for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : p_new_sd->objects) {
|
||||
if ((E.value.pos >= p_new_sd->start) && (E.value.pos < p_new_sd->end)) {
|
||||
if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
|
||||
switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
|
||||
case INLINE_ALIGNMENT_TO_TOP: {
|
||||
E.value.rect.position.y = -p_new_sd->ascent;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_CENTER: {
|
||||
E.value.rect.position.y = (-p_new_sd->ascent + p_new_sd->descent) / 2;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_BASELINE: {
|
||||
E.value.rect.position.y = 0;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_BOTTOM: {
|
||||
E.value.rect.position.y = p_new_sd->descent;
|
||||
} break;
|
||||
}
|
||||
switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
|
||||
case INLINE_ALIGNMENT_BOTTOM_TO: {
|
||||
E.value.rect.position.y -= E.value.rect.size.y;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_CENTER_TO: {
|
||||
E.value.rect.position.y -= E.value.rect.size.y / 2;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TOP_TO: {
|
||||
// NOP
|
||||
} break;
|
||||
}
|
||||
full_ascent = MAX(full_ascent, -E.value.rect.position.y);
|
||||
full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y);
|
||||
} else {
|
||||
switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
|
||||
case INLINE_ALIGNMENT_TO_TOP: {
|
||||
E.value.rect.position.x = -p_new_sd->ascent;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_CENTER: {
|
||||
E.value.rect.position.x = (-p_new_sd->ascent + p_new_sd->descent) / 2;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_BASELINE: {
|
||||
E.value.rect.position.x = 0;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_BOTTOM: {
|
||||
E.value.rect.position.x = p_new_sd->descent;
|
||||
} break;
|
||||
}
|
||||
switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
|
||||
case INLINE_ALIGNMENT_BOTTOM_TO: {
|
||||
E.value.rect.position.x -= E.value.rect.size.x;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_CENTER_TO: {
|
||||
E.value.rect.position.x -= E.value.rect.size.x / 2;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TOP_TO: {
|
||||
// NOP
|
||||
} break;
|
||||
}
|
||||
full_ascent = MAX(full_ascent, -E.value.rect.position.x);
|
||||
full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x);
|
||||
}
|
||||
}
|
||||
}
|
||||
p_new_sd->ascent = full_ascent;
|
||||
p_new_sd->descent = full_descent;
|
||||
_realign(p_new_sd);
|
||||
}
|
||||
p_new_sd->valid = true;
|
||||
|
||||
|
@ -3968,40 +3946,43 @@ bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) {
|
|||
|
||||
const UChar *data = sd->utf16.ptr();
|
||||
|
||||
HashMap<int, bool> breaks;
|
||||
UErrorCode err = U_ZERO_ERROR;
|
||||
int i = 0;
|
||||
while (i < sd->spans.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) {
|
||||
if (!sd->break_ops_valid) {
|
||||
sd->breaks.clear();
|
||||
UErrorCode err = U_ZERO_ERROR;
|
||||
int i = 0;
|
||||
while (i < sd->spans.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) {
|
||||
i++;
|
||||
}
|
||||
int r_end = sd->spans[i].end;
|
||||
UBreakIterator *bi = ubrk_open(UBRK_LINE, language.ascii().get_data(), data + _convert_pos_inv(sd, r_start), _convert_pos_inv(sd, r_end - r_start), &err);
|
||||
if (U_FAILURE(err)) {
|
||||
// No data loaded - use fallback.
|
||||
for (int j = r_start; j < r_end; j++) {
|
||||
char32_t c = sd->text[j - sd->start];
|
||||
if (is_whitespace(c)) {
|
||||
sd->breaks[j + 1] = false;
|
||||
}
|
||||
if (is_linebreak(c)) {
|
||||
sd->breaks[j + 1] = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while (ubrk_next(bi) != UBRK_DONE) {
|
||||
int pos = _convert_pos(sd, ubrk_current(bi)) + r_start;
|
||||
if ((ubrk_getRuleStatus(bi) >= UBRK_LINE_HARD) && (ubrk_getRuleStatus(bi) < UBRK_LINE_HARD_LIMIT)) {
|
||||
sd->breaks[pos] = true;
|
||||
} else if ((ubrk_getRuleStatus(bi) >= UBRK_LINE_SOFT) && (ubrk_getRuleStatus(bi) < UBRK_LINE_SOFT_LIMIT)) {
|
||||
sd->breaks[pos] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
ubrk_close(bi);
|
||||
i++;
|
||||
}
|
||||
int r_end = sd->spans[i].end;
|
||||
UBreakIterator *bi = ubrk_open(UBRK_LINE, language.ascii().get_data(), data + _convert_pos_inv(sd, r_start), _convert_pos_inv(sd, r_end - r_start), &err);
|
||||
if (U_FAILURE(err)) {
|
||||
// No data loaded - use fallback.
|
||||
for (int j = r_start; j < r_end; j++) {
|
||||
char32_t c = sd->text[j - sd->start];
|
||||
if (is_whitespace(c)) {
|
||||
breaks[j + 1] = false;
|
||||
}
|
||||
if (is_linebreak(c)) {
|
||||
breaks[j + 1] = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while (ubrk_next(bi) != UBRK_DONE) {
|
||||
int pos = _convert_pos(sd, ubrk_current(bi)) + r_start;
|
||||
if ((ubrk_getRuleStatus(bi) >= UBRK_LINE_HARD) && (ubrk_getRuleStatus(bi) < UBRK_LINE_HARD_LIMIT)) {
|
||||
breaks[pos] = true;
|
||||
} else if ((ubrk_getRuleStatus(bi) >= UBRK_LINE_SOFT) && (ubrk_getRuleStatus(bi) < UBRK_LINE_SOFT_LIMIT)) {
|
||||
breaks[pos] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
ubrk_close(bi);
|
||||
i++;
|
||||
sd->break_ops_valid = true;
|
||||
}
|
||||
|
||||
sd->sort_valid = false;
|
||||
|
@ -4013,7 +3994,7 @@ bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) {
|
|||
int c_punct_size = sd->custom_punct.length();
|
||||
const char32_t *c_punct = sd->custom_punct.ptr();
|
||||
|
||||
for (i = 0; i < sd_size; i++) {
|
||||
for (int i = 0; i < sd_size; i++) {
|
||||
if (sd_glyphs[i].count > 0) {
|
||||
char32_t c = ch[sd_glyphs[i].start - sd->start];
|
||||
if (c == 0xfffc) {
|
||||
|
@ -4040,8 +4021,8 @@ bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) {
|
|||
if (is_underscore(c)) {
|
||||
sd_glyphs[i].flags |= GRAPHEME_IS_UNDERSCORE;
|
||||
}
|
||||
if (breaks.has(sd_glyphs[i].end)) {
|
||||
if (breaks[sd_glyphs[i].end] && (is_linebreak(c))) {
|
||||
if (sd->breaks.has(sd_glyphs[i].end)) {
|
||||
if (sd->breaks[sd_glyphs[i].end] && (is_linebreak(c))) {
|
||||
sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_HARD;
|
||||
} else if (is_whitespace(c)) {
|
||||
sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_SOFT;
|
||||
|
@ -4186,41 +4167,45 @@ bool TextServerAdvanced::shaped_text_update_justification_ops(RID p_shaped) {
|
|||
const UChar *data = sd->utf16.ptr();
|
||||
int32_t data_size = sd->utf16.length();
|
||||
|
||||
Map<int, bool> jstops;
|
||||
if (!sd->js_ops_valid) {
|
||||
sd->jstops.clear();
|
||||
|
||||
// Use ICU word iterator and custom kashida detection.
|
||||
UErrorCode err = U_ZERO_ERROR;
|
||||
UBreakIterator *bi = ubrk_open(UBRK_WORD, "", data, data_size, &err);
|
||||
if (U_FAILURE(err)) {
|
||||
// No data - use fallback.
|
||||
int limit = 0;
|
||||
for (int i = 0; i < sd->text.length(); i++) {
|
||||
if (is_whitespace(data[i])) {
|
||||
int ks = _generate_kashida_justification_opportunies(sd->text, limit, i) + sd->start;
|
||||
if (ks != -1) {
|
||||
jstops[ks] = true;
|
||||
// Use ICU word iterator and custom kashida detection.
|
||||
UErrorCode err = U_ZERO_ERROR;
|
||||
UBreakIterator *bi = ubrk_open(UBRK_WORD, "", data, data_size, &err);
|
||||
if (U_FAILURE(err)) {
|
||||
// No data - use fallback.
|
||||
int limit = 0;
|
||||
for (int i = 0; i < sd->text.length(); i++) {
|
||||
if (is_whitespace(data[i])) {
|
||||
int ks = _generate_kashida_justification_opportunies(sd->text, limit, i) + sd->start;
|
||||
if (ks != -1) {
|
||||
sd->jstops[ks] = true;
|
||||
}
|
||||
limit = i + 1;
|
||||
}
|
||||
limit = i + 1;
|
||||
}
|
||||
}
|
||||
int ks = _generate_kashida_justification_opportunies(sd->text, limit, sd->text.length()) + sd->start;
|
||||
if (ks != -1) {
|
||||
jstops[ks] = true;
|
||||
}
|
||||
} else {
|
||||
int limit = 0;
|
||||
while (ubrk_next(bi) != UBRK_DONE) {
|
||||
if (ubrk_getRuleStatus(bi) != UBRK_WORD_NONE) {
|
||||
int i = _convert_pos(sd, ubrk_current(bi));
|
||||
jstops[i + sd->start] = false;
|
||||
int ks = _generate_kashida_justification_opportunies(sd->text, limit, i);
|
||||
if (ks != -1) {
|
||||
jstops[ks + sd->start] = true;
|
||||
int ks = _generate_kashida_justification_opportunies(sd->text, limit, sd->text.length()) + sd->start;
|
||||
if (ks != -1) {
|
||||
sd->jstops[ks] = true;
|
||||
}
|
||||
} else {
|
||||
int limit = 0;
|
||||
while (ubrk_next(bi) != UBRK_DONE) {
|
||||
if (ubrk_getRuleStatus(bi) != UBRK_WORD_NONE) {
|
||||
int i = _convert_pos(sd, ubrk_current(bi));
|
||||
sd->jstops[i + sd->start] = false;
|
||||
int ks = _generate_kashida_justification_opportunies(sd->text, limit, i);
|
||||
if (ks != -1) {
|
||||
sd->jstops[ks + sd->start] = true;
|
||||
}
|
||||
limit = i;
|
||||
}
|
||||
limit = i;
|
||||
}
|
||||
ubrk_close(bi);
|
||||
}
|
||||
ubrk_close(bi);
|
||||
|
||||
sd->js_ops_valid = true;
|
||||
}
|
||||
|
||||
sd->sort_valid = false;
|
||||
|
@ -4228,18 +4213,18 @@ bool TextServerAdvanced::shaped_text_update_justification_ops(RID p_shaped) {
|
|||
|
||||
Glyph *sd_glyphs = sd->glyphs.ptrw();
|
||||
int sd_size = sd->glyphs.size();
|
||||
if (jstops.size() > 0) {
|
||||
if (sd->jstops.size() > 0) {
|
||||
for (int i = 0; i < sd_size; i++) {
|
||||
if (sd_glyphs[i].count > 0) {
|
||||
char32_t c = sd->text[sd_glyphs[i].start - sd->start];
|
||||
if (c == 0x0640) {
|
||||
sd_glyphs[i].flags |= GRAPHEME_IS_ELONGATION;
|
||||
}
|
||||
if (jstops.has(sd_glyphs[i].start)) {
|
||||
if (sd->jstops.has(sd_glyphs[i].start)) {
|
||||
if (c == 0xfffc) {
|
||||
continue;
|
||||
}
|
||||
if (jstops[sd_glyphs[i].start]) {
|
||||
if (sd->jstops[sd_glyphs[i].start]) {
|
||||
if (c != 0x0640) {
|
||||
if (sd_glyphs[i].font_rid != RID()) {
|
||||
Glyph gl = _shape_single_glyph(sd, 0x0640, HB_SCRIPT_ARABIC, HB_DIRECTION_RTL, sd->glyphs[i].font_rid, sd->glyphs[i].font_size);
|
||||
|
@ -4574,7 +4559,7 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) {
|
|||
return true;
|
||||
}
|
||||
|
||||
invalidate(sd);
|
||||
invalidate(sd, false);
|
||||
if (sd->parent != RID()) {
|
||||
shaped_text_shape(sd->parent);
|
||||
ShapedTextDataAdvanced *parent_sd = shaped_owner.get_or_null(sd->parent);
|
||||
|
@ -4733,70 +4718,7 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) {
|
|||
}
|
||||
}
|
||||
|
||||
// Align embedded objects to baseline.
|
||||
float full_ascent = sd->ascent;
|
||||
float full_descent = sd->descent;
|
||||
for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
|
||||
if (sd->orientation == ORIENTATION_HORIZONTAL) {
|
||||
switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
|
||||
case INLINE_ALIGNMENT_TO_TOP: {
|
||||
E.value.rect.position.y = -sd->ascent;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_CENTER: {
|
||||
E.value.rect.position.y = (-sd->ascent + sd->descent) / 2;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_BASELINE: {
|
||||
E.value.rect.position.y = 0;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_BOTTOM: {
|
||||
E.value.rect.position.y = sd->descent;
|
||||
} break;
|
||||
}
|
||||
switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
|
||||
case INLINE_ALIGNMENT_BOTTOM_TO: {
|
||||
E.value.rect.position.y -= E.value.rect.size.y;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_CENTER_TO: {
|
||||
E.value.rect.position.y -= E.value.rect.size.y / 2;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TOP_TO: {
|
||||
// NOP
|
||||
} break;
|
||||
}
|
||||
full_ascent = MAX(full_ascent, -E.value.rect.position.y);
|
||||
full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y);
|
||||
} else {
|
||||
switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
|
||||
case INLINE_ALIGNMENT_TO_TOP: {
|
||||
E.value.rect.position.x = -sd->ascent;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_CENTER: {
|
||||
E.value.rect.position.x = (-sd->ascent + sd->descent) / 2;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_BASELINE: {
|
||||
E.value.rect.position.x = 0;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_BOTTOM: {
|
||||
E.value.rect.position.x = sd->descent;
|
||||
} break;
|
||||
}
|
||||
switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
|
||||
case INLINE_ALIGNMENT_BOTTOM_TO: {
|
||||
E.value.rect.position.x -= E.value.rect.size.x;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_CENTER_TO: {
|
||||
E.value.rect.position.x -= E.value.rect.size.x / 2;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TOP_TO: {
|
||||
// NOP
|
||||
} break;
|
||||
}
|
||||
full_ascent = MAX(full_ascent, -E.value.rect.position.x);
|
||||
full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x);
|
||||
}
|
||||
}
|
||||
sd->ascent = full_ascent;
|
||||
sd->descent = full_descent;
|
||||
_realign(sd);
|
||||
sd->valid = true;
|
||||
return sd->valid;
|
||||
}
|
||||
|
@ -4863,7 +4785,7 @@ Array TextServerAdvanced::shaped_text_get_objects(RID p_shaped) const {
|
|||
ERR_FAIL_COND_V(!sd, ret);
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
|
||||
for (const KeyValue<Variant, ShapedTextDataAdvanced::EmbeddedObject> &E : sd->objects) {
|
||||
ret.push_back(E.key);
|
||||
}
|
||||
|
||||
|
|
|
@ -242,6 +242,21 @@ class TextServerAdvanced : public TextServer {
|
|||
// Shaped text cache data.
|
||||
|
||||
struct ShapedTextDataAdvanced : public ShapedTextData {
|
||||
struct Span {
|
||||
int start = -1;
|
||||
int end = -1;
|
||||
|
||||
Vector<RID> fonts;
|
||||
int font_size = 0;
|
||||
|
||||
Variant embedded_key;
|
||||
|
||||
String language;
|
||||
Dictionary features;
|
||||
Variant meta;
|
||||
};
|
||||
Vector<Span> spans;
|
||||
|
||||
/* Intermediate data */
|
||||
Char16String utf16;
|
||||
Vector<UBiDi *> bidi_iter;
|
||||
|
@ -249,6 +264,11 @@ class TextServerAdvanced : public TextServer {
|
|||
ScriptIterator *script_iter = nullptr;
|
||||
hb_buffer_t *hb_buffer = nullptr;
|
||||
|
||||
HashMap<int, bool> jstops;
|
||||
HashMap<int, bool> breaks;
|
||||
bool break_ops_valid = false;
|
||||
bool js_ops_valid = false;
|
||||
|
||||
~ShapedTextDataAdvanced() {
|
||||
for (int i = 0; i < bidi_iter.size(); i++) {
|
||||
ubidi_close(bidi_iter[i]);
|
||||
|
@ -268,6 +288,7 @@ class TextServerAdvanced : public TextServer {
|
|||
mutable RID_PtrOwner<FontDataAdvanced> font_owner;
|
||||
mutable RID_PtrOwner<ShapedTextDataAdvanced> shaped_owner;
|
||||
|
||||
void _realign(ShapedTextDataAdvanced *p_sd) const;
|
||||
int _convert_pos(const ShapedTextDataAdvanced *p_sd, int p_pos) const;
|
||||
int _convert_pos_inv(const ShapedTextDataAdvanced *p_sd, int p_pos) const;
|
||||
bool _shape_substr(ShapedTextDataAdvanced *p_new_sd, const ShapedTextDataAdvanced *p_sd, int p_start, int p_length) const;
|
||||
|
@ -302,7 +323,7 @@ protected:
|
|||
static void _bind_methods(){};
|
||||
|
||||
void full_copy(ShapedTextDataAdvanced *p_shaped);
|
||||
void invalidate(ShapedTextDataAdvanced *p_shaped);
|
||||
void invalidate(ShapedTextDataAdvanced *p_shaped, bool p_text = false);
|
||||
|
||||
public:
|
||||
virtual bool has_feature(Feature p_feature) const override;
|
||||
|
@ -482,10 +503,14 @@ public:
|
|||
virtual void shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) override;
|
||||
virtual bool shaped_text_get_preserve_control(RID p_shaped) const override;
|
||||
|
||||
virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "") override;
|
||||
virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant()) override;
|
||||
virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1) override;
|
||||
virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER) override;
|
||||
|
||||
virtual int shaped_get_span_count(RID p_shaped) const override;
|
||||
virtual Variant shaped_get_span_meta(RID p_shaped, int p_index) const override;
|
||||
virtual void shaped_set_span_update_font(RID p_shaped, int p_index, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary()) override;
|
||||
|
||||
virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const override;
|
||||
virtual RID shaped_text_get_parent(RID p_shaped) const override;
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ void TextServerFallback::free(RID p_rid) {
|
|||
font_owner.free(p_rid);
|
||||
memdelete(fd);
|
||||
} else if (shaped_owner.owns(p_rid)) {
|
||||
ShapedTextData *sd = shaped_owner.get_or_null(p_rid);
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_rid);
|
||||
shaped_owner.free(p_rid);
|
||||
memdelete(sd);
|
||||
}
|
||||
|
@ -2061,7 +2061,7 @@ void TextServerFallback::font_set_global_oversampling(float p_oversampling) {
|
|||
/* Shaped text buffer interface */
|
||||
/*************************************************************************/
|
||||
|
||||
void TextServerFallback::invalidate(ShapedTextData *p_shaped) {
|
||||
void TextServerFallback::invalidate(ShapedTextDataFallback *p_shaped) {
|
||||
p_shaped->valid = false;
|
||||
p_shaped->sort_valid = false;
|
||||
p_shaped->line_breaks_valid = false;
|
||||
|
@ -2075,17 +2075,17 @@ void TextServerFallback::invalidate(ShapedTextData *p_shaped) {
|
|||
p_shaped->glyphs_logical.clear();
|
||||
}
|
||||
|
||||
void TextServerFallback::full_copy(ShapedTextData *p_shaped) {
|
||||
ShapedTextData *parent = shaped_owner.get_or_null(p_shaped->parent);
|
||||
void TextServerFallback::full_copy(ShapedTextDataFallback *p_shaped) {
|
||||
ShapedTextDataFallback *parent = shaped_owner.get_or_null(p_shaped->parent);
|
||||
|
||||
for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : parent->objects) {
|
||||
for (const KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : parent->objects) {
|
||||
if (E.value.pos >= p_shaped->start && E.value.pos < p_shaped->end) {
|
||||
p_shaped->objects[E.key] = E.value;
|
||||
}
|
||||
}
|
||||
|
||||
for (int k = 0; k < parent->spans.size(); k++) {
|
||||
ShapedTextData::Span span = parent->spans[k];
|
||||
ShapedTextDataFallback::Span span = parent->spans[k];
|
||||
if (span.start >= p_shaped->end || span.end <= p_shaped->start) {
|
||||
continue;
|
||||
}
|
||||
|
@ -2099,7 +2099,7 @@ void TextServerFallback::full_copy(ShapedTextData *p_shaped) {
|
|||
|
||||
RID TextServerFallback::create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
|
||||
_THREAD_SAFE_METHOD_
|
||||
ShapedTextData *sd = memnew(ShapedTextData);
|
||||
ShapedTextDataFallback *sd = memnew(ShapedTextDataFallback);
|
||||
sd->direction = p_direction;
|
||||
sd->orientation = p_orientation;
|
||||
|
||||
|
@ -2107,7 +2107,7 @@ RID TextServerFallback::create_shaped_text(TextServer::Direction p_direction, Te
|
|||
}
|
||||
|
||||
void TextServerFallback::shaped_text_clear(RID p_shaped) {
|
||||
ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND(!sd);
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
|
@ -2136,7 +2136,7 @@ TextServer::Direction TextServerFallback::shaped_text_get_inferred_direction(RID
|
|||
|
||||
void TextServerFallback::shaped_text_set_custom_punctuation(RID p_shaped, const String &p_punct) {
|
||||
_THREAD_SAFE_METHOD_
|
||||
ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND(!sd);
|
||||
|
||||
if (sd->custom_punct != p_punct) {
|
||||
|
@ -2150,13 +2150,13 @@ void TextServerFallback::shaped_text_set_custom_punctuation(RID p_shaped, const
|
|||
|
||||
String TextServerFallback::shaped_text_get_custom_punctuation(RID p_shaped) const {
|
||||
_THREAD_SAFE_METHOD_
|
||||
const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, String());
|
||||
return sd->custom_punct;
|
||||
}
|
||||
|
||||
void TextServerFallback::shaped_text_set_orientation(RID p_shaped, TextServer::Orientation p_orientation) {
|
||||
ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND(!sd);
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
|
@ -2174,7 +2174,7 @@ void TextServerFallback::shaped_text_set_bidi_override(RID p_shaped, const Array
|
|||
}
|
||||
|
||||
TextServer::Orientation TextServerFallback::shaped_text_get_orientation(RID p_shaped) const {
|
||||
const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, TextServer::ORIENTATION_HORIZONTAL);
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
|
@ -2182,7 +2182,7 @@ TextServer::Orientation TextServerFallback::shaped_text_get_orientation(RID p_sh
|
|||
}
|
||||
|
||||
void TextServerFallback::shaped_text_set_preserve_invalid(RID p_shaped, bool p_enabled) {
|
||||
ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
ERR_FAIL_COND(!sd);
|
||||
|
@ -2196,7 +2196,7 @@ void TextServerFallback::shaped_text_set_preserve_invalid(RID p_shaped, bool p_e
|
|||
}
|
||||
|
||||
bool TextServerFallback::shaped_text_get_preserve_invalid(RID p_shaped) const {
|
||||
const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, false);
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
|
@ -2204,7 +2204,7 @@ bool TextServerFallback::shaped_text_get_preserve_invalid(RID p_shaped) const {
|
|||
}
|
||||
|
||||
void TextServerFallback::shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) {
|
||||
ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND(!sd);
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
|
@ -2218,15 +2218,52 @@ void TextServerFallback::shaped_text_set_preserve_control(RID p_shaped, bool p_e
|
|||
}
|
||||
|
||||
bool TextServerFallback::shaped_text_get_preserve_control(RID p_shaped) const {
|
||||
const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, false);
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
return sd->preserve_control;
|
||||
}
|
||||
|
||||
bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
|
||||
ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
int TextServerFallback::shaped_get_span_count(RID p_shaped) const {
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, 0);
|
||||
return sd->spans.size();
|
||||
}
|
||||
|
||||
Variant TextServerFallback::shaped_get_span_meta(RID p_shaped, int p_index) const {
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, Variant());
|
||||
ERR_FAIL_INDEX_V(p_index, sd->spans.size(), Variant());
|
||||
return sd->spans[p_index].meta;
|
||||
}
|
||||
|
||||
void TextServerFallback::shaped_set_span_update_font(RID p_shaped, int p_index, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features) {
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND(!sd);
|
||||
ERR_FAIL_INDEX(p_index, sd->spans.size());
|
||||
|
||||
ShapedTextDataFallback::Span &span = sd->spans.write[p_index];
|
||||
span.fonts.clear();
|
||||
// Pre-sort fonts, push fonts with the language support first.
|
||||
Vector<RID> fonts_no_match;
|
||||
int font_count = p_fonts.size();
|
||||
for (int i = 0; i < font_count; i++) {
|
||||
if (font_is_language_supported(p_fonts[i], span.language)) {
|
||||
span.fonts.push_back(p_fonts[i]);
|
||||
} else {
|
||||
fonts_no_match.push_back(p_fonts[i]);
|
||||
}
|
||||
}
|
||||
span.fonts.append_array(fonts_no_match);
|
||||
span.font_size = p_size;
|
||||
span.features = p_opentype_features;
|
||||
|
||||
sd->valid = false;
|
||||
}
|
||||
|
||||
bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) {
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, false);
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
|
@ -2244,7 +2281,7 @@ bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_te
|
|||
full_copy(sd);
|
||||
}
|
||||
|
||||
ShapedTextData::Span span;
|
||||
ShapedTextDataFallback::Span span;
|
||||
span.start = sd->text.length();
|
||||
span.end = span.start + p_text.length();
|
||||
|
||||
|
@ -2263,6 +2300,7 @@ bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_te
|
|||
ERR_FAIL_COND_V(span.fonts.is_empty(), false);
|
||||
span.font_size = p_size;
|
||||
span.language = p_language;
|
||||
span.meta = p_meta;
|
||||
|
||||
sd->spans.push_back(span);
|
||||
sd->text += p_text;
|
||||
|
@ -2273,7 +2311,7 @@ bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_te
|
|||
}
|
||||
|
||||
bool TextServerFallback::shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align, int p_length) {
|
||||
ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, false);
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
|
@ -2284,12 +2322,12 @@ bool TextServerFallback::shaped_text_add_object(RID p_shaped, Variant p_key, con
|
|||
full_copy(sd);
|
||||
}
|
||||
|
||||
ShapedTextData::Span span;
|
||||
ShapedTextDataFallback::Span span;
|
||||
span.start = sd->start + sd->text.length();
|
||||
span.end = span.start + p_length;
|
||||
span.embedded_key = p_key;
|
||||
|
||||
ShapedTextData::EmbeddedObject obj;
|
||||
ShapedTextDataFallback::EmbeddedObject obj;
|
||||
obj.inline_align = p_inline_align;
|
||||
obj.rect.size = p_size;
|
||||
obj.pos = span.start;
|
||||
|
@ -2304,7 +2342,7 @@ bool TextServerFallback::shaped_text_add_object(RID p_shaped, Variant p_key, con
|
|||
}
|
||||
|
||||
bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align) {
|
||||
ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, false);
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
|
@ -2324,7 +2362,7 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key,
|
|||
Glyph gl = sd->glyphs[i];
|
||||
Variant key;
|
||||
if (gl.count == 1) {
|
||||
for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
|
||||
for (const KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : sd->objects) {
|
||||
if (E.value.pos == gl.start) {
|
||||
key = E.key;
|
||||
break;
|
||||
|
@ -2364,79 +2402,82 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key,
|
|||
sd->width += gl.advance * gl.repeat;
|
||||
}
|
||||
}
|
||||
|
||||
// Align embedded objects to baseline.
|
||||
float full_ascent = sd->ascent;
|
||||
float full_descent = sd->descent;
|
||||
for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
|
||||
if ((E.value.pos >= sd->start) && (E.value.pos < sd->end)) {
|
||||
if (sd->orientation == ORIENTATION_HORIZONTAL) {
|
||||
switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
|
||||
case INLINE_ALIGNMENT_TO_TOP: {
|
||||
E.value.rect.position.y = -sd->ascent;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_CENTER: {
|
||||
E.value.rect.position.y = (-sd->ascent + sd->descent) / 2;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_BASELINE: {
|
||||
E.value.rect.position.y = 0;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_BOTTOM: {
|
||||
E.value.rect.position.y = sd->descent;
|
||||
} break;
|
||||
}
|
||||
switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
|
||||
case INLINE_ALIGNMENT_BOTTOM_TO: {
|
||||
E.value.rect.position.y -= E.value.rect.size.y;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_CENTER_TO: {
|
||||
E.value.rect.position.y -= E.value.rect.size.y / 2;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TOP_TO: {
|
||||
// NOP
|
||||
} break;
|
||||
}
|
||||
full_ascent = MAX(full_ascent, -E.value.rect.position.y);
|
||||
full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y);
|
||||
} else {
|
||||
switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
|
||||
case INLINE_ALIGNMENT_TO_TOP: {
|
||||
E.value.rect.position.x = -sd->ascent;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_CENTER: {
|
||||
E.value.rect.position.x = (-sd->ascent + sd->descent) / 2;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_BASELINE: {
|
||||
E.value.rect.position.x = 0;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_BOTTOM: {
|
||||
E.value.rect.position.x = sd->descent;
|
||||
} break;
|
||||
}
|
||||
switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
|
||||
case INLINE_ALIGNMENT_BOTTOM_TO: {
|
||||
E.value.rect.position.x -= E.value.rect.size.x;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_CENTER_TO: {
|
||||
E.value.rect.position.x -= E.value.rect.size.x / 2;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TOP_TO: {
|
||||
// NOP
|
||||
} break;
|
||||
}
|
||||
full_ascent = MAX(full_ascent, -E.value.rect.position.x);
|
||||
full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x);
|
||||
}
|
||||
}
|
||||
}
|
||||
sd->ascent = full_ascent;
|
||||
sd->descent = full_descent;
|
||||
_realign(sd);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void TextServerFallback::_realign(ShapedTextDataFallback *p_sd) const {
|
||||
// Align embedded objects to baseline.
|
||||
float full_ascent = p_sd->ascent;
|
||||
float full_descent = p_sd->descent;
|
||||
for (KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : p_sd->objects) {
|
||||
if ((E.value.pos >= p_sd->start) && (E.value.pos < p_sd->end)) {
|
||||
if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
|
||||
switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
|
||||
case INLINE_ALIGNMENT_TO_TOP: {
|
||||
E.value.rect.position.y = -p_sd->ascent;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_CENTER: {
|
||||
E.value.rect.position.y = (-p_sd->ascent + p_sd->descent) / 2;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_BASELINE: {
|
||||
E.value.rect.position.y = 0;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_BOTTOM: {
|
||||
E.value.rect.position.y = p_sd->descent;
|
||||
} break;
|
||||
}
|
||||
switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
|
||||
case INLINE_ALIGNMENT_BOTTOM_TO: {
|
||||
E.value.rect.position.y -= E.value.rect.size.y;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_CENTER_TO: {
|
||||
E.value.rect.position.y -= E.value.rect.size.y / 2;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TOP_TO: {
|
||||
// NOP
|
||||
} break;
|
||||
}
|
||||
full_ascent = MAX(full_ascent, -E.value.rect.position.y);
|
||||
full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y);
|
||||
} else {
|
||||
switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
|
||||
case INLINE_ALIGNMENT_TO_TOP: {
|
||||
E.value.rect.position.x = -p_sd->ascent;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_CENTER: {
|
||||
E.value.rect.position.x = (-p_sd->ascent + p_sd->descent) / 2;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_BASELINE: {
|
||||
E.value.rect.position.x = 0;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TO_BOTTOM: {
|
||||
E.value.rect.position.x = p_sd->descent;
|
||||
} break;
|
||||
}
|
||||
switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
|
||||
case INLINE_ALIGNMENT_BOTTOM_TO: {
|
||||
E.value.rect.position.x -= E.value.rect.size.x;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_CENTER_TO: {
|
||||
E.value.rect.position.x -= E.value.rect.size.x / 2;
|
||||
} break;
|
||||
case INLINE_ALIGNMENT_TOP_TO: {
|
||||
// NOP
|
||||
} break;
|
||||
}
|
||||
full_ascent = MAX(full_ascent, -E.value.rect.position.x);
|
||||
full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x);
|
||||
}
|
||||
}
|
||||
}
|
||||
p_sd->ascent = full_ascent;
|
||||
p_sd->descent = full_descent;
|
||||
}
|
||||
|
||||
RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_length) const {
|
||||
const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, RID());
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
|
@ -2450,7 +2491,7 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng
|
|||
ERR_FAIL_COND_V(sd->start > p_start || sd->end < p_start, RID());
|
||||
ERR_FAIL_COND_V(sd->end < p_start + p_length, RID());
|
||||
|
||||
ShapedTextData *new_sd = memnew(ShapedTextData);
|
||||
ShapedTextDataFallback *new_sd = memnew(ShapedTextDataFallback);
|
||||
new_sd->parent = p_shaped;
|
||||
new_sd->start = p_start;
|
||||
new_sd->end = p_start + p_length;
|
||||
|
@ -2476,7 +2517,7 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng
|
|||
Variant key;
|
||||
bool find_embedded = false;
|
||||
if (gl.count == 1) {
|
||||
for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
|
||||
for (const KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : sd->objects) {
|
||||
if (E.value.pos == gl.start) {
|
||||
find_embedded = true;
|
||||
key = E.key;
|
||||
|
@ -2520,7 +2561,7 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng
|
|||
// Align embedded objects to baseline.
|
||||
float full_ascent = new_sd->ascent;
|
||||
float full_descent = new_sd->descent;
|
||||
for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : new_sd->objects) {
|
||||
for (KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : new_sd->objects) {
|
||||
if ((E.value.pos >= new_sd->start) && (E.value.pos < new_sd->end)) {
|
||||
if (sd->orientation == ORIENTATION_HORIZONTAL) {
|
||||
switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
|
||||
|
@ -2590,7 +2631,7 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng
|
|||
}
|
||||
|
||||
RID TextServerFallback::shaped_text_get_parent(RID p_shaped) const {
|
||||
ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, RID());
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
|
@ -2598,7 +2639,7 @@ RID TextServerFallback::shaped_text_get_parent(RID p_shaped) const {
|
|||
}
|
||||
|
||||
float TextServerFallback::shaped_text_fit_to_width(RID p_shaped, float p_width, uint16_t /*JustificationFlag*/ p_jst_flags) {
|
||||
ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, 0.f);
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
|
@ -2707,7 +2748,7 @@ float TextServerFallback::shaped_text_fit_to_width(RID p_shaped, float p_width,
|
|||
}
|
||||
|
||||
float TextServerFallback::shaped_text_tab_align(RID p_shaped, const PackedFloat32Array &p_tab_stops) {
|
||||
ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, 0.f);
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
|
@ -2763,7 +2804,7 @@ float TextServerFallback::shaped_text_tab_align(RID p_shaped, const PackedFloat3
|
|||
}
|
||||
|
||||
bool TextServerFallback::shaped_text_update_breaks(RID p_shaped) {
|
||||
ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, false);
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
|
@ -2819,7 +2860,7 @@ bool TextServerFallback::shaped_text_update_breaks(RID p_shaped) {
|
|||
}
|
||||
|
||||
bool TextServerFallback::shaped_text_update_justification_ops(RID p_shaped) {
|
||||
ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, false);
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
|
@ -2835,7 +2876,7 @@ bool TextServerFallback::shaped_text_update_justification_ops(RID p_shaped) {
|
|||
}
|
||||
|
||||
void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, float p_width, uint16_t p_trim_flags) {
|
||||
ShapedTextData *sd = shaped_owner.get_or_null(p_shaped_line);
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped_line);
|
||||
ERR_FAIL_COND_MSG(!sd, "ShapedTextDataFallback invalid.");
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
|
@ -2863,9 +2904,9 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, fl
|
|||
return;
|
||||
}
|
||||
|
||||
Vector<ShapedTextData::Span> &spans = sd->spans;
|
||||
Vector<ShapedTextDataFallback::Span> &spans = sd->spans;
|
||||
if (sd->parent != RID()) {
|
||||
ShapedTextData *parent_sd = shaped_owner.get_or_null(sd->parent);
|
||||
ShapedTextDataFallback *parent_sd = shaped_owner.get_or_null(sd->parent);
|
||||
ERR_FAIL_COND(!parent_sd->valid);
|
||||
spans = parent_sd->spans;
|
||||
}
|
||||
|
@ -2987,39 +3028,39 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, fl
|
|||
}
|
||||
|
||||
int TextServerFallback::shaped_text_get_trim_pos(RID p_shaped) const {
|
||||
ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V_MSG(!sd, -1, "ShapedTextData invalid.");
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V_MSG(!sd, -1, "ShapedTextDataFallback invalid.");
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
return sd->overrun_trim_data.trim_pos;
|
||||
}
|
||||
|
||||
int TextServerFallback::shaped_text_get_ellipsis_pos(RID p_shaped) const {
|
||||
ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V_MSG(!sd, -1, "ShapedTextData invalid.");
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V_MSG(!sd, -1, "ShapedTextDataFallback invalid.");
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
return sd->overrun_trim_data.ellipsis_pos;
|
||||
}
|
||||
|
||||
const Glyph *TextServerFallback::shaped_text_get_ellipsis_glyphs(RID p_shaped) const {
|
||||
ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V_MSG(!sd, nullptr, "ShapedTextData invalid.");
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V_MSG(!sd, nullptr, "ShapedTextDataFallback invalid.");
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
return sd->overrun_trim_data.ellipsis_glyph_buf.ptr();
|
||||
}
|
||||
|
||||
int TextServerFallback::shaped_text_get_ellipsis_glyph_count(RID p_shaped) const {
|
||||
ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V_MSG(!sd, 0, "ShapedTextData invalid.");
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V_MSG(!sd, 0, "ShapedTextDataFallback invalid.");
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
return sd->overrun_trim_data.ellipsis_glyph_buf.size();
|
||||
}
|
||||
|
||||
bool TextServerFallback::shaped_text_shape(RID p_shaped) {
|
||||
ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, false);
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
|
@ -3046,7 +3087,7 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) {
|
|||
|
||||
// "Shape" string.
|
||||
for (int i = 0; i < sd->spans.size(); i++) {
|
||||
const ShapedTextData::Span &span = sd->spans[i];
|
||||
const ShapedTextDataFallback::Span &span = sd->spans[i];
|
||||
if (span.embedded_key != Variant()) {
|
||||
// Embedded object.
|
||||
if (sd->orientation == ORIENTATION_HORIZONTAL) {
|
||||
|
@ -3146,7 +3187,7 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) {
|
|||
// Align embedded objects to baseline.
|
||||
float full_ascent = sd->ascent;
|
||||
float full_descent = sd->descent;
|
||||
for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
|
||||
for (KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : sd->objects) {
|
||||
if (sd->orientation == ORIENTATION_HORIZONTAL) {
|
||||
switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
|
||||
case INLINE_ALIGNMENT_TO_TOP: {
|
||||
|
@ -3212,7 +3253,7 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) {
|
|||
}
|
||||
|
||||
bool TextServerFallback::shaped_text_is_ready(RID p_shaped) const {
|
||||
const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, false);
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
|
@ -3220,7 +3261,7 @@ bool TextServerFallback::shaped_text_is_ready(RID p_shaped) const {
|
|||
}
|
||||
|
||||
const Glyph *TextServerFallback::shaped_text_get_glyphs(RID p_shaped) const {
|
||||
const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, nullptr);
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
|
@ -3231,7 +3272,7 @@ const Glyph *TextServerFallback::shaped_text_get_glyphs(RID p_shaped) const {
|
|||
}
|
||||
|
||||
int TextServerFallback::shaped_text_get_glyph_count(RID p_shaped) const {
|
||||
const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, 0);
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
|
@ -3242,7 +3283,7 @@ int TextServerFallback::shaped_text_get_glyph_count(RID p_shaped) const {
|
|||
}
|
||||
|
||||
const Glyph *TextServerFallback::shaped_text_sort_logical(RID p_shaped) {
|
||||
const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, nullptr);
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
|
@ -3254,7 +3295,7 @@ const Glyph *TextServerFallback::shaped_text_sort_logical(RID p_shaped) {
|
|||
}
|
||||
|
||||
Vector2i TextServerFallback::shaped_text_get_range(RID p_shaped) const {
|
||||
const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, Vector2i());
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
|
@ -3263,11 +3304,11 @@ Vector2i TextServerFallback::shaped_text_get_range(RID p_shaped) const {
|
|||
|
||||
Array TextServerFallback::shaped_text_get_objects(RID p_shaped) const {
|
||||
Array ret;
|
||||
const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, ret);
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
|
||||
for (const KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : sd->objects) {
|
||||
ret.push_back(E.key);
|
||||
}
|
||||
|
||||
|
@ -3275,7 +3316,7 @@ Array TextServerFallback::shaped_text_get_objects(RID p_shaped) const {
|
|||
}
|
||||
|
||||
Rect2 TextServerFallback::shaped_text_get_object_rect(RID p_shaped, Variant p_key) const {
|
||||
const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, Rect2());
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
|
@ -3287,7 +3328,7 @@ Rect2 TextServerFallback::shaped_text_get_object_rect(RID p_shaped, Variant p_ke
|
|||
}
|
||||
|
||||
Size2 TextServerFallback::shaped_text_get_size(RID p_shaped) const {
|
||||
const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, Size2());
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
|
@ -3302,7 +3343,7 @@ Size2 TextServerFallback::shaped_text_get_size(RID p_shaped) const {
|
|||
}
|
||||
|
||||
float TextServerFallback::shaped_text_get_ascent(RID p_shaped) const {
|
||||
const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, 0.f);
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
|
@ -3313,7 +3354,7 @@ float TextServerFallback::shaped_text_get_ascent(RID p_shaped) const {
|
|||
}
|
||||
|
||||
float TextServerFallback::shaped_text_get_descent(RID p_shaped) const {
|
||||
const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, 0.f);
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
|
@ -3324,7 +3365,7 @@ float TextServerFallback::shaped_text_get_descent(RID p_shaped) const {
|
|||
}
|
||||
|
||||
float TextServerFallback::shaped_text_get_width(RID p_shaped) const {
|
||||
const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, 0.f);
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
|
@ -3335,7 +3376,7 @@ float TextServerFallback::shaped_text_get_width(RID p_shaped) const {
|
|||
}
|
||||
|
||||
float TextServerFallback::shaped_text_get_underline_position(RID p_shaped) const {
|
||||
const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, 0.f);
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
|
@ -3347,7 +3388,7 @@ float TextServerFallback::shaped_text_get_underline_position(RID p_shaped) const
|
|||
}
|
||||
|
||||
float TextServerFallback::shaped_text_get_underline_thickness(RID p_shaped) const {
|
||||
const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_COND_V(!sd, 0.f);
|
||||
|
||||
MutexLock lock(sd->mutex);
|
||||
|
|
|
@ -203,17 +203,38 @@ class TextServerFallback : public TextServer {
|
|||
}
|
||||
}
|
||||
|
||||
// Shaped text cache data.
|
||||
|
||||
struct ShapedTextDataFallback : public ShapedTextData {
|
||||
struct Span {
|
||||
int start = -1;
|
||||
int end = -1;
|
||||
|
||||
Vector<RID> fonts;
|
||||
int font_size = 0;
|
||||
|
||||
Variant embedded_key;
|
||||
|
||||
String language;
|
||||
Dictionary features;
|
||||
Variant meta;
|
||||
};
|
||||
Vector<Span> spans;
|
||||
};
|
||||
|
||||
// Common data.
|
||||
|
||||
float oversampling = 1.f;
|
||||
mutable RID_PtrOwner<FontDataFallback> font_owner;
|
||||
mutable RID_PtrOwner<ShapedTextData> shaped_owner;
|
||||
mutable RID_PtrOwner<ShapedTextDataFallback> shaped_owner;
|
||||
|
||||
void _realign(ShapedTextDataFallback *p_sd) const;
|
||||
|
||||
protected:
|
||||
static void _bind_methods(){};
|
||||
|
||||
void full_copy(ShapedTextData *p_shaped);
|
||||
void invalidate(ShapedTextData *p_shaped);
|
||||
void full_copy(ShapedTextDataFallback *p_shaped);
|
||||
void invalidate(ShapedTextDataFallback *p_shaped);
|
||||
|
||||
public:
|
||||
virtual bool has_feature(Feature p_feature) const override;
|
||||
|
@ -391,10 +412,14 @@ public:
|
|||
virtual void shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) override;
|
||||
virtual bool shaped_text_get_preserve_control(RID p_shaped) const override;
|
||||
|
||||
virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "") override;
|
||||
virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant()) override;
|
||||
virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1) override;
|
||||
virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER) override;
|
||||
|
||||
virtual int shaped_get_span_count(RID p_shaped) const override;
|
||||
virtual Variant shaped_get_span_meta(RID p_shaped, int p_index) const override;
|
||||
virtual void shaped_set_span_update_font(RID p_shaped, int p_index, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary()) override;
|
||||
|
||||
virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const override;
|
||||
virtual RID shaped_text_get_parent(RID p_shaped) const override;
|
||||
|
||||
|
|
|
@ -82,9 +82,11 @@ void Label::_shape() {
|
|||
Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"), SNAME("Label"));
|
||||
int width = (get_size().width - style->get_minimum_size().width);
|
||||
|
||||
if (dirty) {
|
||||
if (dirty || font_dirty) {
|
||||
String lang = (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale();
|
||||
TS->shaped_text_clear(text_rid);
|
||||
if (dirty) {
|
||||
TS->shaped_text_clear(text_rid);
|
||||
}
|
||||
if (text_direction == Control::TEXT_DIRECTION_INHERITED) {
|
||||
TS->shaped_text_set_direction(text_rid, is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
|
||||
} else {
|
||||
|
@ -97,9 +99,17 @@ void Label::_shape() {
|
|||
if (visible_chars >= 0 && visible_chars_behavior == VC_CHARS_BEFORE_SHAPING) {
|
||||
text = text.substr(0, visible_chars);
|
||||
}
|
||||
TS->shaped_text_add_string(text_rid, text, font->get_rids(), font_size, opentype_features, lang);
|
||||
if (dirty) {
|
||||
TS->shaped_text_add_string(text_rid, text, font->get_rids(), font_size, opentype_features, lang);
|
||||
} else {
|
||||
int spans = TS->shaped_get_span_count(text_rid);
|
||||
for (int i = 0; i < spans; i++) {
|
||||
TS->shaped_set_span_update_font(text_rid, i, font->get_rids(), font_size, opentype_features);
|
||||
}
|
||||
}
|
||||
TS->shaped_text_set_bidi_override(text_rid, structured_text_parser(st_parser, st_args, text));
|
||||
dirty = false;
|
||||
font_dirty = false;
|
||||
lines_dirty = true;
|
||||
}
|
||||
|
||||
|
@ -276,7 +286,7 @@ void Label::_notification(int p_what) {
|
|||
RenderingServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), true);
|
||||
}
|
||||
|
||||
if (dirty || lines_dirty) {
|
||||
if (dirty || font_dirty || lines_dirty) {
|
||||
_shape();
|
||||
}
|
||||
|
||||
|
@ -521,7 +531,7 @@ void Label::_notification(int p_what) {
|
|||
}
|
||||
|
||||
if (p_what == NOTIFICATION_THEME_CHANGED) {
|
||||
dirty = true;
|
||||
font_dirty = true;
|
||||
update();
|
||||
}
|
||||
if (p_what == NOTIFICATION_RESIZED) {
|
||||
|
@ -531,7 +541,7 @@ void Label::_notification(int p_what) {
|
|||
|
||||
Size2 Label::get_minimum_size() const {
|
||||
// don't want to mutable everything
|
||||
if (dirty || lines_dirty) {
|
||||
if (dirty || font_dirty || lines_dirty) {
|
||||
const_cast<Label *>(this)->_shape();
|
||||
}
|
||||
|
||||
|
@ -555,7 +565,7 @@ int Label::get_line_count() const {
|
|||
if (!is_inside_tree()) {
|
||||
return 1;
|
||||
}
|
||||
if (dirty || lines_dirty) {
|
||||
if (dirty || font_dirty || lines_dirty) {
|
||||
const_cast<Label *>(this)->_shape();
|
||||
}
|
||||
|
||||
|
@ -630,7 +640,7 @@ void Label::set_text_direction(Control::TextDirection p_text_direction) {
|
|||
ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);
|
||||
if (text_direction != p_text_direction) {
|
||||
text_direction = p_text_direction;
|
||||
dirty = true;
|
||||
font_dirty = true;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
@ -638,7 +648,7 @@ void Label::set_text_direction(Control::TextDirection p_text_direction) {
|
|||
void Label::set_structured_text_bidi_override(Control::StructuredTextParser p_parser) {
|
||||
if (st_parser != p_parser) {
|
||||
st_parser = p_parser;
|
||||
dirty = true;
|
||||
font_dirty = true;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
@ -649,7 +659,7 @@ Control::StructuredTextParser Label::get_structured_text_bidi_override() const {
|
|||
|
||||
void Label::set_structured_text_bidi_override_options(Array p_args) {
|
||||
st_args = p_args;
|
||||
dirty = true;
|
||||
font_dirty = true;
|
||||
update();
|
||||
}
|
||||
|
||||
|
@ -663,7 +673,7 @@ Control::TextDirection Label::get_text_direction() const {
|
|||
|
||||
void Label::clear_opentype_features() {
|
||||
opentype_features.clear();
|
||||
dirty = true;
|
||||
font_dirty = true;
|
||||
update();
|
||||
}
|
||||
|
||||
|
@ -671,7 +681,7 @@ void Label::set_opentype_feature(const String &p_name, int p_value) {
|
|||
int32_t tag = TS->name_to_tag(p_name);
|
||||
if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) {
|
||||
opentype_features[tag] = p_value;
|
||||
dirty = true;
|
||||
font_dirty = true;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
@ -798,7 +808,7 @@ int Label::get_max_lines_visible() const {
|
|||
}
|
||||
|
||||
int Label::get_total_character_count() const {
|
||||
if (dirty || lines_dirty) {
|
||||
if (dirty || font_dirty || lines_dirty) {
|
||||
const_cast<Label *>(this)->_shape();
|
||||
}
|
||||
|
||||
|
@ -814,13 +824,13 @@ bool Label::_set(const StringName &p_name, const Variant &p_value) {
|
|||
if (value == -1) {
|
||||
if (opentype_features.has(tag)) {
|
||||
opentype_features.erase(tag);
|
||||
dirty = true;
|
||||
font_dirty = true;
|
||||
update();
|
||||
}
|
||||
} else {
|
||||
if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) {
|
||||
opentype_features[tag] = value;
|
||||
dirty = true;
|
||||
font_dirty = true;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,6 +73,7 @@ private:
|
|||
|
||||
bool lines_dirty = true;
|
||||
bool dirty = true;
|
||||
bool font_dirty = true;
|
||||
RID text_rid;
|
||||
Vector<RID> lines_rid;
|
||||
|
||||
|
|
|
@ -205,6 +205,49 @@ String RichTextLabel::_letters(int p_num, bool p_capitalize) const {
|
|||
return s;
|
||||
}
|
||||
|
||||
void RichTextLabel::_update_line_font(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size) {
|
||||
ERR_FAIL_COND(p_frame == nullptr);
|
||||
ERR_FAIL_COND(p_line < 0 || p_line >= p_frame->lines.size());
|
||||
|
||||
Line &l = p_frame->lines.write[p_line];
|
||||
|
||||
RID t = l.text_buf->get_rid();
|
||||
int spans = TS->shaped_get_span_count(t);
|
||||
for (int i = 0; i < spans; i++) {
|
||||
ItemText *it = (ItemText *)(uint64_t)TS->shaped_get_span_meta(t, i);
|
||||
if (it) {
|
||||
Ref<Font> font = _find_font(it);
|
||||
if (font.is_null()) {
|
||||
font = p_base_font;
|
||||
}
|
||||
int font_size = _find_font_size(it);
|
||||
if (font_size == -1) {
|
||||
font_size = p_base_font_size;
|
||||
}
|
||||
Dictionary font_ftr = _find_font_features(it);
|
||||
TS->shaped_set_span_update_font(t, i, font->get_rids(), font_size, font_ftr);
|
||||
}
|
||||
}
|
||||
|
||||
Item *it_to = (p_line + 1 < 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) {
|
||||
case ITEM_TABLE: {
|
||||
ItemTable *table = static_cast<ItemTable *>(it);
|
||||
for (Item *E : table->subitems) {
|
||||
ERR_CONTINUE(E->type != ITEM_FRAME); // Children should all be frames.
|
||||
ItemFrame *frame = static_cast<ItemFrame *>(E);
|
||||
for (int i = 0; i < frame->lines.size(); i++) {
|
||||
_update_line_font(frame, i, p_base_font, p_base_font_size);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width) {
|
||||
ERR_FAIL_COND(p_frame == nullptr);
|
||||
ERR_FAIL_COND(p_line < 0 || p_line >= p_frame->lines.size());
|
||||
|
@ -449,7 +492,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
|
|||
}
|
||||
remaining_characters -= tx.length();
|
||||
|
||||
l.text_buf->add_string(tx, font, font_size, font_ftr, lang);
|
||||
l.text_buf->add_string(tx, font, font_size, font_ftr, lang, (uint64_t)it);
|
||||
text += tx;
|
||||
l.char_count += tx.length();
|
||||
} break;
|
||||
|
@ -1448,7 +1491,10 @@ void RichTextLabel::_notification(int p_what) {
|
|||
update();
|
||||
|
||||
} break;
|
||||
case NOTIFICATION_THEME_CHANGED:
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
main->first_invalid_font_line = 0; //invalidate ALL
|
||||
update();
|
||||
} break;
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
if (!text.is_empty()) {
|
||||
set_text(text);
|
||||
|
@ -1545,6 +1591,10 @@ Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const
|
|||
return get_default_cursor_shape(); //invalid
|
||||
}
|
||||
|
||||
if (main->first_invalid_font_line < main->lines.size()) {
|
||||
return get_default_cursor_shape(); //invalid
|
||||
}
|
||||
|
||||
if (main->first_resized_line < main->lines.size()) {
|
||||
return get_default_cursor_shape(); //invalid
|
||||
}
|
||||
|
@ -1569,6 +1619,9 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
|
|||
if (main->first_invalid_line < main->lines.size()) {
|
||||
return;
|
||||
}
|
||||
if (main->first_invalid_font_line < main->lines.size()) {
|
||||
return;
|
||||
}
|
||||
if (main->first_resized_line < main->lines.size()) {
|
||||
return;
|
||||
}
|
||||
|
@ -1732,6 +1785,9 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
|
|||
if (main->first_invalid_line < main->lines.size()) {
|
||||
return;
|
||||
}
|
||||
if (main->first_invalid_font_line < main->lines.size()) {
|
||||
return;
|
||||
}
|
||||
if (main->first_resized_line < main->lines.size()) {
|
||||
return;
|
||||
}
|
||||
|
@ -2184,6 +2240,18 @@ bool RichTextLabel::_find_layout_subitem(Item *from, Item *to) {
|
|||
|
||||
void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) {
|
||||
if (p_frame->first_invalid_line == p_frame->lines.size()) {
|
||||
Ref<Font> base_font = get_theme_font(SNAME("normal_font"));
|
||||
int base_font_size = get_theme_font_size(SNAME("normal_font_size"));
|
||||
|
||||
// Update fonts.
|
||||
if (p_frame->first_invalid_font_line != p_frame->lines.size()) {
|
||||
for (int i = p_frame->first_invalid_font_line; i < p_frame->lines.size(); i++) {
|
||||
_update_line_font(p_frame, i, base_font, base_font_size);
|
||||
}
|
||||
p_frame->first_resized_line = p_frame->first_invalid_font_line;
|
||||
p_frame->first_invalid_font_line = p_frame->lines.size();
|
||||
}
|
||||
|
||||
if (p_frame->first_resized_line == p_frame->lines.size()) {
|
||||
return;
|
||||
}
|
||||
|
@ -2191,9 +2259,6 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) {
|
|||
// Resize lines without reshaping.
|
||||
Rect2 text_rect = _get_text_rect();
|
||||
|
||||
Ref<Font> base_font = get_theme_font(SNAME("normal_font"));
|
||||
int base_font_size = get_theme_font_size(SNAME("normal_font_size"));
|
||||
|
||||
for (int i = p_frame->first_resized_line; i < p_frame->lines.size(); i++) {
|
||||
_resize_line(p_frame, i, base_font, base_font_size, text_rect.get_size().width - scroll_w);
|
||||
}
|
||||
|
@ -2237,6 +2302,7 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) {
|
|||
|
||||
p_frame->first_invalid_line = p_frame->lines.size();
|
||||
p_frame->first_resized_line = p_frame->lines.size();
|
||||
p_frame->first_invalid_font_line = p_frame->lines.size();
|
||||
|
||||
updating_scroll = true;
|
||||
vscroll->set_max(total_height);
|
||||
|
@ -4494,6 +4560,7 @@ RichTextLabel::RichTextLabel() {
|
|||
main->lines.write[0].from = main;
|
||||
main->first_invalid_line = 0;
|
||||
main->first_resized_line = 0;
|
||||
main->first_invalid_font_line = 0;
|
||||
current_frame = main;
|
||||
|
||||
vscroll = memnew(VScrollBar);
|
||||
|
|
|
@ -129,6 +129,7 @@ private:
|
|||
|
||||
Vector<Line> lines;
|
||||
int first_invalid_line = 0;
|
||||
int first_invalid_font_line = 0;
|
||||
int first_resized_line = 0;
|
||||
|
||||
ItemFrame *parent_frame = nullptr;
|
||||
|
@ -414,6 +415,7 @@ private:
|
|||
|
||||
void _shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width, int *r_char_offset);
|
||||
void _resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width);
|
||||
void _update_line_font(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size);
|
||||
int _draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, int p_shadow_outline_size, const Point2 &p_shadow_ofs, int &r_processed_glyphs);
|
||||
float _find_click_in_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Point2i &p_click, ItemFrame **r_click_frame = nullptr, int *r_click_line = nullptr, Item **r_click_item = nullptr, int *r_click_char = nullptr);
|
||||
|
||||
|
|
|
@ -187,29 +187,44 @@ void TextEdit::Text::_calculate_max_line_width() {
|
|||
max_width = width;
|
||||
}
|
||||
|
||||
void TextEdit::Text::invalidate_cache(int p_line, int p_column, const String &p_ime_text, const Array &p_bidi_override) {
|
||||
void TextEdit::Text::invalidate_cache(int p_line, int p_column, bool p_text_changed, const String &p_ime_text, const Array &p_bidi_override) {
|
||||
ERR_FAIL_INDEX(p_line, text.size());
|
||||
|
||||
if (font.is_null() || font_size <= 0) {
|
||||
return; // Not in tree?
|
||||
}
|
||||
|
||||
text.write[p_line].data_buf->clear();
|
||||
if (p_text_changed) {
|
||||
text.write[p_line].data_buf->clear();
|
||||
}
|
||||
|
||||
text.write[p_line].data_buf->set_width(width);
|
||||
text.write[p_line].data_buf->set_direction((TextServer::Direction)direction);
|
||||
text.write[p_line].data_buf->set_preserve_control(draw_control_chars);
|
||||
if (p_ime_text.length() > 0) {
|
||||
text.write[p_line].data_buf->add_string(p_ime_text, font, font_size, opentype_features, language);
|
||||
if (p_text_changed) {
|
||||
text.write[p_line].data_buf->add_string(p_ime_text, font, font_size, opentype_features, language);
|
||||
}
|
||||
if (!p_bidi_override.is_empty()) {
|
||||
TS->shaped_text_set_bidi_override(text.write[p_line].data_buf->get_rid(), p_bidi_override);
|
||||
}
|
||||
} else {
|
||||
text.write[p_line].data_buf->add_string(text[p_line].data, font, font_size, opentype_features, language);
|
||||
if (p_text_changed) {
|
||||
text.write[p_line].data_buf->add_string(text[p_line].data, font, font_size, opentype_features, language);
|
||||
}
|
||||
if (!text[p_line].bidi_override.is_empty()) {
|
||||
TS->shaped_text_set_bidi_override(text.write[p_line].data_buf->get_rid(), text[p_line].bidi_override);
|
||||
}
|
||||
}
|
||||
|
||||
if (!p_text_changed) {
|
||||
RID r = text.write[p_line].data_buf->get_rid();
|
||||
int spans = TS->shaped_get_span_count(r);
|
||||
for (int i = 0; i < spans; i++) {
|
||||
TS->shaped_set_span_update_font(r, i, font->get_rids(), font_size, opentype_features);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply tab align.
|
||||
if (tab_size > 0) {
|
||||
Vector<float> tabs;
|
||||
|
@ -266,6 +281,24 @@ void TextEdit::Text::invalidate_all_lines() {
|
|||
}
|
||||
}
|
||||
|
||||
void TextEdit::Text::invalidate_font() {
|
||||
if (!is_dirty) {
|
||||
return;
|
||||
}
|
||||
|
||||
max_width = -1;
|
||||
line_height = -1;
|
||||
|
||||
if (!font.is_null() && font_size > 0) {
|
||||
font_height = font->get_height(font_size);
|
||||
}
|
||||
|
||||
for (int i = 0; i < text.size(); i++) {
|
||||
invalidate_cache(i, -1, false);
|
||||
}
|
||||
is_dirty = false;
|
||||
}
|
||||
|
||||
void TextEdit::Text::invalidate_all() {
|
||||
if (!is_dirty) {
|
||||
return;
|
||||
|
@ -279,7 +312,7 @@ void TextEdit::Text::invalidate_all() {
|
|||
}
|
||||
|
||||
for (int i = 0; i < text.size(); i++) {
|
||||
invalidate_cache(i);
|
||||
invalidate_cache(i, -1, true);
|
||||
}
|
||||
is_dirty = false;
|
||||
}
|
||||
|
@ -294,7 +327,7 @@ void TextEdit::Text::clear() {
|
|||
line.gutters.resize(gutter_count);
|
||||
line.data = "";
|
||||
text.insert(0, line);
|
||||
invalidate_cache(0);
|
||||
invalidate_cache(0, -1, true);
|
||||
}
|
||||
|
||||
int TextEdit::Text::get_max_width() const {
|
||||
|
@ -306,7 +339,7 @@ void TextEdit::Text::set(int p_line, const String &p_text, const Array &p_bidi_o
|
|||
|
||||
text.write[p_line].data = p_text;
|
||||
text.write[p_line].bidi_override = p_bidi_override;
|
||||
invalidate_cache(p_line);
|
||||
invalidate_cache(p_line, -1, true);
|
||||
}
|
||||
|
||||
void TextEdit::Text::insert(int p_at, const Vector<String> &p_text, const Vector<Array> &p_bidi_override) {
|
||||
|
@ -331,7 +364,7 @@ void TextEdit::Text::insert(int p_at, const Vector<String> &p_text, const Vector
|
|||
line.data = p_text[i];
|
||||
line.bidi_override = p_bidi_override[i];
|
||||
text.write[p_at + i] = line;
|
||||
invalidate_cache(p_at + i);
|
||||
invalidate_cache(p_at + i, -1, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1446,9 +1479,11 @@ void TextEdit::_notification(int p_what) {
|
|||
DisplayServer::get_singleton()->window_set_ime_position(Point2(), get_viewport()->get_window_id());
|
||||
DisplayServer::get_singleton()->window_set_ime_active(false, get_viewport()->get_window_id());
|
||||
}
|
||||
ime_text = "";
|
||||
ime_selection = Point2();
|
||||
text.invalidate_cache(caret.line, caret.column, ime_text);
|
||||
if (!ime_text.is_empty()) {
|
||||
ime_text = "";
|
||||
ime_selection = Point2();
|
||||
text.invalidate_cache(caret.line, caret.column, true, ime_text);
|
||||
}
|
||||
|
||||
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) {
|
||||
DisplayServer::get_singleton()->virtual_keyboard_hide();
|
||||
|
@ -1470,7 +1505,7 @@ void TextEdit::_notification(int p_what) {
|
|||
t = ime_text;
|
||||
}
|
||||
|
||||
text.invalidate_cache(caret.line, caret.column, t, structured_text_parser(st_parser, st_args, t));
|
||||
text.invalidate_cache(caret.line, caret.column, true, t, structured_text_parser(st_parser, st_args, t));
|
||||
update();
|
||||
}
|
||||
} break;
|
||||
|
@ -2538,7 +2573,7 @@ void TextEdit::_update_caches() {
|
|||
text.set_draw_control_chars(draw_control_chars);
|
||||
text.set_font(font);
|
||||
text.set_font_size(font_size);
|
||||
text.invalidate_all();
|
||||
text.invalidate_font();
|
||||
_update_placeholder();
|
||||
|
||||
/* Syntax highlighting. */
|
||||
|
@ -2718,7 +2753,7 @@ void TextEdit::set_text_direction(Control::TextDirection p_text_direction) {
|
|||
dir = (TextServer::Direction)text_direction;
|
||||
}
|
||||
text.set_direction_and_language(dir, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale());
|
||||
text.invalidate_all();
|
||||
text.invalidate_font();
|
||||
_update_placeholder();
|
||||
|
||||
if (menu_dir) {
|
||||
|
@ -2740,7 +2775,7 @@ void TextEdit::set_opentype_feature(const String &p_name, int p_value) {
|
|||
if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) {
|
||||
opentype_features[tag] = p_value;
|
||||
text.set_font_features(opentype_features);
|
||||
text.invalidate_all();
|
||||
text.invalidate_font();
|
||||
_update_placeholder();
|
||||
update();
|
||||
}
|
||||
|
@ -2757,7 +2792,7 @@ int TextEdit::get_opentype_feature(const String &p_name) const {
|
|||
void TextEdit::clear_opentype_features() {
|
||||
opentype_features.clear();
|
||||
text.set_font_features(opentype_features);
|
||||
text.invalidate_all();
|
||||
text.invalidate_font();
|
||||
_update_placeholder();
|
||||
update();
|
||||
}
|
||||
|
@ -4852,7 +4887,7 @@ void TextEdit::set_draw_control_chars(bool p_enabled) {
|
|||
menu->set_item_checked(menu->get_item_index(MENU_DISPLAY_UCC), draw_control_chars);
|
||||
}
|
||||
text.set_draw_control_chars(draw_control_chars);
|
||||
text.invalidate_all();
|
||||
text.invalidate_font();
|
||||
_update_placeholder();
|
||||
update();
|
||||
}
|
||||
|
@ -5319,7 +5354,7 @@ bool TextEdit::_set(const StringName &p_name, const Variant &p_value) {
|
|||
if (opentype_features.has(tag)) {
|
||||
opentype_features.erase(tag);
|
||||
text.set_font_features(opentype_features);
|
||||
text.invalidate_all();
|
||||
text.invalidate_font();
|
||||
_update_placeholder();
|
||||
update();
|
||||
}
|
||||
|
@ -5327,7 +5362,7 @@ bool TextEdit::_set(const StringName &p_name, const Variant &p_value) {
|
|||
if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) {
|
||||
opentype_features[tag] = value;
|
||||
text.set_font_features(opentype_features);
|
||||
text.invalidate_all();
|
||||
text.invalidate_font();
|
||||
_update_placeholder();
|
||||
update();
|
||||
}
|
||||
|
|
|
@ -210,7 +210,8 @@ private:
|
|||
int size() const { return text.size(); }
|
||||
void clear();
|
||||
|
||||
void invalidate_cache(int p_line, int p_column = -1, const String &p_ime_text = String(), const Array &p_bidi_override = Array());
|
||||
void invalidate_cache(int p_line, int p_column = -1, bool p_text_changed = false, const String &p_ime_text = String(), const Array &p_bidi_override = Array());
|
||||
void invalidate_font();
|
||||
void invalidate_all();
|
||||
void invalidate_all_lines();
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ void TextLine::_bind_methods() {
|
|||
|
||||
ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextLine::set_bidi_override);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language"), &TextLine::add_string, DEFVAL(Dictionary()), DEFVAL(""));
|
||||
ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language", "meta"), &TextLine::add_string, DEFVAL(Dictionary()), DEFVAL(""), DEFVAL(Variant()));
|
||||
ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextLine::add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1));
|
||||
ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextLine::resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER));
|
||||
|
||||
|
@ -200,9 +200,9 @@ void TextLine::set_bidi_override(const Array &p_override) {
|
|||
dirty = true;
|
||||
}
|
||||
|
||||
bool TextLine::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
|
||||
bool TextLine::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) {
|
||||
ERR_FAIL_COND_V(p_fonts.is_null(), false);
|
||||
bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
|
||||
bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language, p_meta);
|
||||
spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP);
|
||||
spacing_bottom = p_fonts->get_spacing(TextServer::SPACING_BOTTOM);
|
||||
dirty = true;
|
||||
|
|
|
@ -86,7 +86,7 @@ public:
|
|||
void set_preserve_control(bool p_enabled);
|
||||
bool get_preserve_control() const;
|
||||
|
||||
bool add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "");
|
||||
bool add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), 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);
|
||||
bool resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER);
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ void TextParagraph::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_dropcap", "text", "fonts", "size", "dropcap_margins", "opentype_features", "language"), &TextParagraph::set_dropcap, DEFVAL(Rect2()), DEFVAL(Dictionary()), DEFVAL(""));
|
||||
ClassDB::bind_method(D_METHOD("clear_dropcap"), &TextParagraph::clear_dropcap);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language"), &TextParagraph::add_string, DEFVAL(Dictionary()), DEFVAL(""));
|
||||
ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language", "meta"), &TextParagraph::add_string, DEFVAL(Dictionary()), DEFVAL(""), DEFVAL(Variant()));
|
||||
ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextParagraph::add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1));
|
||||
ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextParagraph::resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER));
|
||||
|
||||
|
@ -344,9 +344,9 @@ void TextParagraph::clear_dropcap() {
|
|||
lines_dirty = true;
|
||||
}
|
||||
|
||||
bool TextParagraph::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
|
||||
bool TextParagraph::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) {
|
||||
ERR_FAIL_COND_V(p_fonts.is_null(), false);
|
||||
bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
|
||||
bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language, p_meta);
|
||||
spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP);
|
||||
spacing_bottom = p_fonts->get_spacing(TextServer::SPACING_BOTTOM);
|
||||
lines_dirty = true;
|
||||
|
|
|
@ -102,7 +102,7 @@ public:
|
|||
bool set_dropcap(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Rect2 &p_dropcap_margins = Rect2(), const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "");
|
||||
void clear_dropcap();
|
||||
|
||||
bool add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "");
|
||||
bool add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), 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);
|
||||
bool resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER);
|
||||
|
||||
|
|
|
@ -210,10 +210,14 @@ void TextServerExtension::_bind_methods() {
|
|||
GDVIRTUAL_BIND(_shaped_text_set_preserve_control, "shaped", "enabled");
|
||||
GDVIRTUAL_BIND(_shaped_text_get_preserve_control, "shaped");
|
||||
|
||||
GDVIRTUAL_BIND(_shaped_text_add_string, "shaped", "text", "fonts", "size", "opentype_features", "language");
|
||||
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");
|
||||
GDVIRTUAL_BIND(_shaped_text_resize_object, "shaped", "key", "size", "inline_align");
|
||||
|
||||
GDVIRTUAL_BIND(_shaped_get_span_count, "shaped");
|
||||
GDVIRTUAL_BIND(_shaped_get_span_meta, "shaped", "index");
|
||||
GDVIRTUAL_BIND(_shaped_set_span_update_font, "shaped", "index", "fonts", "size", "opentype_features");
|
||||
|
||||
GDVIRTUAL_BIND(_shaped_text_substr, "shaped", "start", "length");
|
||||
GDVIRTUAL_BIND(_shaped_text_get_parent, "shaped");
|
||||
|
||||
|
@ -1018,13 +1022,13 @@ bool TextServerExtension::shaped_text_get_preserve_control(RID p_shaped) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool TextServerExtension::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
|
||||
bool TextServerExtension::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) {
|
||||
bool ret;
|
||||
Array fonts;
|
||||
for (int i = 0; i < p_fonts.size(); i++) {
|
||||
fonts.push_back(p_fonts[i]);
|
||||
}
|
||||
if (GDVIRTUAL_CALL(_shaped_text_add_string, p_shaped, p_text, fonts, p_size, p_opentype_features, p_language, ret)) {
|
||||
if (GDVIRTUAL_CALL(_shaped_text_add_string, p_shaped, p_text, fonts, p_size, p_opentype_features, p_language, p_meta, ret)) {
|
||||
return ret;
|
||||
}
|
||||
return false;
|
||||
|
@ -1046,6 +1050,30 @@ bool TextServerExtension::shaped_text_resize_object(RID p_shaped, Variant p_key,
|
|||
return false;
|
||||
}
|
||||
|
||||
int TextServerExtension::shaped_get_span_count(RID p_shaped) const {
|
||||
int ret;
|
||||
if (GDVIRTUAL_CALL(_shaped_get_span_count, p_shaped, ret)) {
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Variant TextServerExtension::shaped_get_span_meta(RID p_shaped, int p_index) const {
|
||||
Variant ret;
|
||||
if (GDVIRTUAL_CALL(_shaped_get_span_meta, p_shaped, p_index, ret)) {
|
||||
return ret;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TextServerExtension::shaped_set_span_update_font(RID p_shaped, int p_index, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features) {
|
||||
Array fonts;
|
||||
for (int i = 0; i < p_fonts.size(); i++) {
|
||||
fonts.push_back(p_fonts[i]);
|
||||
}
|
||||
GDVIRTUAL_CALL(_shaped_set_span_update_font, p_shaped, p_index, fonts, p_size, p_opentype_features);
|
||||
}
|
||||
|
||||
RID TextServerExtension::shaped_text_substr(RID p_shaped, int p_start, int p_length) const {
|
||||
RID ret;
|
||||
if (GDVIRTUAL_CALL(_shaped_text_substr, p_shaped, p_start, p_length, ret)) {
|
||||
|
|
|
@ -343,13 +343,20 @@ public:
|
|||
GDVIRTUAL2(_shaped_text_set_preserve_control, RID, bool);
|
||||
GDVIRTUAL1RC(bool, _shaped_text_get_preserve_control, RID);
|
||||
|
||||
virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "") override;
|
||||
virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant()) override;
|
||||
virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1) override;
|
||||
virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER) override;
|
||||
GDVIRTUAL6R(bool, _shaped_text_add_string, RID, const String &, const Array &, int, const Dictionary &, const String &);
|
||||
GDVIRTUAL7R(bool, _shaped_text_add_string, RID, const String &, const Array &, int, const Dictionary &, const String &, const Variant &);
|
||||
GDVIRTUAL5R(bool, _shaped_text_add_object, RID, Variant, const Size2 &, InlineAlignment, int);
|
||||
GDVIRTUAL4R(bool, _shaped_text_resize_object, RID, Variant, const Size2 &, InlineAlignment);
|
||||
|
||||
virtual int shaped_get_span_count(RID p_shaped) const override;
|
||||
virtual Variant shaped_get_span_meta(RID p_shaped, int p_index) const override;
|
||||
virtual void shaped_set_span_update_font(RID p_shaped, int p_index, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary()) override;
|
||||
GDVIRTUAL1RC(int, _shaped_get_span_count, RID);
|
||||
GDVIRTUAL2RC(Variant, _shaped_get_span_meta, RID, int);
|
||||
GDVIRTUAL5(_shaped_set_span_update_font, RID, int, const Array &, int, const Dictionary &);
|
||||
|
||||
virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const override;
|
||||
virtual RID shaped_text_get_parent(RID p_shaped) const override;
|
||||
GDVIRTUAL3RC(RID, _shaped_text_substr, RID, int, int);
|
||||
|
|
|
@ -363,10 +363,14 @@ void TextServer::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("shaped_text_set_preserve_control", "shaped", "enabled"), &TextServer::shaped_text_set_preserve_control);
|
||||
ClassDB::bind_method(D_METHOD("shaped_text_get_preserve_control", "shaped"), &TextServer::shaped_text_get_preserve_control);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("shaped_text_add_string", "shaped", "text", "fonts", "size", "opentype_features", "language"), &TextServer::shaped_text_add_string, DEFVAL(Dictionary()), DEFVAL(""));
|
||||
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"), &TextServer::shaped_text_add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1));
|
||||
ClassDB::bind_method(D_METHOD("shaped_text_resize_object", "shaped", "key", "size", "inline_align"), &TextServer::shaped_text_resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER));
|
||||
|
||||
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_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);
|
||||
ClassDB::bind_method(D_METHOD("shaped_text_get_parent", "shaped"), &TextServer::shaped_text_get_parent);
|
||||
ClassDB::bind_method(D_METHOD("shaped_text_fit_to_width", "shaped", "width", "jst_flags"), &TextServer::shaped_text_fit_to_width, DEFVAL(JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA));
|
||||
|
|
|
@ -155,20 +155,6 @@ protected:
|
|||
TextServer::Direction direction = DIRECTION_LTR; // Desired text direction.
|
||||
TextServer::Orientation orientation = ORIENTATION_HORIZONTAL;
|
||||
|
||||
struct Span {
|
||||
int start = -1;
|
||||
int end = -1;
|
||||
|
||||
Vector<RID> fonts;
|
||||
int font_size = 0;
|
||||
|
||||
Variant embedded_key;
|
||||
|
||||
String language;
|
||||
Dictionary features;
|
||||
};
|
||||
Vector<Span> spans;
|
||||
|
||||
struct EmbeddedObject {
|
||||
int pos = 0;
|
||||
InlineAlignment inline_align = INLINE_ALIGNMENT_CENTER;
|
||||
|
@ -387,10 +373,14 @@ public:
|
|||
virtual void shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) = 0;
|
||||
virtual bool shaped_text_get_preserve_control(RID p_shaped) const = 0;
|
||||
|
||||
virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "") = 0;
|
||||
virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant()) = 0;
|
||||
virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1) = 0;
|
||||
virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER) = 0;
|
||||
|
||||
virtual int shaped_get_span_count(RID p_shaped) const = 0;
|
||||
virtual Variant shaped_get_span_meta(RID p_shaped, int p_index) const = 0;
|
||||
virtual void shaped_set_span_update_font(RID p_shaped, int p_index, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary()) = 0;
|
||||
|
||||
virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const = 0; // Copy shaped substring (e.g. line break) without reshaping, but correctly reordered, preservers range.
|
||||
virtual RID shaped_text_get_parent(RID p_shaped) const = 0;
|
||||
|
||||
|
|
Loading…
Reference in a new issue