Merge pull request #71598 from bruvzg/gdscript_bidi_override

Implement BiDi override mode for GDScript source.
This commit is contained in:
Rémi Verschelde 2023-01-19 19:33:18 +01:00
commit 65883cc73b
No known key found for this signature in database
GPG Key ID: C3336907360768E1
21 changed files with 178 additions and 55 deletions

View File

@ -196,12 +196,12 @@
</description>
</method>
<method name="_structured_text_parser" qualifiers="virtual const">
<return type="Vector2i[]" />
<return type="Vector3i[]" />
<param index="0" name="args" type="Array" />
<param index="1" name="text" type="String" />
<description>
User defined BiDi algorithm override function.
Returns an [Array] of [Vector2i] text ranges, in the left-to-right order. Ranges should cover full source [param text] without overlaps. BiDi algorithm will be used on each range separately.
Returns an [Array] of [Vector3i] text ranges and text base directions, in the left-to-right order. Ranges should cover full source [param text] without overlaps. BiDi algorithm will be used on each range separately.
</description>
</method>
<method name="accept_event">

View File

@ -1042,7 +1042,7 @@
</description>
</method>
<method name="parse_structured_text" qualifiers="const">
<return type="Vector2i[]" />
<return type="Vector3i[]" />
<param index="0" name="parser_type" type="int" enum="TextServer.StructuredTextParser" />
<param index="1" name="args" type="Array" />
<param index="2" name="text" type="String" />
@ -1634,6 +1634,9 @@
<constant name="DIRECTION_RTL" value="2" enum="Direction">
Text is written from right to left.
</constant>
<constant name="DIRECTION_INHERITED" value="3" enum="Direction">
Text writing direction is the same as base string writing direction. Used for BiDi override only.
</constant>
<constant name="ORIENTATION_HORIZONTAL" value="0" enum="Orientation">
Text is written horizontally.
</constant>
@ -1881,7 +1884,7 @@
Font have fixed-width characters.
</constant>
<constant name="STRUCTURED_TEXT_DEFAULT" value="0" enum="StructuredTextParser">
Use default behavior. Same as [constant STRUCTURED_TEXT_NONE] unless specified otherwise in the control description.
Use default Unicode BiDi algorithm.
</constant>
<constant name="STRUCTURED_TEXT_URI" value="1" enum="StructuredTextParser">
BiDi override for URI.
@ -1896,8 +1899,8 @@
BiDi override for lists.
Structured text options: list separator [code]String[/code].
</constant>
<constant name="STRUCTURED_TEXT_NONE" value="5" enum="StructuredTextParser">
Use default Unicode BiDi algorithm.
<constant name="STRUCTURED_TEXT_GDSCRIPT" value="5" enum="StructuredTextParser">
BiDi override for GDScript.
</constant>
<constant name="STRUCTURED_TEXT_CUSTOM" value="6" enum="StructuredTextParser">
User defined structured text BiDi override function.

View File

@ -896,7 +896,7 @@
</description>
</method>
<method name="_parse_structured_text" qualifiers="virtual const">
<return type="Vector2i[]" />
<return type="Vector3i[]" />
<param index="0" name="parser_type" type="int" enum="TextServer.StructuredTextParser" />
<param index="1" name="args" type="Array" />
<param index="2" name="text" type="String" />

View File

@ -2086,6 +2086,7 @@ CodeTextEditor::CodeTextEditor() {
text_editor = memnew(CodeEdit);
add_child(text_editor);
text_editor->set_v_size_flags(SIZE_EXPAND_FILL);
text_editor->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_GDSCRIPT);
int ot_mode = EDITOR_GET("interface/editor/code_font_contextual_ligatures");
Ref<FontVariation> fc = text_editor->get_theme_font(SNAME("font"));

View File

@ -3654,6 +3654,7 @@ void TextServerAdvanced::full_copy(ShapedTextDataAdvanced *p_shaped) {
RID TextServerAdvanced::_create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V_MSG(p_direction == DIRECTION_INHERITED, RID(), "Invalid text direction.");
ShapedTextDataAdvanced *sd = memnew(ShapedTextDataAdvanced);
sd->hb_buffer = hb_buffer_create();
@ -3679,6 +3680,7 @@ void TextServerAdvanced::_shaped_text_clear(const RID &p_shaped) {
void TextServerAdvanced::_shaped_text_set_direction(const RID &p_shaped, TextServer::Direction p_direction) {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_MSG(p_direction == DIRECTION_INHERITED, "Invalid text direction.");
ERR_FAIL_COND(!sd);
MutexLock lock(sd->mutex);
@ -3738,8 +3740,12 @@ void TextServerAdvanced::_shaped_text_set_bidi_override(const RID &p_shaped, con
}
sd->bidi_override.clear();
for (int i = 0; i < p_override.size(); i++) {
if (p_override[i].get_type() == Variant::VECTOR2I) {
sd->bidi_override.push_back(p_override[i]);
if (p_override[i].get_type() == Variant::VECTOR3I) {
const Vector3i &r = p_override[i];
sd->bidi_override.push_back(r);
} else if (p_override[i].get_type() == Variant::VECTOR2I) {
const Vector2i &r = p_override[i];
sd->bidi_override.push_back(Vector3i(r.x, r.y, DIRECTION_INHERITED));
}
}
invalidate(sd, false);
@ -5544,8 +5550,31 @@ bool TextServerAdvanced::_shaped_text_shape(const RID &p_shaped) {
sd->script_iter = memnew(ScriptIterator(sd->text, 0, sd->text.length()));
}
int base_para_direction = UBIDI_DEFAULT_LTR;
switch (sd->direction) {
case DIRECTION_LTR: {
sd->para_direction = DIRECTION_LTR;
base_para_direction = UBIDI_LTR;
} break;
case DIRECTION_RTL: {
sd->para_direction = DIRECTION_RTL;
base_para_direction = UBIDI_RTL;
} break;
case DIRECTION_INHERITED:
case DIRECTION_AUTO: {
UBiDiDirection direction = ubidi_getBaseDirection(data, sd->utf16.length());
if (direction != UBIDI_NEUTRAL) {
sd->para_direction = (direction == UBIDI_RTL) ? DIRECTION_RTL : DIRECTION_LTR;
base_para_direction = direction;
} else {
sd->para_direction = DIRECTION_LTR;
base_para_direction = UBIDI_DEFAULT_LTR;
}
} break;
}
if (sd->bidi_override.is_empty()) {
sd->bidi_override.push_back(Vector2i(sd->start, sd->end));
sd->bidi_override.push_back(Vector3i(sd->start, sd->end, DIRECTION_INHERITED));
}
for (int ov = 0; ov < sd->bidi_override.size(); ov++) {
@ -5561,23 +5590,22 @@ bool TextServerAdvanced::_shaped_text_shape(const RID &p_shaped) {
UBiDi *bidi_iter = ubidi_openSized(end, 0, &err);
ERR_FAIL_COND_V_MSG(U_FAILURE(err), false, u_errorName(err));
switch (sd->direction) {
switch (static_cast<TextServer::Direction>(sd->bidi_override[ov].z)) {
case DIRECTION_LTR: {
ubidi_setPara(bidi_iter, data + start, end - start, UBIDI_LTR, nullptr, &err);
sd->para_direction = DIRECTION_LTR;
} break;
case DIRECTION_RTL: {
ubidi_setPara(bidi_iter, data + start, end - start, UBIDI_RTL, nullptr, &err);
sd->para_direction = DIRECTION_RTL;
} break;
case DIRECTION_INHERITED: {
ubidi_setPara(bidi_iter, data + start, end - start, base_para_direction, nullptr, &err);
} break;
case DIRECTION_AUTO: {
UBiDiDirection direction = ubidi_getBaseDirection(data + start, end - start);
if (direction != UBIDI_NEUTRAL) {
ubidi_setPara(bidi_iter, data + start, end - start, direction, nullptr, &err);
sd->para_direction = (direction == UBIDI_RTL) ? DIRECTION_RTL : DIRECTION_LTR;
} else {
ubidi_setPara(bidi_iter, data + start, end - start, UBIDI_DEFAULT_LTR, nullptr, &err);
sd->para_direction = DIRECTION_LTR;
}
} break;
}

View File

@ -499,7 +499,7 @@ class TextServerAdvanced : public TextServerExtension {
/* Intermediate data */
Char16String utf16;
Vector<UBiDi *> bidi_iter;
Vector<Vector2i> bidi_override;
Vector<Vector3i> bidi_override;
ScriptIterator *script_iter = nullptr;
hb_buffer_t *hb_buffer = nullptr;

View File

@ -2664,6 +2664,7 @@ void TextServerFallback::full_copy(ShapedTextDataFallback *p_shaped) {
RID TextServerFallback::_create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V_MSG(p_direction == DIRECTION_INHERITED, RID(), "Invalid text direction.");
ShapedTextDataFallback *sd = memnew(ShapedTextDataFallback);
sd->direction = p_direction;
@ -2687,6 +2688,7 @@ void TextServerFallback::_shaped_text_clear(const RID &p_shaped) {
}
void TextServerFallback::_shaped_text_set_direction(const RID &p_shaped, TextServer::Direction p_direction) {
ERR_FAIL_COND_MSG(p_direction == DIRECTION_INHERITED, "Invalid text direction.");
if (p_direction == DIRECTION_RTL) {
ERR_PRINT_ONCE("Right-to-left layout is not supported by this text server.");
}

View File

@ -442,7 +442,7 @@ void Label3D::_shape() {
TS->shaped_text_set_spacing(text_rid, TextServer::SpacingType(i), font->get_spacing(TextServer::SpacingType(i)));
}
TypedArray<Vector2i> stt;
TypedArray<Vector3i> stt;
if (st_parser == TextServer::STRUCTURED_TEXT_CUSTOM) {
GDVIRTUAL_CALL(_structured_text_parser, st_args, txt, stt);
} else {

View File

@ -143,7 +143,7 @@ private:
void _generate_glyph_surfaces(const Glyph &p_glyph, Vector2 &r_offset, const Color &p_modulate, int p_priority = 0, int p_outline_size = 0);
protected:
GDVIRTUAL2RC(Array, _structured_text_parser, Array, String)
GDVIRTUAL2RC(TypedArray<Vector3i>, _structured_text_parser, Array, String)
void _notification(int p_what);

View File

@ -2765,9 +2765,9 @@ void Control::end_bulk_theme_override() {
// Internationalization.
TypedArray<Vector2i> Control::structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const {
TypedArray<Vector3i> Control::structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const {
if (p_parser_type == TextServer::STRUCTURED_TEXT_CUSTOM) {
TypedArray<Vector2i> ret;
TypedArray<Vector3i> ret;
GDVIRTUAL_CALL(_structured_text_parser, p_args, p_text, ret);
return ret;
} else {

View File

@ -145,7 +145,7 @@ public:
TEXT_DIRECTION_AUTO = TextServer::DIRECTION_AUTO,
TEXT_DIRECTION_LTR = TextServer::DIRECTION_LTR,
TEXT_DIRECTION_RTL = TextServer::DIRECTION_RTL,
TEXT_DIRECTION_INHERITED,
TEXT_DIRECTION_INHERITED = TextServer::DIRECTION_INHERITED,
};
private:
@ -330,7 +330,7 @@ protected:
// Internationalization.
virtual TypedArray<Vector2i> structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const;
virtual TypedArray<Vector3i> structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const;
// Base object overrides.
@ -340,7 +340,7 @@ protected:
// Exposed virtual methods.
GDVIRTUAL1RC(bool, _has_point, Vector2)
GDVIRTUAL2RC(TypedArray<Vector2i>, _structured_text_parser, Array, String)
GDVIRTUAL2RC(TypedArray<Vector3i>, _structured_text_parser, Array, String)
GDVIRTUAL0RC(Vector2, _get_minimum_size)
GDVIRTUAL1RC(Variant, _get_drag_data, Vector2)

View File

@ -4074,8 +4074,8 @@ void RichTextLabel::append_text(const String &p_bbcode) {
st_parser_type = TextServer::STRUCTURED_TEXT_EMAIL;
} else if (subtag_a[1] == "l" || subtag_a[1] == "list") {
st_parser_type = TextServer::STRUCTURED_TEXT_LIST;
} else if (subtag_a[1] == "n" || subtag_a[1] == "none") {
st_parser_type = TextServer::STRUCTURED_TEXT_NONE;
} else if (subtag_a[1] == "n" || subtag_a[1] == "gdscript") {
st_parser_type = TextServer::STRUCTURED_TEXT_GDSCRIPT;
} else if (subtag_a[1] == "c" || subtag_a[1] == "custom") {
st_parser_type = TextServer::STRUCTURED_TEXT_CUSTOM;
}

View File

@ -1208,7 +1208,15 @@ void TextEdit::_notification(int p_what) {
char_ofs = 0;
}
for (int j = 0; j < gl_size; j++) {
const Variant *color_data = color_map.getptr(glyphs[j].start);
int64_t color_start = -1;
for (const Variant *key = color_map.next(nullptr); key; key = color_map.next(key)) {
if (int64_t(*key) <= glyphs[j].start) {
color_start = *key;
} else {
break;
}
}
const Variant *color_data = (color_start >= 0) ? color_map.getptr(color_start) : nullptr;
if (color_data != nullptr) {
current_color = (color_data->operator Dictionary()).get("color", font_color);
if (!editable && current_color.a > font_readonly_color.a) {

View File

@ -332,7 +332,7 @@ void TreeItem::set_structured_text_bidi_override(int p_column, TextServer::Struc
}
TextServer::StructuredTextParser TreeItem::get_structured_text_bidi_override(int p_column) const {
ERR_FAIL_INDEX_V(p_column, cells.size(), TextServer::STRUCTURED_TEXT_NONE);
ERR_FAIL_INDEX_V(p_column, cells.size(), TextServer::STRUCTURED_TEXT_DEFAULT);
return cells[p_column].st_parser;
}

View File

@ -2901,7 +2901,7 @@ void TextMesh::_create_mesh_array(Array &p_arr) const {
TS->shaped_text_set_spacing(text_rid, TextServer::SpacingType(i), font->get_spacing(TextServer::SpacingType(i)));
}
Array stt;
TypedArray<Vector3i> stt;
if (st_parser == TextServer::STRUCTURED_TEXT_CUSTOM) {
GDVIRTUAL_CALL(_structured_text_parser, st_args, txt, stt);
} else {

View File

@ -622,7 +622,7 @@ protected:
virtual void _create_mesh_array(Array &p_arr) const override;
public:
GDVIRTUAL2RC(Array, _structured_text_parser, Array, String)
GDVIRTUAL2RC(TypedArray<Vector3i>, _structured_text_parser, Array, String)
TextMesh();
~TextMesh();

View File

@ -1373,8 +1373,8 @@ String TextServerExtension::string_to_lower(const String &p_string, const String
return p_string;
}
TypedArray<Vector2i> TextServerExtension::parse_structured_text(StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const {
TypedArray<Vector2i> ret;
TypedArray<Vector3i> TextServerExtension::parse_structured_text(StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const {
TypedArray<Vector3i> ret;
GDVIRTUAL_CALL(_parse_structured_text, p_parser_type, p_args, p_text, ret);
return ret;
}

View File

@ -521,8 +521,8 @@ public:
GDVIRTUAL2RC(String, _string_to_upper, const String &, const String &);
GDVIRTUAL2RC(String, _string_to_lower, const String &, const String &);
TypedArray<Vector2i> parse_structured_text(StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const;
GDVIRTUAL3RC(TypedArray<Vector2i>, _parse_structured_text, StructuredTextParser, const Array &, const String &);
TypedArray<Vector3i> parse_structured_text(StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const;
GDVIRTUAL3RC(TypedArray<Vector3i>, _parse_structured_text, StructuredTextParser, const Array &, const String &);
virtual int64_t is_confusable(const String &p_string, const PackedStringArray &p_dict) const override;
virtual bool spoof_check(const String &p_string) const override;

View File

@ -483,6 +483,7 @@ void TextServer::_bind_methods() {
BIND_ENUM_CONSTANT(DIRECTION_AUTO);
BIND_ENUM_CONSTANT(DIRECTION_LTR);
BIND_ENUM_CONSTANT(DIRECTION_RTL);
BIND_ENUM_CONSTANT(DIRECTION_INHERITED);
/* Orientation */
BIND_ENUM_CONSTANT(ORIENTATION_HORIZONTAL);
@ -599,7 +600,7 @@ void TextServer::_bind_methods() {
BIND_ENUM_CONSTANT(STRUCTURED_TEXT_FILE);
BIND_ENUM_CONSTANT(STRUCTURED_TEXT_EMAIL);
BIND_ENUM_CONSTANT(STRUCTURED_TEXT_LIST);
BIND_ENUM_CONSTANT(STRUCTURED_TEXT_NONE);
BIND_ENUM_CONSTANT(STRUCTURED_TEXT_GDSCRIPT);
BIND_ENUM_CONSTANT(STRUCTURED_TEXT_CUSTOM);
}
@ -1692,22 +1693,22 @@ String TextServer::strip_diacritics(const String &p_string) const {
return result;
}
TypedArray<Vector2i> TextServer::parse_structured_text(StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const {
TypedArray<Vector2i> ret;
TypedArray<Vector3i> TextServer::parse_structured_text(StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const {
TypedArray<Vector3i> ret;
switch (p_parser_type) {
case STRUCTURED_TEXT_URI: {
int prev = 0;
for (int i = 0; i < p_text.length(); i++) {
if ((p_text[i] == '\\') || (p_text[i] == '/') || (p_text[i] == '.') || (p_text[i] == ':') || (p_text[i] == '&') || (p_text[i] == '=') || (p_text[i] == '@') || (p_text[i] == '?') || (p_text[i] == '#')) {
if (prev != i) {
ret.push_back(Vector2i(prev, i));
ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
}
ret.push_back(Vector2i(i, i + 1));
ret.push_back(Vector3i(i, i + 1, TextServer::DIRECTION_LTR));
prev = i + 1;
}
}
if (prev != p_text.length()) {
ret.push_back(Vector2i(prev, p_text.length()));
ret.push_back(Vector3i(prev, p_text.length(), TextServer::DIRECTION_AUTO));
}
} break;
case STRUCTURED_TEXT_FILE: {
@ -1715,14 +1716,14 @@ TypedArray<Vector2i> TextServer::parse_structured_text(StructuredTextParser p_pa
for (int i = 0; i < p_text.length(); i++) {
if ((p_text[i] == '\\') || (p_text[i] == '/') || (p_text[i] == ':')) {
if (prev != i) {
ret.push_back(Vector2i(prev, i));
ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
}
ret.push_back(Vector2i(i, i + 1));
ret.push_back(Vector3i(i, i + 1, TextServer::DIRECTION_LTR));
prev = i + 1;
}
}
if (prev != p_text.length()) {
ret.push_back(Vector2i(prev, p_text.length()));
ret.push_back(Vector3i(prev, p_text.length(), TextServer::DIRECTION_AUTO));
}
} break;
case STRUCTURED_TEXT_EMAIL: {
@ -1731,19 +1732,19 @@ TypedArray<Vector2i> TextServer::parse_structured_text(StructuredTextParser p_pa
for (int i = 0; i < p_text.length(); i++) {
if ((p_text[i] == '@') && local) { // Add full "local" as single context.
local = false;
ret.push_back(Vector2i(prev, i));
ret.push_back(Vector2i(i, i + 1));
ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
ret.push_back(Vector3i(i, i + 1, TextServer::DIRECTION_LTR));
prev = i + 1;
} else if (!local && (p_text[i] == '.')) { // Add each dot separated "domain" part as context.
if (prev != i) {
ret.push_back(Vector2i(prev, i));
ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
}
ret.push_back(Vector2i(i, i + 1));
ret.push_back(Vector3i(i, i + 1, TextServer::DIRECTION_LTR));
prev = i + 1;
}
}
if (prev != p_text.length()) {
ret.push_back(Vector2i(prev, p_text.length()));
ret.push_back(Vector3i(prev, p_text.length(), TextServer::DIRECTION_AUTO));
}
} break;
case STRUCTURED_TEXT_LIST: {
@ -1752,18 +1753,97 @@ TypedArray<Vector2i> TextServer::parse_structured_text(StructuredTextParser p_pa
int prev = 0;
for (int i = 0; i < tags.size(); i++) {
if (prev != i) {
ret.push_back(Vector2i(prev, prev + tags[i].length()));
ret.push_back(Vector3i(prev, prev + tags[i].length(), TextServer::DIRECTION_INHERITED));
}
ret.push_back(Vector2i(prev + tags[i].length(), prev + tags[i].length() + 1));
ret.push_back(Vector3i(prev + tags[i].length(), prev + tags[i].length() + 1, TextServer::DIRECTION_INHERITED));
prev = prev + tags[i].length() + 1;
}
}
} break;
case STRUCTURED_TEXT_GDSCRIPT: {
bool in_string_literal = false;
bool in_string_literal_single = false;
bool in_id = false;
int prev = 0;
for (int i = 0; i < p_text.length(); i++) {
char32_t c = p_text[i];
if (in_string_literal) {
if (c == '\\') {
i++;
continue; // Skip escaped chars.
} else if (c == '\"') {
// String literal end, push string and ".
if (prev != i) {
ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
}
prev = i + 1;
ret.push_back(Vector3i(i, i + 1, TextServer::DIRECTION_LTR));
in_string_literal = false;
}
} else if (in_string_literal_single) {
if (c == '\\') {
i++;
continue; // Skip escaped chars.
} else if (c == '\'') {
// String literal end, push string and '.
if (prev != i) {
ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
}
prev = i + 1;
ret.push_back(Vector3i(i, i + 1, TextServer::DIRECTION_LTR));
in_string_literal_single = false;
}
} else if (in_id) {
if (!is_unicode_identifier_continue(c)) {
// End of id, push id.
if (prev != i) {
ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
}
prev = i;
in_id = false;
}
} else if (is_unicode_identifier_start(c)) {
// Start of new id, push prev element.
if (prev != i) {
ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
}
prev = i;
in_id = true;
} else if (c == '\"') {
// String literal start, push prev element and ".
if (prev != i) {
ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
}
prev = i + 1;
ret.push_back(Vector3i(i, i + 1, TextServer::DIRECTION_LTR));
in_string_literal = true;
} else if (c == '\'') {
// String literal start, push prev element and '.
if (prev != i) {
ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
}
prev = i + 1;
ret.push_back(Vector3i(i, i + 1, TextServer::DIRECTION_LTR));
in_string_literal_single = true;
} else if (c == '#') {
// Start of comment, push prev element and #, skip the rest of the text.
if (prev != i) {
ret.push_back(Vector3i(prev, i, TextServer::DIRECTION_AUTO));
}
prev = i + 1;
ret.push_back(Vector3i(i, i + 1, TextServer::DIRECTION_LTR));
break;
}
}
if (prev < p_text.length()) {
ret.push_back(Vector3i(prev, p_text.length(), TextServer::DIRECTION_AUTO));
}
} break;
case STRUCTURED_TEXT_CUSTOM:
case STRUCTURED_TEXT_NONE:
case STRUCTURED_TEXT_DEFAULT:
default: {
ret.push_back(Vector2i(0, p_text.length()));
ret.push_back(Vector3i(0, p_text.length(), TextServer::DIRECTION_INHERITED));
}
}
return ret;

View File

@ -65,7 +65,8 @@ public:
enum Direction {
DIRECTION_AUTO,
DIRECTION_LTR,
DIRECTION_RTL
DIRECTION_RTL,
DIRECTION_INHERITED,
};
enum Orientation {
@ -198,7 +199,7 @@ public:
STRUCTURED_TEXT_FILE,
STRUCTURED_TEXT_EMAIL,
STRUCTURED_TEXT_LIST,
STRUCTURED_TEXT_NONE,
STRUCTURED_TEXT_GDSCRIPT,
STRUCTURED_TEXT_CUSTOM
};
@ -505,7 +506,7 @@ public:
virtual String string_to_upper(const String &p_string, const String &p_language = "") const = 0;
virtual String string_to_lower(const String &p_string, const String &p_language = "") const = 0;
TypedArray<Vector2i> parse_structured_text(StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const;
TypedArray<Vector3i> parse_structured_text(StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const;
virtual void cleanup() {}

View File

@ -734,7 +734,7 @@ TEST_CASE("[SceneTree][Primitive][Text] Text Primitive") {
text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_FILE ||
text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_EMAIL ||
text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_LIST ||
text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_NONE ||
text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_GDSCRIPT ||
text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_CUSTOM));
CHECK(text->get_structured_text_bidi_override_options().size() >= 0);
CHECK(text->get_width() > 0);