mirror of
				https://github.com/godotengine/godot.git
				synced 2025-11-04 07:31:16 +00:00 
			
		
		
		
	Merge pull request #71598 from bruvzg/gdscript_bidi_override
Implement BiDi override mode for GDScript source.
This commit is contained in:
		
						commit
						65883cc73b
					
				
					 21 changed files with 178 additions and 55 deletions
				
			
		| 
						 | 
					@ -196,12 +196,12 @@
 | 
				
			||||||
			</description>
 | 
								</description>
 | 
				
			||||||
		</method>
 | 
							</method>
 | 
				
			||||||
		<method name="_structured_text_parser" qualifiers="virtual const">
 | 
							<method name="_structured_text_parser" qualifiers="virtual const">
 | 
				
			||||||
			<return type="Vector2i[]" />
 | 
								<return type="Vector3i[]" />
 | 
				
			||||||
			<param index="0" name="args" type="Array" />
 | 
								<param index="0" name="args" type="Array" />
 | 
				
			||||||
			<param index="1" name="text" type="String" />
 | 
								<param index="1" name="text" type="String" />
 | 
				
			||||||
			<description>
 | 
								<description>
 | 
				
			||||||
				User defined BiDi algorithm override function.
 | 
									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>
 | 
								</description>
 | 
				
			||||||
		</method>
 | 
							</method>
 | 
				
			||||||
		<method name="accept_event">
 | 
							<method name="accept_event">
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1042,7 +1042,7 @@
 | 
				
			||||||
			</description>
 | 
								</description>
 | 
				
			||||||
		</method>
 | 
							</method>
 | 
				
			||||||
		<method name="parse_structured_text" qualifiers="const">
 | 
							<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="0" name="parser_type" type="int" enum="TextServer.StructuredTextParser" />
 | 
				
			||||||
			<param index="1" name="args" type="Array" />
 | 
								<param index="1" name="args" type="Array" />
 | 
				
			||||||
			<param index="2" name="text" type="String" />
 | 
								<param index="2" name="text" type="String" />
 | 
				
			||||||
| 
						 | 
					@ -1634,6 +1634,9 @@
 | 
				
			||||||
		<constant name="DIRECTION_RTL" value="2" enum="Direction">
 | 
							<constant name="DIRECTION_RTL" value="2" enum="Direction">
 | 
				
			||||||
			Text is written from right to left.
 | 
								Text is written from right to left.
 | 
				
			||||||
		</constant>
 | 
							</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">
 | 
							<constant name="ORIENTATION_HORIZONTAL" value="0" enum="Orientation">
 | 
				
			||||||
			Text is written horizontally.
 | 
								Text is written horizontally.
 | 
				
			||||||
		</constant>
 | 
							</constant>
 | 
				
			||||||
| 
						 | 
					@ -1881,7 +1884,7 @@
 | 
				
			||||||
			Font have fixed-width characters.
 | 
								Font have fixed-width characters.
 | 
				
			||||||
		</constant>
 | 
							</constant>
 | 
				
			||||||
		<constant name="STRUCTURED_TEXT_DEFAULT" value="0" enum="StructuredTextParser">
 | 
							<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>
 | 
				
			||||||
		<constant name="STRUCTURED_TEXT_URI" value="1" enum="StructuredTextParser">
 | 
							<constant name="STRUCTURED_TEXT_URI" value="1" enum="StructuredTextParser">
 | 
				
			||||||
			BiDi override for URI.
 | 
								BiDi override for URI.
 | 
				
			||||||
| 
						 | 
					@ -1896,8 +1899,8 @@
 | 
				
			||||||
			BiDi override for lists.
 | 
								BiDi override for lists.
 | 
				
			||||||
			Structured text options: list separator [code]String[/code].
 | 
								Structured text options: list separator [code]String[/code].
 | 
				
			||||||
		</constant>
 | 
							</constant>
 | 
				
			||||||
		<constant name="STRUCTURED_TEXT_NONE" value="5" enum="StructuredTextParser">
 | 
							<constant name="STRUCTURED_TEXT_GDSCRIPT" value="5" enum="StructuredTextParser">
 | 
				
			||||||
			Use default Unicode BiDi algorithm.
 | 
								BiDi override for GDScript.
 | 
				
			||||||
		</constant>
 | 
							</constant>
 | 
				
			||||||
		<constant name="STRUCTURED_TEXT_CUSTOM" value="6" enum="StructuredTextParser">
 | 
							<constant name="STRUCTURED_TEXT_CUSTOM" value="6" enum="StructuredTextParser">
 | 
				
			||||||
			User defined structured text BiDi override function.
 | 
								User defined structured text BiDi override function.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -896,7 +896,7 @@
 | 
				
			||||||
			</description>
 | 
								</description>
 | 
				
			||||||
		</method>
 | 
							</method>
 | 
				
			||||||
		<method name="_parse_structured_text" qualifiers="virtual const">
 | 
							<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="0" name="parser_type" type="int" enum="TextServer.StructuredTextParser" />
 | 
				
			||||||
			<param index="1" name="args" type="Array" />
 | 
								<param index="1" name="args" type="Array" />
 | 
				
			||||||
			<param index="2" name="text" type="String" />
 | 
								<param index="2" name="text" type="String" />
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2086,6 +2086,7 @@ CodeTextEditor::CodeTextEditor() {
 | 
				
			||||||
	text_editor = memnew(CodeEdit);
 | 
						text_editor = memnew(CodeEdit);
 | 
				
			||||||
	add_child(text_editor);
 | 
						add_child(text_editor);
 | 
				
			||||||
	text_editor->set_v_size_flags(SIZE_EXPAND_FILL);
 | 
						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");
 | 
						int ot_mode = EDITOR_GET("interface/editor/code_font_contextual_ligatures");
 | 
				
			||||||
	Ref<FontVariation> fc = text_editor->get_theme_font(SNAME("font"));
 | 
						Ref<FontVariation> fc = text_editor->get_theme_font(SNAME("font"));
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3654,6 +3654,7 @@ void TextServerAdvanced::full_copy(ShapedTextDataAdvanced *p_shaped) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RID TextServerAdvanced::_create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
 | 
					RID TextServerAdvanced::_create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
 | 
				
			||||||
	_THREAD_SAFE_METHOD_
 | 
						_THREAD_SAFE_METHOD_
 | 
				
			||||||
 | 
						ERR_FAIL_COND_V_MSG(p_direction == DIRECTION_INHERITED, RID(), "Invalid text direction.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ShapedTextDataAdvanced *sd = memnew(ShapedTextDataAdvanced);
 | 
						ShapedTextDataAdvanced *sd = memnew(ShapedTextDataAdvanced);
 | 
				
			||||||
	sd->hb_buffer = hb_buffer_create();
 | 
						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) {
 | 
					void TextServerAdvanced::_shaped_text_set_direction(const RID &p_shaped, TextServer::Direction p_direction) {
 | 
				
			||||||
	ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
 | 
						ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
 | 
				
			||||||
 | 
						ERR_FAIL_COND_MSG(p_direction == DIRECTION_INHERITED, "Invalid text direction.");
 | 
				
			||||||
	ERR_FAIL_COND(!sd);
 | 
						ERR_FAIL_COND(!sd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	MutexLock lock(sd->mutex);
 | 
						MutexLock lock(sd->mutex);
 | 
				
			||||||
| 
						 | 
					@ -3738,8 +3740,12 @@ void TextServerAdvanced::_shaped_text_set_bidi_override(const RID &p_shaped, con
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	sd->bidi_override.clear();
 | 
						sd->bidi_override.clear();
 | 
				
			||||||
	for (int i = 0; i < p_override.size(); i++) {
 | 
						for (int i = 0; i < p_override.size(); i++) {
 | 
				
			||||||
		if (p_override[i].get_type() == Variant::VECTOR2I) {
 | 
							if (p_override[i].get_type() == Variant::VECTOR3I) {
 | 
				
			||||||
			sd->bidi_override.push_back(p_override[i]);
 | 
								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);
 | 
						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()));
 | 
							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()) {
 | 
						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++) {
 | 
						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);
 | 
							UBiDi *bidi_iter = ubidi_openSized(end, 0, &err);
 | 
				
			||||||
		ERR_FAIL_COND_V_MSG(U_FAILURE(err), false, u_errorName(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: {
 | 
								case DIRECTION_LTR: {
 | 
				
			||||||
				ubidi_setPara(bidi_iter, data + start, end - start, UBIDI_LTR, nullptr, &err);
 | 
									ubidi_setPara(bidi_iter, data + start, end - start, UBIDI_LTR, nullptr, &err);
 | 
				
			||||||
				sd->para_direction = DIRECTION_LTR;
 | 
					 | 
				
			||||||
			} break;
 | 
								} break;
 | 
				
			||||||
			case DIRECTION_RTL: {
 | 
								case DIRECTION_RTL: {
 | 
				
			||||||
				ubidi_setPara(bidi_iter, data + start, end - start, UBIDI_RTL, nullptr, &err);
 | 
									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;
 | 
								} break;
 | 
				
			||||||
			case DIRECTION_AUTO: {
 | 
								case DIRECTION_AUTO: {
 | 
				
			||||||
				UBiDiDirection direction = ubidi_getBaseDirection(data + start, end - start);
 | 
									UBiDiDirection direction = ubidi_getBaseDirection(data + start, end - start);
 | 
				
			||||||
				if (direction != UBIDI_NEUTRAL) {
 | 
									if (direction != UBIDI_NEUTRAL) {
 | 
				
			||||||
					ubidi_setPara(bidi_iter, data + start, end - start, direction, nullptr, &err);
 | 
										ubidi_setPara(bidi_iter, data + start, end - start, direction, nullptr, &err);
 | 
				
			||||||
					sd->para_direction = (direction == UBIDI_RTL) ? DIRECTION_RTL : DIRECTION_LTR;
 | 
					 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
					ubidi_setPara(bidi_iter, data + start, end - start, UBIDI_DEFAULT_LTR, nullptr, &err);
 | 
										ubidi_setPara(bidi_iter, data + start, end - start, UBIDI_DEFAULT_LTR, nullptr, &err);
 | 
				
			||||||
					sd->para_direction = DIRECTION_LTR;
 | 
					 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			} break;
 | 
								} break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -499,7 +499,7 @@ class TextServerAdvanced : public TextServerExtension {
 | 
				
			||||||
		/* Intermediate data */
 | 
							/* Intermediate data */
 | 
				
			||||||
		Char16String utf16;
 | 
							Char16String utf16;
 | 
				
			||||||
		Vector<UBiDi *> bidi_iter;
 | 
							Vector<UBiDi *> bidi_iter;
 | 
				
			||||||
		Vector<Vector2i> bidi_override;
 | 
							Vector<Vector3i> bidi_override;
 | 
				
			||||||
		ScriptIterator *script_iter = nullptr;
 | 
							ScriptIterator *script_iter = nullptr;
 | 
				
			||||||
		hb_buffer_t *hb_buffer = nullptr;
 | 
							hb_buffer_t *hb_buffer = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2664,6 +2664,7 @@ void TextServerFallback::full_copy(ShapedTextDataFallback *p_shaped) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RID TextServerFallback::_create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
 | 
					RID TextServerFallback::_create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
 | 
				
			||||||
	_THREAD_SAFE_METHOD_
 | 
						_THREAD_SAFE_METHOD_
 | 
				
			||||||
 | 
						ERR_FAIL_COND_V_MSG(p_direction == DIRECTION_INHERITED, RID(), "Invalid text direction.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ShapedTextDataFallback *sd = memnew(ShapedTextDataFallback);
 | 
						ShapedTextDataFallback *sd = memnew(ShapedTextDataFallback);
 | 
				
			||||||
	sd->direction = p_direction;
 | 
						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) {
 | 
					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) {
 | 
						if (p_direction == DIRECTION_RTL) {
 | 
				
			||||||
		ERR_PRINT_ONCE("Right-to-left layout is not supported by this text server.");
 | 
							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)));
 | 
								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) {
 | 
							if (st_parser == TextServer::STRUCTURED_TEXT_CUSTOM) {
 | 
				
			||||||
			GDVIRTUAL_CALL(_structured_text_parser, st_args, txt, stt);
 | 
								GDVIRTUAL_CALL(_structured_text_parser, st_args, txt, stt);
 | 
				
			||||||
		} else {
 | 
							} 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);
 | 
						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:
 | 
					protected:
 | 
				
			||||||
	GDVIRTUAL2RC(Array, _structured_text_parser, Array, String)
 | 
						GDVIRTUAL2RC(TypedArray<Vector3i>, _structured_text_parser, Array, String)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void _notification(int p_what);
 | 
						void _notification(int p_what);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2765,9 +2765,9 @@ void Control::end_bulk_theme_override() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Internationalization.
 | 
					// 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) {
 | 
						if (p_parser_type == TextServer::STRUCTURED_TEXT_CUSTOM) {
 | 
				
			||||||
		TypedArray<Vector2i> ret;
 | 
							TypedArray<Vector3i> ret;
 | 
				
			||||||
		GDVIRTUAL_CALL(_structured_text_parser, p_args, p_text, ret);
 | 
							GDVIRTUAL_CALL(_structured_text_parser, p_args, p_text, ret);
 | 
				
			||||||
		return ret;
 | 
							return ret;
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -145,7 +145,7 @@ public:
 | 
				
			||||||
		TEXT_DIRECTION_AUTO = TextServer::DIRECTION_AUTO,
 | 
							TEXT_DIRECTION_AUTO = TextServer::DIRECTION_AUTO,
 | 
				
			||||||
		TEXT_DIRECTION_LTR = TextServer::DIRECTION_LTR,
 | 
							TEXT_DIRECTION_LTR = TextServer::DIRECTION_LTR,
 | 
				
			||||||
		TEXT_DIRECTION_RTL = TextServer::DIRECTION_RTL,
 | 
							TEXT_DIRECTION_RTL = TextServer::DIRECTION_RTL,
 | 
				
			||||||
		TEXT_DIRECTION_INHERITED,
 | 
							TEXT_DIRECTION_INHERITED = TextServer::DIRECTION_INHERITED,
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
| 
						 | 
					@ -330,7 +330,7 @@ protected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Internationalization.
 | 
						// 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.
 | 
						// Base object overrides.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -340,7 +340,7 @@ protected:
 | 
				
			||||||
	// Exposed virtual methods.
 | 
						// Exposed virtual methods.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	GDVIRTUAL1RC(bool, _has_point, Vector2)
 | 
						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)
 | 
						GDVIRTUAL0RC(Vector2, _get_minimum_size)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	GDVIRTUAL1RC(Variant, _get_drag_data, Vector2)
 | 
						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;
 | 
												st_parser_type = TextServer::STRUCTURED_TEXT_EMAIL;
 | 
				
			||||||
						} else if (subtag_a[1] == "l" || subtag_a[1] == "list") {
 | 
											} else if (subtag_a[1] == "l" || subtag_a[1] == "list") {
 | 
				
			||||||
							st_parser_type = TextServer::STRUCTURED_TEXT_LIST;
 | 
												st_parser_type = TextServer::STRUCTURED_TEXT_LIST;
 | 
				
			||||||
						} else if (subtag_a[1] == "n" || subtag_a[1] == "none") {
 | 
											} else if (subtag_a[1] == "n" || subtag_a[1] == "gdscript") {
 | 
				
			||||||
							st_parser_type = TextServer::STRUCTURED_TEXT_NONE;
 | 
												st_parser_type = TextServer::STRUCTURED_TEXT_GDSCRIPT;
 | 
				
			||||||
						} else if (subtag_a[1] == "c" || subtag_a[1] == "custom") {
 | 
											} else if (subtag_a[1] == "c" || subtag_a[1] == "custom") {
 | 
				
			||||||
							st_parser_type = TextServer::STRUCTURED_TEXT_CUSTOM;
 | 
												st_parser_type = TextServer::STRUCTURED_TEXT_CUSTOM;
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1208,7 +1208,15 @@ void TextEdit::_notification(int p_what) {
 | 
				
			||||||
						char_ofs = 0;
 | 
											char_ofs = 0;
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
					for (int j = 0; j < gl_size; j++) {
 | 
										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) {
 | 
											if (color_data != nullptr) {
 | 
				
			||||||
							current_color = (color_data->operator Dictionary()).get("color", font_color);
 | 
												current_color = (color_data->operator Dictionary()).get("color", font_color);
 | 
				
			||||||
							if (!editable && current_color.a > font_readonly_color.a) {
 | 
												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 {
 | 
					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;
 | 
						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)));
 | 
								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) {
 | 
							if (st_parser == TextServer::STRUCTURED_TEXT_CUSTOM) {
 | 
				
			||||||
			GDVIRTUAL_CALL(_structured_text_parser, st_args, txt, stt);
 | 
								GDVIRTUAL_CALL(_structured_text_parser, st_args, txt, stt);
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -622,7 +622,7 @@ protected:
 | 
				
			||||||
	virtual void _create_mesh_array(Array &p_arr) const override;
 | 
						virtual void _create_mesh_array(Array &p_arr) const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	GDVIRTUAL2RC(Array, _structured_text_parser, Array, String)
 | 
						GDVIRTUAL2RC(TypedArray<Vector3i>, _structured_text_parser, Array, String)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	TextMesh();
 | 
						TextMesh();
 | 
				
			||||||
	~TextMesh();
 | 
						~TextMesh();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1373,8 +1373,8 @@ String TextServerExtension::string_to_lower(const String &p_string, const String
 | 
				
			||||||
	return p_string;
 | 
						return p_string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TypedArray<Vector2i> TextServerExtension::parse_structured_text(StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const {
 | 
					TypedArray<Vector3i> TextServerExtension::parse_structured_text(StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const {
 | 
				
			||||||
	TypedArray<Vector2i> ret;
 | 
						TypedArray<Vector3i> ret;
 | 
				
			||||||
	GDVIRTUAL_CALL(_parse_structured_text, p_parser_type, p_args, p_text, ret);
 | 
						GDVIRTUAL_CALL(_parse_structured_text, p_parser_type, p_args, p_text, ret);
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -521,8 +521,8 @@ public:
 | 
				
			||||||
	GDVIRTUAL2RC(String, _string_to_upper, const String &, const String &);
 | 
						GDVIRTUAL2RC(String, _string_to_upper, const String &, const String &);
 | 
				
			||||||
	GDVIRTUAL2RC(String, _string_to_lower, 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;
 | 
						TypedArray<Vector3i> 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 &);
 | 
						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 int64_t is_confusable(const String &p_string, const PackedStringArray &p_dict) const override;
 | 
				
			||||||
	virtual bool spoof_check(const String &p_string) 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_AUTO);
 | 
				
			||||||
	BIND_ENUM_CONSTANT(DIRECTION_LTR);
 | 
						BIND_ENUM_CONSTANT(DIRECTION_LTR);
 | 
				
			||||||
	BIND_ENUM_CONSTANT(DIRECTION_RTL);
 | 
						BIND_ENUM_CONSTANT(DIRECTION_RTL);
 | 
				
			||||||
 | 
						BIND_ENUM_CONSTANT(DIRECTION_INHERITED);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Orientation */
 | 
						/* Orientation */
 | 
				
			||||||
	BIND_ENUM_CONSTANT(ORIENTATION_HORIZONTAL);
 | 
						BIND_ENUM_CONSTANT(ORIENTATION_HORIZONTAL);
 | 
				
			||||||
| 
						 | 
					@ -599,7 +600,7 @@ void TextServer::_bind_methods() {
 | 
				
			||||||
	BIND_ENUM_CONSTANT(STRUCTURED_TEXT_FILE);
 | 
						BIND_ENUM_CONSTANT(STRUCTURED_TEXT_FILE);
 | 
				
			||||||
	BIND_ENUM_CONSTANT(STRUCTURED_TEXT_EMAIL);
 | 
						BIND_ENUM_CONSTANT(STRUCTURED_TEXT_EMAIL);
 | 
				
			||||||
	BIND_ENUM_CONSTANT(STRUCTURED_TEXT_LIST);
 | 
						BIND_ENUM_CONSTANT(STRUCTURED_TEXT_LIST);
 | 
				
			||||||
	BIND_ENUM_CONSTANT(STRUCTURED_TEXT_NONE);
 | 
						BIND_ENUM_CONSTANT(STRUCTURED_TEXT_GDSCRIPT);
 | 
				
			||||||
	BIND_ENUM_CONSTANT(STRUCTURED_TEXT_CUSTOM);
 | 
						BIND_ENUM_CONSTANT(STRUCTURED_TEXT_CUSTOM);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1692,22 +1693,22 @@ String TextServer::strip_diacritics(const String &p_string) const {
 | 
				
			||||||
	return result;
 | 
						return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TypedArray<Vector2i> TextServer::parse_structured_text(StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const {
 | 
					TypedArray<Vector3i> TextServer::parse_structured_text(StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const {
 | 
				
			||||||
	TypedArray<Vector2i> ret;
 | 
						TypedArray<Vector3i> ret;
 | 
				
			||||||
	switch (p_parser_type) {
 | 
						switch (p_parser_type) {
 | 
				
			||||||
		case STRUCTURED_TEXT_URI: {
 | 
							case STRUCTURED_TEXT_URI: {
 | 
				
			||||||
			int prev = 0;
 | 
								int prev = 0;
 | 
				
			||||||
			for (int i = 0; i < p_text.length(); i++) {
 | 
								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 ((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) {
 | 
										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;
 | 
										prev = i + 1;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if (prev != p_text.length()) {
 | 
								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;
 | 
							} break;
 | 
				
			||||||
		case STRUCTURED_TEXT_FILE: {
 | 
							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++) {
 | 
								for (int i = 0; i < p_text.length(); i++) {
 | 
				
			||||||
				if ((p_text[i] == '\\') || (p_text[i] == '/') || (p_text[i] == ':')) {
 | 
									if ((p_text[i] == '\\') || (p_text[i] == '/') || (p_text[i] == ':')) {
 | 
				
			||||||
					if (prev != 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;
 | 
										prev = i + 1;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if (prev != p_text.length()) {
 | 
								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;
 | 
							} break;
 | 
				
			||||||
		case STRUCTURED_TEXT_EMAIL: {
 | 
							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++) {
 | 
								for (int i = 0; i < p_text.length(); i++) {
 | 
				
			||||||
				if ((p_text[i] == '@') && local) { // Add full "local" as single context.
 | 
									if ((p_text[i] == '@') && local) { // Add full "local" as single context.
 | 
				
			||||||
					local = false;
 | 
										local = false;
 | 
				
			||||||
					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;
 | 
										prev = i + 1;
 | 
				
			||||||
				} else if (!local && (p_text[i] == '.')) { // Add each dot separated "domain" part as context.
 | 
									} else if (!local && (p_text[i] == '.')) { // Add each dot separated "domain" part as context.
 | 
				
			||||||
					if (prev != 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;
 | 
										prev = i + 1;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if (prev != p_text.length()) {
 | 
								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;
 | 
							} break;
 | 
				
			||||||
		case STRUCTURED_TEXT_LIST: {
 | 
							case STRUCTURED_TEXT_LIST: {
 | 
				
			||||||
| 
						 | 
					@ -1752,18 +1753,97 @@ TypedArray<Vector2i> TextServer::parse_structured_text(StructuredTextParser p_pa
 | 
				
			||||||
				int prev = 0;
 | 
									int prev = 0;
 | 
				
			||||||
				for (int i = 0; i < tags.size(); i++) {
 | 
									for (int i = 0; i < tags.size(); i++) {
 | 
				
			||||||
					if (prev != 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;
 | 
										prev = prev + tags[i].length() + 1;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} break;
 | 
							} 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_CUSTOM:
 | 
				
			||||||
		case STRUCTURED_TEXT_NONE:
 | 
					 | 
				
			||||||
		case STRUCTURED_TEXT_DEFAULT:
 | 
							case STRUCTURED_TEXT_DEFAULT:
 | 
				
			||||||
		default: {
 | 
							default: {
 | 
				
			||||||
			ret.push_back(Vector2i(0, p_text.length()));
 | 
								ret.push_back(Vector3i(0, p_text.length(), TextServer::DIRECTION_INHERITED));
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -65,7 +65,8 @@ public:
 | 
				
			||||||
	enum Direction {
 | 
						enum Direction {
 | 
				
			||||||
		DIRECTION_AUTO,
 | 
							DIRECTION_AUTO,
 | 
				
			||||||
		DIRECTION_LTR,
 | 
							DIRECTION_LTR,
 | 
				
			||||||
		DIRECTION_RTL
 | 
							DIRECTION_RTL,
 | 
				
			||||||
 | 
							DIRECTION_INHERITED,
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	enum Orientation {
 | 
						enum Orientation {
 | 
				
			||||||
| 
						 | 
					@ -198,7 +199,7 @@ public:
 | 
				
			||||||
		STRUCTURED_TEXT_FILE,
 | 
							STRUCTURED_TEXT_FILE,
 | 
				
			||||||
		STRUCTURED_TEXT_EMAIL,
 | 
							STRUCTURED_TEXT_EMAIL,
 | 
				
			||||||
		STRUCTURED_TEXT_LIST,
 | 
							STRUCTURED_TEXT_LIST,
 | 
				
			||||||
		STRUCTURED_TEXT_NONE,
 | 
							STRUCTURED_TEXT_GDSCRIPT,
 | 
				
			||||||
		STRUCTURED_TEXT_CUSTOM
 | 
							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_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;
 | 
						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() {}
 | 
						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_FILE ||
 | 
				
			||||||
				text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_EMAIL ||
 | 
									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_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));
 | 
									text->get_structured_text_bidi_override() == TextServer::STRUCTURED_TEXT_CUSTOM));
 | 
				
			||||||
		CHECK(text->get_structured_text_bidi_override_options().size() >= 0);
 | 
							CHECK(text->get_structured_text_bidi_override_options().size() >= 0);
 | 
				
			||||||
		CHECK(text->get_width() > 0);
 | 
							CHECK(text->get_width() > 0);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue