Implement BiDi override mode for GDScript source.
This commit is contained in:
parent
b5f3ac522e
commit
5361ec9f43
21 changed files with 178 additions and 55 deletions
|
@ -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">
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -3680,6 +3680,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();
|
||||
|
@ -3705,6 +3706,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);
|
||||
|
@ -3764,8 +3766,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);
|
||||
|
@ -5570,8 +5576,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++) {
|
||||
|
@ -5587,23 +5616,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;
|
||||
}
|
||||
|
|
|
@ -500,7 +500,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;
|
||||
|
||||
|
|
|
@ -2683,6 +2683,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;
|
||||
|
@ -2706,6 +2707,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.");
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -2769,9 +2769,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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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() {}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue