diff options
Diffstat (limited to 'servers/text_server.cpp')
-rw-r--r-- | servers/text_server.cpp | 511 |
1 files changed, 363 insertions, 148 deletions
diff --git a/servers/text_server.cpp b/servers/text_server.cpp index fe5ade88a1..027109b67d 100644 --- a/servers/text_server.cpp +++ b/servers/text_server.cpp @@ -1,34 +1,35 @@ -/*************************************************************************/ -/* text_server.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ +/**************************************************************************/ +/* text_server.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ #include "servers/text_server.h" +#include "core/variant/typed_array.h" #include "servers/rendering_server.h" TextServerManager *TextServerManager::singleton = nullptr; @@ -103,8 +104,8 @@ Ref<TextServer> TextServerManager::find_interface(const String &p_name) const { return interfaces[idx]; } -Array TextServerManager::get_interfaces() const { - Array ret; +TypedArray<Dictionary> TextServerManager::get_interfaces() const { + TypedArray<Dictionary> ret; for (int i = 0; i < interfaces.size(); i++) { Dictionary iface_info; @@ -222,8 +223,14 @@ void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("font_set_style_name", "font_rid", "name"), &TextServer::font_set_style_name); ClassDB::bind_method(D_METHOD("font_get_style_name", "font_rid"), &TextServer::font_get_style_name); - ClassDB::bind_method(D_METHOD("font_set_antialiased", "font_rid", "antialiased"), &TextServer::font_set_antialiased); - ClassDB::bind_method(D_METHOD("font_is_antialiased", "font_rid"), &TextServer::font_is_antialiased); + ClassDB::bind_method(D_METHOD("font_set_weight", "font_rid", "weight"), &TextServer::font_set_weight); + ClassDB::bind_method(D_METHOD("font_get_weight", "font_rid"), &TextServer::font_get_weight); + + ClassDB::bind_method(D_METHOD("font_set_stretch", "font_rid", "weight"), &TextServer::font_set_stretch); + ClassDB::bind_method(D_METHOD("font_get_stretch", "font_rid"), &TextServer::font_get_stretch); + + ClassDB::bind_method(D_METHOD("font_set_antialiasing", "font_rid", "antialiasing"), &TextServer::font_set_antialiasing); + ClassDB::bind_method(D_METHOD("font_get_antialiasing", "font_rid"), &TextServer::font_get_antialiasing); ClassDB::bind_method(D_METHOD("font_set_generate_mipmaps", "font_rid", "generate_mipmaps"), &TextServer::font_set_generate_mipmaps); ClassDB::bind_method(D_METHOD("font_get_generate_mipmaps", "font_rid"), &TextServer::font_get_generate_mipmaps); @@ -240,6 +247,9 @@ void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("font_set_fixed_size", "font_rid", "fixed_size"), &TextServer::font_set_fixed_size); ClassDB::bind_method(D_METHOD("font_get_fixed_size", "font_rid"), &TextServer::font_get_fixed_size); + ClassDB::bind_method(D_METHOD("font_set_allow_system_fallback", "font_rid", "allow_system_fallback"), &TextServer::font_set_allow_system_fallback); + ClassDB::bind_method(D_METHOD("font_is_allow_system_fallback", "font_rid"), &TextServer::font_is_allow_system_fallback); + ClassDB::bind_method(D_METHOD("font_set_force_autohinter", "font_rid", "force_autohinter"), &TextServer::font_set_force_autohinter); ClassDB::bind_method(D_METHOD("font_is_force_autohinter", "font_rid"), &TextServer::font_is_force_autohinter); @@ -280,9 +290,6 @@ void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("font_set_scale", "font_rid", "size", "scale"), &TextServer::font_set_scale); ClassDB::bind_method(D_METHOD("font_get_scale", "font_rid", "size"), &TextServer::font_get_scale); - ClassDB::bind_method(D_METHOD("font_set_spacing", "font_rid", "size", "spacing", "value"), &TextServer::font_set_spacing); - ClassDB::bind_method(D_METHOD("font_get_spacing", "font_rid", "size", "spacing"), &TextServer::font_get_spacing); - ClassDB::bind_method(D_METHOD("font_get_texture_count", "font_rid", "size"), &TextServer::font_get_texture_count); ClassDB::bind_method(D_METHOD("font_clear_textures", "font_rid", "size"), &TextServer::font_clear_textures); ClassDB::bind_method(D_METHOD("font_remove_texture", "font_rid", "size", "texture_index"), &TextServer::font_remove_texture); @@ -383,9 +390,12 @@ void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("shaped_text_set_preserve_control", "shaped", "enabled"), &TextServer::shaped_text_set_preserve_control); ClassDB::bind_method(D_METHOD("shaped_text_get_preserve_control", "shaped"), &TextServer::shaped_text_get_preserve_control); + ClassDB::bind_method(D_METHOD("shaped_text_set_spacing", "shaped", "spacing", "value"), &TextServer::shaped_text_set_spacing); + ClassDB::bind_method(D_METHOD("shaped_text_get_spacing", "shaped", "spacing"), &TextServer::shaped_text_get_spacing); + ClassDB::bind_method(D_METHOD("shaped_text_add_string", "shaped", "text", "fonts", "size", "opentype_features", "language", "meta"), &TextServer::shaped_text_add_string, DEFVAL(Dictionary()), DEFVAL(""), DEFVAL(Variant())); - ClassDB::bind_method(D_METHOD("shaped_text_add_object", "shaped", "key", "size", "inline_align", "length"), &TextServer::shaped_text_add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1)); - ClassDB::bind_method(D_METHOD("shaped_text_resize_object", "shaped", "key", "size", "inline_align"), &TextServer::shaped_text_resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER)); + ClassDB::bind_method(D_METHOD("shaped_text_add_object", "shaped", "key", "size", "inline_align", "length", "baseline"), &TextServer::shaped_text_add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1), DEFVAL(0.0)); + ClassDB::bind_method(D_METHOD("shaped_text_resize_object", "shaped", "key", "size", "inline_align", "baseline"), &TextServer::shaped_text_resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(0.0)); ClassDB::bind_method(D_METHOD("shaped_get_span_count", "shaped"), &TextServer::shaped_get_span_count); ClassDB::bind_method(D_METHOD("shaped_get_span_meta", "shaped", "index"), &TextServer::shaped_get_span_meta); @@ -444,31 +454,48 @@ void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("parse_number", "number", "language"), &TextServer::parse_number, DEFVAL("")); ClassDB::bind_method(D_METHOD("percent_sign", "language"), &TextServer::percent_sign, DEFVAL("")); - ClassDB::bind_method(D_METHOD("string_get_word_breaks", "string", "language"), &TextServer::string_get_word_breaks, DEFVAL("")); + ClassDB::bind_method(D_METHOD("string_get_word_breaks", "string", "language", "chars_per_line"), &TextServer::string_get_word_breaks, DEFVAL(""), DEFVAL(0)); + + ClassDB::bind_method(D_METHOD("is_confusable", "string", "dict"), &TextServer::is_confusable); + ClassDB::bind_method(D_METHOD("spoof_check", "string"), &TextServer::spoof_check); ClassDB::bind_method(D_METHOD("strip_diacritics", "string"), &TextServer::strip_diacritics); + ClassDB::bind_method(D_METHOD("is_valid_identifier", "string"), &TextServer::is_valid_identifier); ClassDB::bind_method(D_METHOD("string_to_upper", "string", "language"), &TextServer::string_to_upper, DEFVAL("")); ClassDB::bind_method(D_METHOD("string_to_lower", "string", "language"), &TextServer::string_to_lower, DEFVAL("")); ClassDB::bind_method(D_METHOD("parse_structured_text", "parser_type", "args", "text"), &TextServer::parse_structured_text); + /* Font AA */ + BIND_ENUM_CONSTANT(FONT_ANTIALIASING_NONE); + BIND_ENUM_CONSTANT(FONT_ANTIALIASING_GRAY); + BIND_ENUM_CONSTANT(FONT_ANTIALIASING_LCD); + + BIND_ENUM_CONSTANT(FONT_LCD_SUBPIXEL_LAYOUT_NONE); + BIND_ENUM_CONSTANT(FONT_LCD_SUBPIXEL_LAYOUT_HRGB); + BIND_ENUM_CONSTANT(FONT_LCD_SUBPIXEL_LAYOUT_HBGR); + BIND_ENUM_CONSTANT(FONT_LCD_SUBPIXEL_LAYOUT_VRGB); + BIND_ENUM_CONSTANT(FONT_LCD_SUBPIXEL_LAYOUT_VBGR); + BIND_ENUM_CONSTANT(FONT_LCD_SUBPIXEL_LAYOUT_MAX); + /* Direction */ 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); BIND_ENUM_CONSTANT(ORIENTATION_VERTICAL); /* JustificationFlag */ - BIND_ENUM_CONSTANT(JUSTIFICATION_NONE); - BIND_ENUM_CONSTANT(JUSTIFICATION_KASHIDA); - BIND_ENUM_CONSTANT(JUSTIFICATION_WORD_BOUND); - BIND_ENUM_CONSTANT(JUSTIFICATION_TRIM_EDGE_SPACES); - BIND_ENUM_CONSTANT(JUSTIFICATION_AFTER_LAST_TAB); - BIND_ENUM_CONSTANT(JUSTIFICATION_CONSTRAIN_ELLIPSIS); + BIND_BITFIELD_FLAG(JUSTIFICATION_NONE); + BIND_BITFIELD_FLAG(JUSTIFICATION_KASHIDA); + BIND_BITFIELD_FLAG(JUSTIFICATION_WORD_BOUND); + BIND_BITFIELD_FLAG(JUSTIFICATION_TRIM_EDGE_SPACES); + BIND_BITFIELD_FLAG(JUSTIFICATION_AFTER_LAST_TAB); + BIND_BITFIELD_FLAG(JUSTIFICATION_CONSTRAIN_ELLIPSIS); /* AutowrapMode */ BIND_ENUM_CONSTANT(AUTOWRAP_OFF); @@ -477,11 +504,12 @@ void TextServer::_bind_methods() { BIND_ENUM_CONSTANT(AUTOWRAP_WORD_SMART); /* LineBreakFlag */ - BIND_ENUM_CONSTANT(BREAK_NONE); - BIND_ENUM_CONSTANT(BREAK_MANDATORY); - BIND_ENUM_CONSTANT(BREAK_WORD_BOUND); - BIND_ENUM_CONSTANT(BREAK_GRAPHEME_BOUND); - BIND_ENUM_CONSTANT(BREAK_WORD_BOUND_ADAPTIVE); + BIND_BITFIELD_FLAG(BREAK_NONE); + BIND_BITFIELD_FLAG(BREAK_MANDATORY); + BIND_BITFIELD_FLAG(BREAK_WORD_BOUND); + BIND_BITFIELD_FLAG(BREAK_GRAPHEME_BOUND); + BIND_BITFIELD_FLAG(BREAK_ADAPTIVE); + BIND_BITFIELD_FLAG(BREAK_TRIM_EDGE_SPACES); /* VisibleCharactersBehavior */ BIND_ENUM_CONSTANT(VC_CHARS_BEFORE_SHAPING); @@ -498,25 +526,26 @@ void TextServer::_bind_methods() { BIND_ENUM_CONSTANT(OVERRUN_TRIM_WORD_ELLIPSIS); /* TextOverrunFlag */ - BIND_ENUM_CONSTANT(OVERRUN_NO_TRIM); - BIND_ENUM_CONSTANT(OVERRUN_TRIM); - BIND_ENUM_CONSTANT(OVERRUN_TRIM_WORD_ONLY); - BIND_ENUM_CONSTANT(OVERRUN_ADD_ELLIPSIS); - BIND_ENUM_CONSTANT(OVERRUN_ENFORCE_ELLIPSIS); - BIND_ENUM_CONSTANT(OVERRUN_JUSTIFICATION_AWARE); + BIND_BITFIELD_FLAG(OVERRUN_NO_TRIM); + BIND_BITFIELD_FLAG(OVERRUN_TRIM); + BIND_BITFIELD_FLAG(OVERRUN_TRIM_WORD_ONLY); + BIND_BITFIELD_FLAG(OVERRUN_ADD_ELLIPSIS); + BIND_BITFIELD_FLAG(OVERRUN_ENFORCE_ELLIPSIS); + BIND_BITFIELD_FLAG(OVERRUN_JUSTIFICATION_AWARE); /* GraphemeFlag */ - BIND_ENUM_CONSTANT(GRAPHEME_IS_VALID); - BIND_ENUM_CONSTANT(GRAPHEME_IS_RTL); - BIND_ENUM_CONSTANT(GRAPHEME_IS_VIRTUAL); - BIND_ENUM_CONSTANT(GRAPHEME_IS_SPACE); - BIND_ENUM_CONSTANT(GRAPHEME_IS_BREAK_HARD); - BIND_ENUM_CONSTANT(GRAPHEME_IS_BREAK_SOFT); - BIND_ENUM_CONSTANT(GRAPHEME_IS_TAB); - BIND_ENUM_CONSTANT(GRAPHEME_IS_ELONGATION); - BIND_ENUM_CONSTANT(GRAPHEME_IS_PUNCTUATION); - BIND_ENUM_CONSTANT(GRAPHEME_IS_UNDERSCORE); - BIND_ENUM_CONSTANT(GRAPHEME_IS_CONNECTED); + BIND_BITFIELD_FLAG(GRAPHEME_IS_VALID); + BIND_BITFIELD_FLAG(GRAPHEME_IS_RTL); + BIND_BITFIELD_FLAG(GRAPHEME_IS_VIRTUAL); + BIND_BITFIELD_FLAG(GRAPHEME_IS_SPACE); + BIND_BITFIELD_FLAG(GRAPHEME_IS_BREAK_HARD); + BIND_BITFIELD_FLAG(GRAPHEME_IS_BREAK_SOFT); + BIND_BITFIELD_FLAG(GRAPHEME_IS_TAB); + BIND_BITFIELD_FLAG(GRAPHEME_IS_ELONGATION); + BIND_BITFIELD_FLAG(GRAPHEME_IS_PUNCTUATION); + BIND_BITFIELD_FLAG(GRAPHEME_IS_UNDERSCORE); + BIND_BITFIELD_FLAG(GRAPHEME_IS_CONNECTED); + BIND_BITFIELD_FLAG(GRAPHEME_IS_SAFE_TO_INSERT_TATWEEL); /* Hinting */ BIND_ENUM_CONSTANT(HINTING_NONE); @@ -545,6 +574,8 @@ void TextServer::_bind_methods() { BIND_ENUM_CONSTANT(FEATURE_FONT_VARIABLE); BIND_ENUM_CONSTANT(FEATURE_CONTEXT_SENSITIVE_CASE_CONVERSION); BIND_ENUM_CONSTANT(FEATURE_USE_SUPPORT_DATA); + BIND_ENUM_CONSTANT(FEATURE_UNICODE_IDENTIFIERS); + BIND_ENUM_CONSTANT(FEATURE_UNICODE_SECURITY); /* FT Contour Point Types */ BIND_ENUM_CONSTANT(CONTOUR_CURVE_TAG_ON); @@ -556,11 +587,12 @@ void TextServer::_bind_methods() { BIND_ENUM_CONSTANT(SPACING_SPACE); BIND_ENUM_CONSTANT(SPACING_TOP); BIND_ENUM_CONSTANT(SPACING_BOTTOM); + BIND_ENUM_CONSTANT(SPACING_MAX); /* Font Style */ - BIND_ENUM_CONSTANT(FONT_BOLD); - BIND_ENUM_CONSTANT(FONT_ITALIC); - BIND_ENUM_CONSTANT(FONT_FIXED_WIDTH); + BIND_BITFIELD_FLAG(FONT_BOLD); + BIND_BITFIELD_FLAG(FONT_ITALIC); + BIND_BITFIELD_FLAG(FONT_FIXED_WIDTH); /* Structured text parser */ BIND_ENUM_CONSTANT(STRUCTURED_TEXT_DEFAULT); @@ -568,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); } @@ -650,7 +682,7 @@ void TextServer::draw_hex_code_box(const RID &p_canvas, int64_t p_size, const Ve } } -PackedInt32Array TextServer::shaped_text_get_line_breaks_adv(const RID &p_shaped, const PackedFloat32Array &p_width, int64_t p_start, bool p_once, int64_t /*TextBreakFlag*/ p_break_flags) const { +PackedInt32Array TextServer::shaped_text_get_line_breaks_adv(const RID &p_shaped, const PackedFloat32Array &p_width, int64_t p_start, bool p_once, BitField<TextServer::LineBreakFlag> p_break_flags) const { PackedInt32Array lines; ERR_FAIL_COND_V(p_width.is_empty(), lines); @@ -660,24 +692,44 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks_adv(const RID &p_shaped real_t width = 0.f; int line_start = MAX(p_start, range.x); + int prev_safe_break = 0; int last_safe_break = -1; + int word_count = 0; int chunk = 0; + bool trim_next = false; int l_size = shaped_text_get_glyph_count(p_shaped); const Glyph *l_gl = const_cast<TextServer *>(this)->shaped_text_sort_logical(p_shaped); for (int i = 0; i < l_size; i++) { if (l_gl[i].start < p_start) { + prev_safe_break = i + 1; continue; } if (l_gl[i].count > 0) { if ((p_width[chunk] > 0) && (width + l_gl[i].advance > p_width[chunk]) && (last_safe_break >= 0)) { - lines.push_back(line_start); - lines.push_back(l_gl[last_safe_break].end); + if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) { + int start_pos = prev_safe_break; + int end_pos = last_safe_break; + while (trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + start_pos += l_gl[start_pos].count; + } + while ((start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + end_pos -= l_gl[end_pos].count; + } + lines.push_back(l_gl[start_pos].start); + lines.push_back(l_gl[end_pos].end); + trim_next = true; + } else { + lines.push_back(line_start); + lines.push_back(l_gl[last_safe_break].end); + } line_start = l_gl[last_safe_break].end; + prev_safe_break = last_safe_break + 1; i = last_safe_break; last_safe_break = -1; width = 0; + word_count = 0; chunk++; if (chunk >= p_width.size()) { chunk = 0; @@ -687,11 +739,26 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks_adv(const RID &p_shaped } continue; } - if ((p_break_flags & BREAK_MANDATORY) == BREAK_MANDATORY) { + if (p_break_flags.has_flag(BREAK_MANDATORY)) { if ((l_gl[i].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD) { - lines.push_back(line_start); - lines.push_back(l_gl[i].end); + if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) { + int start_pos = prev_safe_break; + int end_pos = i; + while (trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + start_pos += l_gl[start_pos].count; + } + while ((start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + end_pos -= l_gl[end_pos].count; + } + lines.push_back(l_gl[start_pos].start); + lines.push_back(l_gl[end_pos].end); + trim_next = false; + } else { + lines.push_back(line_start); + lines.push_back(l_gl[i].end); + } line_start = l_gl[i].end; + prev_safe_break = i + 1; last_safe_break = -1; width = 0; chunk = 0; @@ -701,12 +768,13 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks_adv(const RID &p_shaped continue; } } - if ((p_break_flags & BREAK_WORD_BOUND) == BREAK_WORD_BOUND) { + if (p_break_flags.has_flag(BREAK_WORD_BOUND)) { if ((l_gl[i].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT) { last_safe_break = i; + word_count++; } } - if ((p_break_flags & BREAK_GRAPHEME_BOUND) == BREAK_GRAPHEME_BOUND) { + if (p_break_flags.has_flag(BREAK_GRAPHEME_BOUND) && word_count == 0) { last_safe_break = i; } } @@ -714,8 +782,17 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks_adv(const RID &p_shaped } if (l_size > 0) { - if (lines.size() == 0 || lines[lines.size() - 1] < range.y) { - lines.push_back(line_start); + if (lines.size() == 0 || (lines[lines.size() - 1] < range.y && prev_safe_break < l_size)) { + if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) { + int start_pos = (prev_safe_break < l_size) ? prev_safe_break : l_size - 1; + int end_pos = l_size - 1; + while (trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + start_pos += l_gl[start_pos].count; + } + lines.push_back(l_gl[start_pos].start); + } else { + lines.push_back(line_start); + } lines.push_back(range.y); } } else { @@ -726,7 +803,7 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks_adv(const RID &p_shaped return lines; } -PackedInt32Array TextServer::shaped_text_get_line_breaks(const RID &p_shaped, double p_width, int64_t p_start, int64_t /*TextBreakFlag*/ p_break_flags) const { +PackedInt32Array TextServer::shaped_text_get_line_breaks(const RID &p_shaped, double p_width, int64_t p_start, BitField<TextServer::LineBreakFlag> p_break_flags) const { PackedInt32Array lines; const_cast<TextServer *>(this)->shaped_text_update_breaks(p_shaped); @@ -734,47 +811,80 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks(const RID &p_shaped, do double width = 0.f; int line_start = MAX(p_start, range.x); + int prev_safe_break = 0; int last_safe_break = -1; int word_count = 0; + bool trim_next = false; int l_size = shaped_text_get_glyph_count(p_shaped); const Glyph *l_gl = const_cast<TextServer *>(this)->shaped_text_sort_logical(p_shaped); for (int i = 0; i < l_size; i++) { if (l_gl[i].start < p_start) { + prev_safe_break = i + 1; continue; } if (l_gl[i].count > 0) { if ((p_width > 0) && (width + l_gl[i].advance * l_gl[i].repeat > p_width) && (last_safe_break >= 0)) { - lines.push_back(line_start); - lines.push_back(l_gl[last_safe_break].end); + if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) { + int start_pos = prev_safe_break; + int end_pos = last_safe_break; + while (trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + start_pos += l_gl[start_pos].count; + } + while ((start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + end_pos -= l_gl[end_pos].count; + } + lines.push_back(l_gl[start_pos].start); + lines.push_back(l_gl[end_pos].end); + trim_next = true; + } else { + lines.push_back(line_start); + lines.push_back(l_gl[last_safe_break].end); + } line_start = l_gl[last_safe_break].end; + prev_safe_break = last_safe_break + 1; i = last_safe_break; last_safe_break = -1; width = 0; word_count = 0; continue; } - if ((p_break_flags & BREAK_MANDATORY) == BREAK_MANDATORY) { + if (p_break_flags.has_flag(BREAK_MANDATORY)) { if ((l_gl[i].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD) { - lines.push_back(line_start); - lines.push_back(l_gl[i].end); + if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) { + int start_pos = prev_safe_break; + int end_pos = i; + while (trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + start_pos += l_gl[start_pos].count; + } + while ((start_pos < end_pos) && ((l_gl[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + end_pos -= l_gl[end_pos].count; + } + trim_next = false; + lines.push_back(l_gl[start_pos].start); + lines.push_back(l_gl[end_pos].end); + } else { + lines.push_back(line_start); + lines.push_back(l_gl[i].end); + } line_start = l_gl[i].end; + prev_safe_break = i + 1; last_safe_break = -1; width = 0; continue; } } - if ((p_break_flags & BREAK_WORD_BOUND) == BREAK_WORD_BOUND) { + if (p_break_flags.has_flag(BREAK_WORD_BOUND)) { if ((l_gl[i].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT) { last_safe_break = i; word_count++; } + if (p_break_flags.has_flag(BREAK_ADAPTIVE) && word_count == 0) { + last_safe_break = i; + } } - if (((p_break_flags & BREAK_WORD_BOUND_ADAPTIVE) == BREAK_WORD_BOUND_ADAPTIVE) && word_count == 0) { - last_safe_break = i; - } - if ((p_break_flags & BREAK_GRAPHEME_BOUND) == BREAK_GRAPHEME_BOUND) { + if (p_break_flags.has_flag(BREAK_GRAPHEME_BOUND)) { last_safe_break = i; } } @@ -782,8 +892,17 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks(const RID &p_shaped, do } if (l_size > 0) { - if (lines.size() == 0 || lines[lines.size() - 1] < range.y) { - lines.push_back(line_start); + if (lines.size() == 0 || (lines[lines.size() - 1] < range.y && prev_safe_break < l_size)) { + if (p_break_flags.has_flag(BREAK_TRIM_EDGE_SPACES)) { + int start_pos = (prev_safe_break < l_size) ? prev_safe_break : l_size - 1; + int end_pos = l_size - 1; + while (trim_next && (start_pos < end_pos) && ((l_gl[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (l_gl[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) { + start_pos += l_gl[start_pos].count; + } + lines.push_back(l_gl[start_pos].start); + } else { + lines.push_back(line_start); + } lines.push_back(range.y); } } else { @@ -794,7 +913,7 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks(const RID &p_shaped, do return lines; } -PackedInt32Array TextServer::shaped_text_get_word_breaks(const RID &p_shaped, int64_t p_grapheme_flags) const { +PackedInt32Array TextServer::shaped_text_get_word_breaks(const RID &p_shaped, BitField<TextServer::GraphemeFlag> p_grapheme_flags) const { PackedInt32Array words; const_cast<TextServer *>(this)->shaped_text_update_justification_ops(p_shaped); @@ -847,6 +966,11 @@ CaretInfo TextServer::shaped_text_get_carets(const RID &p_shaped, int64_t p_posi if (glyphs[i].count > 0) { // Caret before grapheme (top / left). if (p_position == glyphs[i].start && ((glyphs[i].flags & GRAPHEME_IS_VIRTUAL) != GRAPHEME_IS_VIRTUAL)) { + real_t advance = 0.f; + for (int j = 0; j < glyphs[i].count; j++) { + advance += glyphs[i + j].advance * glyphs[i + j].repeat; + } + real_t char_adv = advance / (real_t)(glyphs[i].end - glyphs[i].start); Rect2 cr; if (orientation == ORIENTATION_HORIZONTAL) { if (glyphs[i].start == range.x) { @@ -858,15 +982,11 @@ CaretInfo TextServer::shaped_text_get_carets(const RID &p_shaped, int64_t p_posi cr.position.x = off; if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) { caret.t_dir = DIRECTION_RTL; - for (int j = 0; j < glyphs[i].count; j++) { - cr.position.x += glyphs[i + j].advance * glyphs[i + j].repeat; - cr.size.x -= glyphs[i + j].advance * glyphs[i + j].repeat; - } + cr.position.x += advance; + cr.size.x = -char_adv; } else { caret.t_dir = DIRECTION_LTR; - for (int j = 0; j < glyphs[i].count; j++) { - cr.size.x += glyphs[i + j].advance * glyphs[i + j].repeat; - } + cr.size.x = char_adv; } } else { if (glyphs[i].start == range.x) { @@ -878,21 +998,22 @@ CaretInfo TextServer::shaped_text_get_carets(const RID &p_shaped, int64_t p_posi cr.position.y = off; if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) { caret.t_dir = DIRECTION_RTL; - for (int j = 0; j < glyphs[i].count; j++) { - cr.position.y += glyphs[i + j].advance * glyphs[i + j].repeat; - cr.size.y -= glyphs[i + j].advance * glyphs[i + j].repeat; - } + cr.position.y += advance; + cr.size.y = -char_adv; } else { caret.t_dir = DIRECTION_LTR; - for (int j = 0; j < glyphs[i].count; j++) { - cr.size.y += glyphs[i + j].advance * glyphs[i + j].repeat; - } + cr.size.y = char_adv; } } caret.t_caret = cr; } // Caret after grapheme (bottom / right). if (p_position == glyphs[i].end && ((glyphs[i].flags & GRAPHEME_IS_VIRTUAL) != GRAPHEME_IS_VIRTUAL)) { + real_t advance = 0.f; + for (int j = 0; j < glyphs[i].count; j++) { + advance += glyphs[i + j].advance * glyphs[i + j].repeat; + } + real_t char_adv = advance / (real_t)(glyphs[i].end - glyphs[i].start); Rect2 cr; if (orientation == ORIENTATION_HORIZONTAL) { if (glyphs[i].end == range.y) { @@ -905,15 +1026,11 @@ CaretInfo TextServer::shaped_text_get_carets(const RID &p_shaped, int64_t p_posi cr.position.x = off; if ((glyphs[i].flags & GRAPHEME_IS_RTL) != GRAPHEME_IS_RTL) { caret.l_dir = DIRECTION_LTR; - for (int j = 0; j < glyphs[i].count; j++) { - cr.position.x += glyphs[i + j].advance * glyphs[i + j].repeat; - cr.size.x -= glyphs[i + j].advance * glyphs[i + j].repeat; - } + cr.position.x += advance; + cr.size.x = -char_adv; } else { caret.l_dir = DIRECTION_RTL; - for (int j = 0; j < glyphs[i].count; j++) { - cr.size.x += glyphs[i + j].advance * glyphs[i + j].repeat; - } + cr.size.x = char_adv; } } else { cr.size.y = 1.0f; @@ -927,15 +1044,12 @@ CaretInfo TextServer::shaped_text_get_carets(const RID &p_shaped, int64_t p_posi cr.position.y = off; if ((glyphs[i].flags & GRAPHEME_IS_RTL) != GRAPHEME_IS_RTL) { caret.l_dir = DIRECTION_LTR; - for (int j = 0; j < glyphs[i].count; j++) { - cr.position.y += glyphs[i + j].advance * glyphs[i + j].repeat; - cr.size.y -= glyphs[i + j].advance * glyphs[i + j].repeat; - } + cr.position.y += advance; + cr.size.y = -char_adv; } else { caret.l_dir = DIRECTION_RTL; - for (int j = 0; j < glyphs[i].count; j++) { - cr.size.y += glyphs[i + j].advance * glyphs[i + j].repeat; - } + cr.position.x += advance; + cr.size.y = char_adv; } } caret.l_caret = cr; @@ -949,22 +1063,24 @@ CaretInfo TextServer::shaped_text_get_carets(const RID &p_shaped, int64_t p_posi real_t char_adv = advance / (real_t)(glyphs[i].end - glyphs[i].start); Rect2 cr; if (orientation == ORIENTATION_HORIZONTAL) { - cr.size.x = 1.0f; cr.size.y = height * 2; cr.position.y = -ascent; if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) { cr.position.x = off + char_adv * (glyphs[i].end - p_position); + cr.size.x = -char_adv; } else { cr.position.x = off + char_adv * (p_position - glyphs[i].start); + cr.size.x = char_adv; } } else { - cr.size.y = 1.0f; cr.size.x = height * 2; cr.position.x = -ascent; if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) { cr.position.y = off + char_adv * (glyphs[i].end - p_position); + cr.size.y = -char_adv; } else { cr.position.y = off + char_adv * (p_position - glyphs[i].start); + cr.size.y = char_adv; } } caret.t_caret = cr; @@ -1577,22 +1693,22 @@ String TextServer::strip_diacritics(const String &p_string) const { return result; } -Array TextServer::parse_structured_text(StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const { - Array 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: { @@ -1600,14 +1716,14 @@ Array TextServer::parse_structured_text(StructuredTextParser p_parser_type, cons 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: { @@ -1616,19 +1732,19 @@ Array TextServer::parse_structured_text(StructuredTextParser p_parser_type, cons 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. + } 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: { @@ -1637,25 +1753,104 @@ Array TextServer::parse_structured_text(StructuredTextParser p_parser_type, cons 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; } -Array TextServer::_shaped_text_get_glyphs_wrapper(const RID &p_shaped) const { - Array ret; +TypedArray<Dictionary> TextServer::_shaped_text_get_glyphs_wrapper(const RID &p_shaped) const { + TypedArray<Dictionary> ret; const Glyph *glyphs = shaped_text_get_glyphs(p_shaped); int gl_size = shaped_text_get_glyph_count(p_shaped); @@ -1679,7 +1874,7 @@ Array TextServer::_shaped_text_get_glyphs_wrapper(const RID &p_shaped) const { return ret; } -Array TextServer::_shaped_text_sort_logical_wrapper(const RID &p_shaped) { +TypedArray<Dictionary> TextServer::_shaped_text_sort_logical_wrapper(const RID &p_shaped) { Array ret; const Glyph *glyphs = shaped_text_sort_logical(p_shaped); @@ -1704,8 +1899,8 @@ Array TextServer::_shaped_text_sort_logical_wrapper(const RID &p_shaped) { return ret; } -Array TextServer::_shaped_text_get_ellipsis_glyphs_wrapper(const RID &p_shaped) const { - Array ret; +TypedArray<Dictionary> TextServer::_shaped_text_get_ellipsis_glyphs_wrapper(const RID &p_shaped) const { + TypedArray<Dictionary> ret; const Glyph *glyphs = shaped_text_get_ellipsis_glyphs(p_shaped); int gl_size = shaped_text_get_ellipsis_glyph_count(p_shaped); @@ -1729,6 +1924,26 @@ Array TextServer::_shaped_text_get_ellipsis_glyphs_wrapper(const RID &p_shaped) return ret; } +bool TextServer::is_valid_identifier(const String &p_string) const { + const char32_t *str = p_string.ptr(); + int len = p_string.length(); + + if (len == 0) { + return false; // Empty string. + } + + if (!is_unicode_identifier_start(str[0])) { + return false; + } + + for (int i = 1; i < len; i++) { + if (!is_unicode_identifier_continue(str[i])) { + return false; + } + } + return true; +} + TextServer::TextServer() { _init_diacritics_map(); } |