diff options
Diffstat (limited to 'servers/text_server.cpp')
-rw-r--r-- | servers/text_server.cpp | 1285 |
1 files changed, 784 insertions, 501 deletions
diff --git a/servers/text_server.cpp b/servers/text_server.cpp index 2f343e8f80..2bf0837d88 100644 --- a/servers/text_server.cpp +++ b/servers/text_server.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -29,125 +29,107 @@ /*************************************************************************/ #include "servers/text_server.h" -#include "scene/main/canvas_item.h" +#include "servers/rendering_server.h" TextServerManager *TextServerManager::singleton = nullptr; -TextServer *TextServerManager::server = nullptr; -TextServerManager::TextServerCreate TextServerManager::server_create_functions[TextServerManager::MAX_SERVERS]; -int TextServerManager::server_create_count = 0; void TextServerManager::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_interface_count"), &TextServerManager::_get_interface_count); - ClassDB::bind_method(D_METHOD("get_interface_name", "index"), &TextServerManager::_get_interface_name); - ClassDB::bind_method(D_METHOD("get_interface_features", "index"), &TextServerManager::_get_interface_features); - ClassDB::bind_method(D_METHOD("get_interface", "index"), &TextServerManager::_get_interface); - ClassDB::bind_method(D_METHOD("get_interfaces"), &TextServerManager::_get_interfaces); - ClassDB::bind_method(D_METHOD("find_interface", "name"), &TextServerManager::_find_interface); - - ClassDB::bind_method(D_METHOD("set_primary_interface", "index"), &TextServerManager::_set_primary_interface); - ClassDB::bind_method(D_METHOD("get_primary_interface"), &TextServerManager::_get_primary_interface); + ClassDB::bind_method(D_METHOD("add_interface", "interface"), &TextServerManager::add_interface); + ClassDB::bind_method(D_METHOD("get_interface_count"), &TextServerManager::get_interface_count); + ClassDB::bind_method(D_METHOD("remove_interface", "interface"), &TextServerManager::remove_interface); + ClassDB::bind_method(D_METHOD("get_interface", "idx"), &TextServerManager::get_interface); + ClassDB::bind_method(D_METHOD("get_interfaces"), &TextServerManager::get_interfaces); + ClassDB::bind_method(D_METHOD("find_interface", "name"), &TextServerManager::find_interface); + + ClassDB::bind_method(D_METHOD("set_primary_interface", "index"), &TextServerManager::set_primary_interface); + ClassDB::bind_method(D_METHOD("get_primary_interface"), &TextServerManager::get_primary_interface); + + ADD_SIGNAL(MethodInfo("interface_added", PropertyInfo(Variant::STRING_NAME, "interface_name"))); + ADD_SIGNAL(MethodInfo("interface_removed", PropertyInfo(Variant::STRING_NAME, "interface_name"))); } -void TextServerManager::register_create_function(const String &p_name, uint32_t p_features, TextServerManager::CreateFunction p_function, void *p_user_data) { - ERR_FAIL_COND(server_create_count == MAX_SERVERS); - server_create_functions[server_create_count].name = p_name; - server_create_functions[server_create_count].create_function = p_function; - server_create_functions[server_create_count].user_data = p_user_data; - server_create_functions[server_create_count].features = p_features; - server_create_count++; -} - -int TextServerManager::get_interface_count() { - return server_create_count; -} +void TextServerManager::add_interface(const Ref<TextServer> &p_interface) { + ERR_FAIL_COND(p_interface.is_null()); -String TextServerManager::get_interface_name(int p_index) { - ERR_FAIL_INDEX_V(p_index, server_create_count, String()); - return server_create_functions[p_index].name; -} + for (int i = 0; i < interfaces.size(); i++) { + if (interfaces[i] == p_interface) { + ERR_PRINT("TextServer: Interface was already added."); + return; + }; + }; -uint32_t TextServerManager::get_interface_features(int p_index) { - ERR_FAIL_INDEX_V(p_index, server_create_count, 0); - return server_create_functions[p_index].features; + interfaces.push_back(p_interface); + print_verbose("TextServer: Added interface \"" + p_interface->get_name() + "\""); + emit_signal(SNAME("interface_added"), p_interface->get_name()); } -TextServer *TextServerManager::initialize(int p_index, Error &r_error) { - ERR_FAIL_INDEX_V(p_index, server_create_count, nullptr); - if (server_create_functions[p_index].instance == nullptr) { - server_create_functions[p_index].instance = server_create_functions[p_index].create_function(r_error, server_create_functions[p_index].user_data); - if (server_create_functions[p_index].instance != nullptr) { - server_create_functions[p_index].instance->load_support_data(""); // Try loading default data. - } - } - if (server_create_functions[p_index].instance != nullptr) { - server = server_create_functions[p_index].instance; - if (OS::get_singleton()->get_main_loop()) { - OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TEXT_SERVER_CHANGED); - } - } - return server_create_functions[p_index].instance; -} +void TextServerManager::remove_interface(const Ref<TextServer> &p_interface) { + ERR_FAIL_COND(p_interface.is_null()); + ERR_FAIL_COND_MSG(p_interface == primary_interface, "TextServer: Can't remove primary interface."); -TextServer *TextServerManager::get_primary_interface() { - return server; -} + int idx = -1; + for (int i = 0; i < interfaces.size(); i++) { + if (interfaces[i] == p_interface) { + idx = i; + break; + }; + }; -int TextServerManager::_get_interface_count() const { - return server_create_count; + ERR_FAIL_COND_MSG(idx == -1, "Interface not found."); + print_verbose("TextServer: Removed interface \"" + p_interface->get_name() + "\""); + emit_signal(SNAME("interface_removed"), p_interface->get_name()); + interfaces.remove_at(idx); } -String TextServerManager::_get_interface_name(int p_index) const { - return get_interface_name(p_index); +int TextServerManager::get_interface_count() const { + return interfaces.size(); } -uint32_t TextServerManager::_get_interface_features(int p_index) const { - return get_interface_features(p_index); +Ref<TextServer> TextServerManager::get_interface(int p_index) const { + ERR_FAIL_INDEX_V(p_index, interfaces.size(), nullptr); + return interfaces[p_index]; } -TextServer *TextServerManager::_get_interface(int p_index) const { - ERR_FAIL_INDEX_V(p_index, server_create_count, nullptr); - if (server_create_functions[p_index].instance == nullptr) { - Error error; - server_create_functions[p_index].instance = server_create_functions[p_index].create_function(error, server_create_functions[p_index].user_data); - if (server_create_functions[p_index].instance != nullptr) { - server_create_functions[p_index].instance->load_support_data(""); // Try loading default data. - } - } - return server_create_functions[p_index].instance; -} +Ref<TextServer> TextServerManager::find_interface(const String &p_name) const { + int idx = -1; + for (int i = 0; i < interfaces.size(); i++) { + if (interfaces[i]->get_name() == p_name) { + idx = i; + break; + }; + }; -TextServer *TextServerManager::_find_interface(const String &p_name) const { - for (int i = 0; i < server_create_count; i++) { - if (server_create_functions[i].name == p_name) { - return _get_interface(i); - } - } - return nullptr; + ERR_FAIL_COND_V_MSG(idx == -1, nullptr, "Interface not found."); + return interfaces[idx]; } -Array TextServerManager::_get_interfaces() const { +Array TextServerManager::get_interfaces() const { Array ret; - for (int i = 0; i < server_create_count; i++) { + for (int i = 0; i < interfaces.size(); i++) { Dictionary iface_info; iface_info["id"] = i; - iface_info["name"] = server_create_functions[i].name; + iface_info["name"] = interfaces[i]->get_name(); ret.push_back(iface_info); }; return ret; -}; - -bool TextServerManager::_set_primary_interface(int p_index) { - Error error; - TextServerManager::initialize(p_index, error); - return (error == OK); } -TextServer *TextServerManager::_get_primary_interface() const { - return server; +void TextServerManager::set_primary_interface(const Ref<TextServer> &p_primary_interface) { + if (p_primary_interface.is_null()) { + print_verbose("TextServer: Clearing primary interface"); + primary_interface.unref(); + } else { + primary_interface = p_primary_interface; + print_verbose("TextServer: Primary interface set to: \"" + primary_interface->get_name() + "\"."); + + if (OS::get_singleton()->get_main_loop()) { + OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TEXT_SERVER_CHANGED); + } + } } TextServerManager::TextServerManager() { @@ -155,29 +137,29 @@ TextServerManager::TextServerManager() { } TextServerManager::~TextServerManager() { - singleton = nullptr; - for (int i = 0; i < server_create_count; i++) { - if (server_create_functions[i].instance != nullptr) { - memdelete(server_create_functions[i].instance); - server_create_functions[i].instance = nullptr; - } + if (primary_interface.is_valid()) { + primary_interface.unref(); + } + while (interfaces.size() > 0) { + interfaces.remove_at(0); } + singleton = nullptr; } /*************************************************************************/ -bool TextServer::Glyph::operator==(const Glyph &p_a) const { +bool Glyph::operator==(const Glyph &p_a) const { return (p_a.index == index) && (p_a.font_rid == font_rid) && (p_a.font_size == font_size) && (p_a.start == start); } -bool TextServer::Glyph::operator!=(const Glyph &p_a) const { +bool Glyph::operator!=(const Glyph &p_a) const { return (p_a.index != index) || (p_a.font_rid != font_rid) || (p_a.font_size != font_size) || (p_a.start != start); } -bool TextServer::Glyph::operator<(const Glyph &p_a) const { +bool Glyph::operator<(const Glyph &p_a) const { if (p_a.start == start) { if (p_a.count == count) { - if ((p_a.flags & GRAPHEME_IS_VIRTUAL) == GRAPHEME_IS_VIRTUAL) { + if ((p_a.flags & TextServer::GRAPHEME_IS_VIRTUAL) == TextServer::GRAPHEME_IS_VIRTUAL) { return true; } else { return false; @@ -188,10 +170,10 @@ bool TextServer::Glyph::operator<(const Glyph &p_a) const { return p_a.start < start; } -bool TextServer::Glyph::operator>(const Glyph &p_a) const { +bool Glyph::operator>(const Glyph &p_a) const { if (p_a.start == start) { if (p_a.count == count) { - if ((p_a.flags & GRAPHEME_IS_VIRTUAL) == GRAPHEME_IS_VIRTUAL) { + if ((p_a.flags & TextServer::GRAPHEME_IS_VIRTUAL) == TextServer::GRAPHEME_IS_VIRTUAL) { return false; } else { return true; @@ -205,25 +187,47 @@ bool TextServer::Glyph::operator>(const Glyph &p_a) const { void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("has_feature", "feature"), &TextServer::has_feature); ClassDB::bind_method(D_METHOD("get_name"), &TextServer::get_name); + ClassDB::bind_method(D_METHOD("get_features"), &TextServer::get_features); ClassDB::bind_method(D_METHOD("load_support_data", "filename"), &TextServer::load_support_data); + ClassDB::bind_method(D_METHOD("get_support_data_filename"), &TextServer::get_support_data_filename); + ClassDB::bind_method(D_METHOD("get_support_data_info"), &TextServer::get_support_data_info); + ClassDB::bind_method(D_METHOD("save_support_data", "filename"), &TextServer::save_support_data); + ClassDB::bind_method(D_METHOD("is_locale_right_to_left", "locale"), &TextServer::is_locale_right_to_left); ClassDB::bind_method(D_METHOD("name_to_tag", "name"), &TextServer::name_to_tag); ClassDB::bind_method(D_METHOD("tag_to_name", "tag"), &TextServer::tag_to_name); ClassDB::bind_method(D_METHOD("has", "rid"), &TextServer::has); - ClassDB::bind_method(D_METHOD("free_rid", "rid"), &TextServer::free); // shouldn't conflict with Object::free() + ClassDB::bind_method(D_METHOD("free_rid", "rid"), &TextServer::free_rid); /* Font Interface */ ClassDB::bind_method(D_METHOD("create_font"), &TextServer::create_font); - ClassDB::bind_method(D_METHOD("font_set_data", "data"), &TextServer::font_set_data); + ClassDB::bind_method(D_METHOD("font_set_data", "font_rid", "data"), &TextServer::font_set_data); + + ClassDB::bind_method(D_METHOD("font_set_face_index", "font_rid", "face_index"), &TextServer::font_set_face_index); + ClassDB::bind_method(D_METHOD("font_get_face_index", "font_rid"), &TextServer::font_get_face_index); + + ClassDB::bind_method(D_METHOD("font_get_face_count", "font_rid"), &TextServer::font_get_face_count); + + ClassDB::bind_method(D_METHOD("font_set_style", "font_rid", "style"), &TextServer::font_set_style); + ClassDB::bind_method(D_METHOD("font_get_style", "font_rid"), &TextServer::font_get_style); + + ClassDB::bind_method(D_METHOD("font_set_name", "font_rid", "name"), &TextServer::font_set_name); + ClassDB::bind_method(D_METHOD("font_get_name", "font_rid"), &TextServer::font_get_name); + + 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_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); + ClassDB::bind_method(D_METHOD("font_set_multichannel_signed_distance_field", "font_rid", "msdf"), &TextServer::font_set_multichannel_signed_distance_field); ClassDB::bind_method(D_METHOD("font_is_multichannel_signed_distance_field", "font_rid"), &TextServer::font_is_multichannel_signed_distance_field); @@ -239,9 +243,18 @@ void TextServer::_bind_methods() { 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); - ClassDB::bind_method(D_METHOD("font_set_hinting", "font_rid", "_hinting"), &TextServer::font_set_hinting); + ClassDB::bind_method(D_METHOD("font_set_hinting", "font_rid", "hinting"), &TextServer::font_set_hinting); ClassDB::bind_method(D_METHOD("font_get_hinting", "font_rid"), &TextServer::font_get_hinting); + ClassDB::bind_method(D_METHOD("font_set_subpixel_positioning", "font_rid", "subpixel_positioning"), &TextServer::font_set_subpixel_positioning); + ClassDB::bind_method(D_METHOD("font_get_subpixel_positioning", "font_rid"), &TextServer::font_get_subpixel_positioning); + + ClassDB::bind_method(D_METHOD("font_set_embolden", "font_rid", "strength"), &TextServer::font_set_embolden); + ClassDB::bind_method(D_METHOD("font_get_embolden", "font_rid"), &TextServer::font_get_embolden); + + ClassDB::bind_method(D_METHOD("font_set_transform", "font_rid", "transform"), &TextServer::font_set_transform); + ClassDB::bind_method(D_METHOD("font_get_transform", "font_rid"), &TextServer::font_get_transform); + ClassDB::bind_method(D_METHOD("font_set_variation_coordinates", "font_rid", "variation_coordinates"), &TextServer::font_set_variation_coordinates); ClassDB::bind_method(D_METHOD("font_get_variation_coordinates", "font_rid"), &TextServer::font_get_variation_coordinates); @@ -267,9 +280,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); @@ -299,7 +309,10 @@ void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("font_get_glyph_texture_idx", "font_rid", "size", "glyph"), &TextServer::font_get_glyph_texture_idx); ClassDB::bind_method(D_METHOD("font_set_glyph_texture_idx", "font_rid", "size", "glyph", "texture_idx"), &TextServer::font_set_glyph_texture_idx); - ClassDB::bind_method(D_METHOD("font_get_glyph_contours", "font", "size", "index"), &TextServer::_font_get_glyph_contours); + ClassDB::bind_method(D_METHOD("font_get_glyph_texture_rid", "font_rid", "size", "glyph"), &TextServer::font_get_glyph_texture_rid); + ClassDB::bind_method(D_METHOD("font_get_glyph_texture_size", "font_rid", "size", "glyph"), &TextServer::font_get_glyph_texture_size); + + ClassDB::bind_method(D_METHOD("font_get_glyph_contours", "font", "size", "index"), &TextServer::font_get_glyph_contours); ClassDB::bind_method(D_METHOD("font_get_kerning_list", "font_rid", "size"), &TextServer::font_get_kerning_list); ClassDB::bind_method(D_METHOD("font_clear_kerning_map", "font_rid", "size"), &TextServer::font_clear_kerning_map); @@ -331,6 +344,9 @@ void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("font_remove_script_support_override", "font_rid", "script"), &TextServer::font_remove_script_support_override); ClassDB::bind_method(D_METHOD("font_get_script_support_overrides", "font_rid"), &TextServer::font_get_script_support_overrides); + ClassDB::bind_method(D_METHOD("font_set_opentype_feature_overrides", "font_rid", "overrides"), &TextServer::font_set_opentype_feature_overrides); + ClassDB::bind_method(D_METHOD("font_get_opentype_feature_overrides", "font_rid"), &TextServer::font_get_opentype_feature_overrides); + ClassDB::bind_method(D_METHOD("font_supported_feature_list", "font_rid"), &TextServer::font_supported_feature_list); ClassDB::bind_method(D_METHOD("font_supported_variation_list", "font_rid"), &TextServer::font_supported_variation_list); @@ -348,8 +364,12 @@ void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("shaped_text_set_direction", "shaped", "direction"), &TextServer::shaped_text_set_direction, DEFVAL(DIRECTION_AUTO)); ClassDB::bind_method(D_METHOD("shaped_text_get_direction", "shaped"), &TextServer::shaped_text_get_direction); + ClassDB::bind_method(D_METHOD("shaped_text_get_inferred_direction", "shaped"), &TextServer::shaped_text_get_inferred_direction); - ClassDB::bind_method(D_METHOD("shaped_text_set_bidi_override", "shaped", "override"), &TextServer::_shaped_text_set_bidi_override); + ClassDB::bind_method(D_METHOD("shaped_text_set_bidi_override", "shaped", "override"), &TextServer::shaped_text_set_bidi_override); + + ClassDB::bind_method(D_METHOD("shaped_text_set_custom_punctuation", "shaped", "punct"), &TextServer::shaped_text_set_custom_punctuation); + ClassDB::bind_method(D_METHOD("shaped_text_get_custom_punctuation", "shaped"), &TextServer::shaped_text_get_custom_punctuation); ClassDB::bind_method(D_METHOD("shaped_text_set_orientation", "shaped", "orientation"), &TextServer::shaped_text_set_orientation, DEFVAL(ORIENTATION_HORIZONTAL)); ClassDB::bind_method(D_METHOD("shaped_text_get_orientation", "shaped"), &TextServer::shaped_text_get_orientation); @@ -360,9 +380,16 @@ void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("shaped_text_set_preserve_control", "shaped", "enabled"), &TextServer::shaped_text_set_preserve_control); ClassDB::bind_method(D_METHOD("shaped_text_get_preserve_control", "shaped"), &TextServer::shaped_text_get_preserve_control); - ClassDB::bind_method(D_METHOD("shaped_text_add_string", "shaped", "text", "fonts", "size", "opentype_features", "language"), &TextServer::shaped_text_add_string, DEFVAL(Dictionary()), DEFVAL("")); - ClassDB::bind_method(D_METHOD("shaped_text_add_object", "shaped", "key", "size", "inline_align", "length"), &TextServer::shaped_text_add_object, DEFVAL(INLINE_ALIGN_CENTER), DEFVAL(1)); - ClassDB::bind_method(D_METHOD("shaped_text_resize_object", "shaped", "key", "size", "inline_align"), &TextServer::shaped_text_resize_object, DEFVAL(INLINE_ALIGN_CENTER)); + 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_get_span_count", "shaped"), &TextServer::shaped_get_span_count); + ClassDB::bind_method(D_METHOD("shaped_get_span_meta", "shaped", "index"), &TextServer::shaped_get_span_meta); + ClassDB::bind_method(D_METHOD("shaped_set_span_update_font", "shaped", "index", "fonts", "size", "opentype_features"), &TextServer::shaped_set_span_update_font, DEFVAL(Dictionary())); ClassDB::bind_method(D_METHOD("shaped_text_substr", "shaped", "start", "length"), &TextServer::shaped_text_substr); ClassDB::bind_method(D_METHOD("shaped_text_get_parent", "shaped"), &TextServer::shaped_text_get_parent); @@ -372,14 +399,21 @@ void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("shaped_text_shape", "shaped"), &TextServer::shaped_text_shape); ClassDB::bind_method(D_METHOD("shaped_text_is_ready", "shaped"), &TextServer::shaped_text_is_ready); - ClassDB::bind_method(D_METHOD("shaped_text_get_glyphs", "shaped"), &TextServer::_shaped_text_get_glyphs); + ClassDB::bind_method(D_METHOD("shaped_text_get_glyphs", "shaped"), &TextServer::_shaped_text_get_glyphs_wrapper); + ClassDB::bind_method(D_METHOD("shaped_text_sort_logical", "shaped"), &TextServer::_shaped_text_sort_logical_wrapper); + ClassDB::bind_method(D_METHOD("shaped_text_get_glyph_count", "shaped"), &TextServer::shaped_text_get_glyph_count); ClassDB::bind_method(D_METHOD("shaped_text_get_range", "shaped"), &TextServer::shaped_text_get_range); - ClassDB::bind_method(D_METHOD("shaped_text_get_line_breaks_adv", "shaped", "width", "start", "once", "break_flags"), &TextServer::_shaped_text_get_line_breaks_adv, DEFVAL(0), DEFVAL(true), DEFVAL(BREAK_MANDATORY | BREAK_WORD_BOUND)); - ClassDB::bind_method(D_METHOD("shaped_text_get_line_breaks", "shaped", "width", "start", "break_flags"), &TextServer::_shaped_text_get_line_breaks, DEFVAL(0), DEFVAL(BREAK_MANDATORY | BREAK_WORD_BOUND)); - ClassDB::bind_method(D_METHOD("shaped_text_get_word_breaks", "shaped"), &TextServer::_shaped_text_get_word_breaks); + ClassDB::bind_method(D_METHOD("shaped_text_get_line_breaks_adv", "shaped", "width", "start", "once", "break_flags"), &TextServer::shaped_text_get_line_breaks_adv, DEFVAL(0), DEFVAL(true), DEFVAL(BREAK_MANDATORY | BREAK_WORD_BOUND)); + ClassDB::bind_method(D_METHOD("shaped_text_get_line_breaks", "shaped", "width", "start", "break_flags"), &TextServer::shaped_text_get_line_breaks, DEFVAL(0), DEFVAL(BREAK_MANDATORY | BREAK_WORD_BOUND)); + ClassDB::bind_method(D_METHOD("shaped_text_get_word_breaks", "shaped", "grapheme_flags"), &TextServer::shaped_text_get_word_breaks, DEFVAL(GRAPHEME_IS_SPACE | GRAPHEME_IS_PUNCTUATION)); + + ClassDB::bind_method(D_METHOD("shaped_text_get_trim_pos", "shaped"), &TextServer::shaped_text_get_trim_pos); + ClassDB::bind_method(D_METHOD("shaped_text_get_ellipsis_pos", "shaped"), &TextServer::shaped_text_get_ellipsis_pos); + ClassDB::bind_method(D_METHOD("shaped_text_get_ellipsis_glyphs", "shaped"), &TextServer::_shaped_text_get_ellipsis_glyphs_wrapper); + ClassDB::bind_method(D_METHOD("shaped_text_get_ellipsis_glyph_count", "shaped"), &TextServer::shaped_text_get_ellipsis_glyph_count); - ClassDB::bind_method(D_METHOD("shaped_text_overrun_trim_to_width", "shaped", "width", "overrun_trim_flags"), &TextServer::shaped_text_overrun_trim_to_width, DEFVAL(0), DEFVAL(OVERRUN_NO_TRIMMING)); + ClassDB::bind_method(D_METHOD("shaped_text_overrun_trim_to_width", "shaped", "width", "overrun_trim_flags"), &TextServer::shaped_text_overrun_trim_to_width, DEFVAL(0), DEFVAL(OVERRUN_NO_TRIM)); ClassDB::bind_method(D_METHOD("shaped_text_get_objects", "shaped"), &TextServer::shaped_text_get_objects); ClassDB::bind_method(D_METHOD("shaped_text_get_object_rect", "shaped", "key"), &TextServer::shaped_text_get_object_rect); @@ -391,24 +425,38 @@ void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("shaped_text_get_underline_position", "shaped"), &TextServer::shaped_text_get_underline_position); ClassDB::bind_method(D_METHOD("shaped_text_get_underline_thickness", "shaped"), &TextServer::shaped_text_get_underline_thickness); - ClassDB::bind_method(D_METHOD("shaped_text_get_carets", "shaped", "position"), &TextServer::_shaped_text_get_carets); - ClassDB::bind_method(D_METHOD("shaped_text_get_selection", "shaped", "start", "end"), &TextServer::_shaped_text_get_selection); + ClassDB::bind_method(D_METHOD("shaped_text_get_carets", "shaped", "position"), &TextServer::_shaped_text_get_carets_wrapper); + ClassDB::bind_method(D_METHOD("shaped_text_get_selection", "shaped", "start", "end"), &TextServer::shaped_text_get_selection); ClassDB::bind_method(D_METHOD("shaped_text_hit_test_grapheme", "shaped", "coords"), &TextServer::shaped_text_hit_test_grapheme); ClassDB::bind_method(D_METHOD("shaped_text_hit_test_position", "shaped", "coords"), &TextServer::shaped_text_hit_test_position); + ClassDB::bind_method(D_METHOD("shaped_text_get_grapheme_bounds", "shaped", "pos"), &TextServer::shaped_text_get_grapheme_bounds); ClassDB::bind_method(D_METHOD("shaped_text_next_grapheme_pos", "shaped", "pos"), &TextServer::shaped_text_next_grapheme_pos); ClassDB::bind_method(D_METHOD("shaped_text_prev_grapheme_pos", "shaped", "pos"), &TextServer::shaped_text_prev_grapheme_pos); ClassDB::bind_method(D_METHOD("shaped_text_draw", "shaped", "canvas", "pos", "clip_l", "clip_r", "color"), &TextServer::shaped_text_draw, DEFVAL(-1), DEFVAL(-1), DEFVAL(Color(1, 1, 1))); ClassDB::bind_method(D_METHOD("shaped_text_draw_outline", "shaped", "canvas", "pos", "clip_l", "clip_r", "outline_size", "color"), &TextServer::shaped_text_draw_outline, DEFVAL(-1), DEFVAL(-1), DEFVAL(1), DEFVAL(Color(1, 1, 1))); - ClassDB::bind_method(D_METHOD("shaped_text_get_dominant_direciton_in_range", "shaped", "start", "end"), &TextServer::shaped_text_get_dominant_direciton_in_range); + ClassDB::bind_method(D_METHOD("shaped_text_get_dominant_direction_in_range", "shaped", "start", "end"), &TextServer::shaped_text_get_dominant_direction_in_range); ClassDB::bind_method(D_METHOD("format_number", "number", "language"), &TextServer::format_number, DEFVAL("")); 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("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); + /* Direction */ BIND_ENUM_CONSTANT(DIRECTION_AUTO); BIND_ENUM_CONSTANT(DIRECTION_LTR); @@ -419,163 +467,170 @@ void TextServer::_bind_methods() { 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_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); + BIND_ENUM_CONSTANT(AUTOWRAP_ARBITRARY); + BIND_ENUM_CONSTANT(AUTOWRAP_WORD); + 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_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); + + /* VisibleCharactersBehavior */ + BIND_ENUM_CONSTANT(VC_CHARS_BEFORE_SHAPING); + BIND_ENUM_CONSTANT(VC_CHARS_AFTER_SHAPING); + BIND_ENUM_CONSTANT(VC_GLYPHS_AUTO); + BIND_ENUM_CONSTANT(VC_GLYPHS_LTR); + BIND_ENUM_CONSTANT(VC_GLYPHS_RTL); + + /* OverrunBehavior */ + BIND_ENUM_CONSTANT(OVERRUN_NO_TRIMMING); + BIND_ENUM_CONSTANT(OVERRUN_TRIM_CHAR); + BIND_ENUM_CONSTANT(OVERRUN_TRIM_WORD); + BIND_ENUM_CONSTANT(OVERRUN_TRIM_ELLIPSIS); + BIND_ENUM_CONSTANT(OVERRUN_TRIM_WORD_ELLIPSIS); /* TextOverrunFlag */ - BIND_ENUM_CONSTANT(OVERRUN_NO_TRIMMING); - 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_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_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_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); BIND_ENUM_CONSTANT(HINTING_LIGHT); BIND_ENUM_CONSTANT(HINTING_NORMAL); + /* SubpixelPositioning */ + BIND_ENUM_CONSTANT(SUBPIXEL_POSITIONING_DISABLED); + BIND_ENUM_CONSTANT(SUBPIXEL_POSITIONING_AUTO); + BIND_ENUM_CONSTANT(SUBPIXEL_POSITIONING_ONE_HALF); + BIND_ENUM_CONSTANT(SUBPIXEL_POSITIONING_ONE_QUARTER); + BIND_ENUM_CONSTANT(SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE); + BIND_ENUM_CONSTANT(SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE); + /* Feature */ + BIND_ENUM_CONSTANT(FEATURE_SIMPLE_LAYOUT); BIND_ENUM_CONSTANT(FEATURE_BIDI_LAYOUT); BIND_ENUM_CONSTANT(FEATURE_VERTICAL_LAYOUT); BIND_ENUM_CONSTANT(FEATURE_SHAPING); BIND_ENUM_CONSTANT(FEATURE_KASHIDA_JUSTIFICATION); BIND_ENUM_CONSTANT(FEATURE_BREAK_ITERATORS); + BIND_ENUM_CONSTANT(FEATURE_FONT_BITMAP); + BIND_ENUM_CONSTANT(FEATURE_FONT_DYNAMIC); + BIND_ENUM_CONSTANT(FEATURE_FONT_MSDF); BIND_ENUM_CONSTANT(FEATURE_FONT_SYSTEM); 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); BIND_ENUM_CONSTANT(CONTOUR_CURVE_TAG_OFF_CONIC); BIND_ENUM_CONSTANT(CONTOUR_CURVE_TAG_OFF_CUBIC); - /* Font Spacing*/ + /* Font Spacing */ BIND_ENUM_CONSTANT(SPACING_GLYPH); BIND_ENUM_CONSTANT(SPACING_SPACE); BIND_ENUM_CONSTANT(SPACING_TOP); BIND_ENUM_CONSTANT(SPACING_BOTTOM); + BIND_ENUM_CONSTANT(SPACING_MAX); + + /* Font Style */ + 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); + BIND_ENUM_CONSTANT(STRUCTURED_TEXT_URI); + 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_CUSTOM); } -Vector3 TextServer::hex_code_box_font_size[2] = { Vector3(5, 5, 1), Vector3(10, 10, 2) }; -Ref<CanvasTexture> TextServer::hex_code_box_font_tex[2] = { nullptr, nullptr }; - -void TextServer::initialize_hex_code_box_fonts() { - static unsigned int tamsyn5x9_png_len = 175; - static unsigned char tamsyn5x9_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, - 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x05, - 0x04, 0x03, 0x00, 0x00, 0x00, 0x20, 0x7c, 0x76, 0xda, 0x00, 0x00, 0x00, - 0x0f, 0x50, 0x4c, 0x54, 0x45, 0xfd, 0x07, 0x00, 0x00, 0x00, 0x00, 0x06, - 0x7e, 0x74, 0x00, 0x40, 0xc6, 0xff, 0xff, 0xff, 0x47, 0x9a, 0xd4, 0xc7, - 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, 0x4e, 0x53, 0x00, 0x40, 0xe6, 0xd8, - 0x66, 0x00, 0x00, 0x00, 0x4e, 0x49, 0x44, 0x41, 0x54, 0x08, 0x1d, 0x05, - 0xc1, 0x21, 0x01, 0x00, 0x00, 0x00, 0x83, 0x30, 0x04, 0xc1, 0x10, 0xef, - 0x9f, 0xe9, 0x1b, 0x86, 0x2c, 0x17, 0xb9, 0xcc, 0x65, 0x0c, 0x73, 0x38, - 0xc7, 0xe6, 0x22, 0x19, 0x88, 0x98, 0x10, 0x48, 0x4a, 0x29, 0x85, 0x14, - 0x02, 0x89, 0x10, 0xa3, 0x1c, 0x0b, 0x31, 0xd6, 0xe6, 0x08, 0x69, 0x39, - 0x48, 0x44, 0xa0, 0x0d, 0x4a, 0x22, 0xa1, 0x94, 0x42, 0x0a, 0x01, 0x63, - 0x6d, 0x0e, 0x72, 0x18, 0x61, 0x8c, 0x74, 0x38, 0xc7, 0x26, 0x1c, 0xf3, - 0x71, 0x16, 0x15, 0x27, 0x6a, 0xc2, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x49, - 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 - }; - - static unsigned int tamsyn10x20_png_len = 270; - static unsigned char tamsyn10x20_png[] = { - 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, - 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x0a, - 0x04, 0x03, 0x00, 0x00, 0x00, 0xc1, 0x66, 0x48, 0x96, 0x00, 0x00, 0x00, - 0x0f, 0x50, 0x4c, 0x54, 0x45, 0x00, 0x00, 0x00, 0xf9, 0x07, 0x00, 0x5d, - 0x71, 0xa5, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x49, 0xdb, 0xcb, 0x7f, - 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, 0x4e, 0x53, 0x00, 0x40, 0xe6, 0xd8, - 0x66, 0x00, 0x00, 0x00, 0xad, 0x49, 0x44, 0x41, 0x54, 0x28, 0xcf, 0xa5, - 0x92, 0x4b, 0x0e, 0x03, 0x31, 0x08, 0x43, 0xdf, 0x82, 0x83, 0x79, 0xe1, - 0xfb, 0x9f, 0xa9, 0x0b, 0x3e, 0x61, 0xa6, 0x1f, 0x55, 0xad, 0x14, 0x31, - 0x66, 0x42, 0x1c, 0x70, 0x0c, 0xb6, 0x00, 0x01, 0xb6, 0x08, 0xdb, 0x00, - 0x8d, 0xc2, 0x14, 0xb2, 0x55, 0xa1, 0xfe, 0x09, 0xc2, 0x26, 0xdc, 0x25, - 0x75, 0x22, 0x97, 0x1a, 0x25, 0x77, 0x28, 0x31, 0x02, 0x80, 0xc8, 0xdd, - 0x2c, 0x11, 0x1a, 0x54, 0x9f, 0xc8, 0xa2, 0x8a, 0x06, 0xa9, 0x93, 0x22, - 0xbd, 0xd4, 0xd0, 0x0c, 0xcf, 0x81, 0x2b, 0xca, 0xbb, 0x83, 0xe0, 0x10, - 0xe6, 0xad, 0xff, 0x10, 0x2a, 0x66, 0x34, 0x41, 0x58, 0x35, 0x54, 0x49, - 0x5a, 0x63, 0xa5, 0xc2, 0x87, 0xab, 0x52, 0x76, 0x9a, 0xba, 0xc6, 0xf4, - 0x75, 0x7a, 0x9e, 0x3c, 0x46, 0x86, 0x5c, 0xa3, 0xfd, 0x87, 0x0e, 0x75, - 0x08, 0x7b, 0xee, 0x7e, 0xea, 0x21, 0x5c, 0x4f, 0xf6, 0xc5, 0xc8, 0x4b, - 0xb9, 0x11, 0xf2, 0xd6, 0xe1, 0x8f, 0x84, 0x62, 0x7b, 0x67, 0xf9, 0x24, - 0xde, 0x6d, 0xbc, 0xb2, 0xcd, 0xb1, 0xf3, 0xf2, 0x2f, 0xe8, 0xe2, 0xe4, - 0xae, 0x4b, 0x4f, 0xcf, 0x2b, 0xdc, 0x8d, 0x0d, 0xf0, 0x00, 0x8f, 0x22, - 0x26, 0x65, 0x75, 0x8a, 0xe6, 0x84, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, - 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 - }; +Vector2 TextServer::get_hex_code_box_size(int64_t p_size, int64_t p_index) const { + int w = ((p_index <= 0xFF) ? 1 : ((p_index <= 0xFFFF) ? 2 : 3)); + int sp = MAX(0, w - 1); + int sz = MAX(1, Math::round(p_size / 15.f)); - if (RenderingServer::get_singleton() != nullptr) { - Vector<uint8_t> hex_box_data; - - Ref<Image> image; - image.instantiate(); - - Ref<ImageTexture> hex_code_image_tex[2]; - - hex_box_data.resize(tamsyn5x9_png_len); - memcpy(hex_box_data.ptrw(), tamsyn5x9_png, tamsyn5x9_png_len); - image->load_png_from_buffer(hex_box_data); - hex_code_image_tex[0].instantiate(); - hex_code_image_tex[0]->create_from_image(image); - hex_code_box_font_tex[0].instantiate(); - hex_code_box_font_tex[0]->set_diffuse_texture(hex_code_image_tex[0]); - hex_code_box_font_tex[0]->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST); - hex_box_data.clear(); - - hex_box_data.resize(tamsyn10x20_png_len); - memcpy(hex_box_data.ptrw(), tamsyn10x20_png, tamsyn10x20_png_len); - image->load_png_from_buffer(hex_box_data); - hex_code_image_tex[1].instantiate(); - hex_code_image_tex[1]->create_from_image(image); - hex_code_box_font_tex[1].instantiate(); - hex_code_box_font_tex[1]->set_diffuse_texture(hex_code_image_tex[1]); - hex_code_box_font_tex[1]->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST); - hex_box_data.clear(); - } + return Vector2(4 + 3 * w + sp + 1, 15) * sz; } -void TextServer::finish_hex_code_box_fonts() { - if (hex_code_box_font_tex[0].is_valid()) { - hex_code_box_font_tex[0].unref(); +void TextServer::_draw_hex_code_box_number(const RID &p_canvas, int64_t p_size, const Vector2 &p_pos, uint8_t p_index, const Color &p_color) const { + static uint8_t chars[] = { 0x7E, 0x30, 0x6D, 0x79, 0x33, 0x5B, 0x5F, 0x70, 0x7F, 0x7B, 0x77, 0x1F, 0x4E, 0x3D, 0x4F, 0x47, 0x00 }; + uint8_t x = chars[p_index]; + if (x & (1 << 6)) { + RenderingServer::get_singleton()->canvas_item_add_rect(p_canvas, Rect2(p_pos, Size2(3, 1) * p_size), p_color); + } + if (x & (1 << 5)) { + RenderingServer::get_singleton()->canvas_item_add_rect(p_canvas, Rect2(p_pos + Point2(2, 0) * p_size, Size2(1, 3) * p_size), p_color); + } + if (x & (1 << 4)) { + RenderingServer::get_singleton()->canvas_item_add_rect(p_canvas, Rect2(p_pos + Point2(2, 2) * p_size, Size2(1, 3) * p_size), p_color); + } + if (x & (1 << 3)) { + RenderingServer::get_singleton()->canvas_item_add_rect(p_canvas, Rect2(p_pos + Point2(0, 4) * p_size, Size2(3, 1) * p_size), p_color); } - if (hex_code_box_font_tex[1].is_valid()) { - hex_code_box_font_tex[1].unref(); + if (x & (1 << 2)) { + RenderingServer::get_singleton()->canvas_item_add_rect(p_canvas, Rect2(p_pos + Point2(0, 2) * p_size, Size2(1, 3) * p_size), p_color); + } + if (x & (1 << 1)) { + RenderingServer::get_singleton()->canvas_item_add_rect(p_canvas, Rect2(p_pos, Size2(1, 3) * p_size), p_color); + } + if (x & (1 << 0)) { + RenderingServer::get_singleton()->canvas_item_add_rect(p_canvas, Rect2(p_pos + Point2(0, 2) * p_size, Size2(3, 1) * p_size), p_color); } } -Vector2 TextServer::get_hex_code_box_size(int p_size, char32_t p_index) const { - int fnt = (p_size < 20) ? 0 : 1; +void TextServer::draw_hex_code_box(const RID &p_canvas, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color) const { + if (p_index == 0) { + return; + } - real_t w = ((p_index <= 0xFF) ? 1 : ((p_index <= 0xFFFF) ? 2 : 3)) * hex_code_box_font_size[fnt].x; - real_t h = 2 * hex_code_box_font_size[fnt].y; - return Vector2(w + 4, h + 3 + 2 * hex_code_box_font_size[fnt].z); -} + int w = ((p_index <= 0xFF) ? 1 : ((p_index <= 0xFFFF) ? 2 : 3)); + int sp = MAX(0, w - 1); + int sz = MAX(1, Math::round(p_size / 15.f)); -void TextServer::draw_hex_code_box(RID p_canvas, int p_size, const Vector2 &p_pos, char32_t p_index, const Color &p_color) const { - int fnt = (p_size < 20) ? 0 : 1; + Size2 size = Vector2(4 + 3 * w + sp, 15) * sz; + Point2 pos = p_pos - Point2i(0, size.y * 0.85); - ERR_FAIL_COND(hex_code_box_font_tex[fnt].is_null()); + // Draw frame. + RenderingServer::get_singleton()->canvas_item_add_rect(p_canvas, Rect2(pos + Point2(0, 0), Size2(sz, size.y)), p_color); + RenderingServer::get_singleton()->canvas_item_add_rect(p_canvas, Rect2(pos + Point2(size.x - sz, 0), Size2(sz, size.y)), p_color); + RenderingServer::get_singleton()->canvas_item_add_rect(p_canvas, Rect2(pos + Point2(0, 0), Size2(size.x, sz)), p_color); + RenderingServer::get_singleton()->canvas_item_add_rect(p_canvas, Rect2(pos + Point2(0, size.y - sz), Size2(size.x, sz)), p_color); uint8_t a = p_index & 0x0F; uint8_t b = (p_index >> 4) & 0x0F; @@ -584,57 +639,31 @@ void TextServer::draw_hex_code_box(RID p_canvas, int p_size, const Vector2 &p_po uint8_t e = (p_index >> 16) & 0x0F; uint8_t f = (p_index >> 20) & 0x0F; - Vector2 pos = p_pos; - Rect2 dest = Rect2(Vector2(), Vector2(hex_code_box_font_size[fnt].x, hex_code_box_font_size[fnt].y)); - - real_t w = ((p_index <= 0xFF) ? 1 : ((p_index <= 0xFFFF) ? 2 : 3)) * hex_code_box_font_size[fnt].x; - real_t h = 2 * hex_code_box_font_size[fnt].y; - - pos.y -= Math::floor((h + 3 + hex_code_box_font_size[fnt].z) * 0.75); - - RenderingServer::get_singleton()->canvas_item_add_rect(p_canvas, Rect2(pos + Point2(0, 0), Size2(1, h + 2 + 2 * hex_code_box_font_size[fnt].z)), p_color); - RenderingServer::get_singleton()->canvas_item_add_rect(p_canvas, Rect2(pos + Point2(w + 2, 0), Size2(1, h + 2 + 2 * hex_code_box_font_size[fnt].z)), p_color); - RenderingServer::get_singleton()->canvas_item_add_rect(p_canvas, Rect2(pos + Point2(0, 0), Size2(w + 2, 1)), p_color); - RenderingServer::get_singleton()->canvas_item_add_rect(p_canvas, Rect2(pos + Point2(0, h + 2 + 2 * hex_code_box_font_size[fnt].z), Size2(w + 2, 1)), p_color); - - pos += Point2(2, 2); + // Draw hex code. if (p_index <= 0xFF) { - dest.position = pos + Vector2(hex_code_box_font_size[fnt].x, hex_code_box_font_size[fnt].y) * Point2(0, 0); - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, dest, hex_code_box_font_tex[fnt]->get_rid(), Rect2(Point2(b * hex_code_box_font_size[fnt].x, 0), dest.size), p_color, false, false); - dest.position = pos + Vector2(hex_code_box_font_size[fnt].x, hex_code_box_font_size[fnt].y) * Point2(0, 1) + Point2(0, hex_code_box_font_size[fnt].z); - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, dest, hex_code_box_font_tex[fnt]->get_rid(), Rect2(Point2(a * hex_code_box_font_size[fnt].x, 0), dest.size), p_color, false, false); + _draw_hex_code_box_number(p_canvas, sz, pos + Point2(2, 2) * sz, b, p_color); + _draw_hex_code_box_number(p_canvas, sz, pos + Point2(2, 8) * sz, a, p_color); } else if (p_index <= 0xFFFF) { - dest.position = pos + Vector2(hex_code_box_font_size[fnt].x, hex_code_box_font_size[fnt].y) * Point2(0, 0); - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, dest, hex_code_box_font_tex[fnt]->get_rid(), Rect2(Point2(d * hex_code_box_font_size[fnt].x, 0), dest.size), p_color, false, false); - dest.position = pos + Vector2(hex_code_box_font_size[fnt].x, hex_code_box_font_size[fnt].y) * Point2(1, 0); - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, dest, hex_code_box_font_tex[fnt]->get_rid(), Rect2(Point2(c * hex_code_box_font_size[fnt].x, 0), dest.size), p_color, false, false); - dest.position = pos + Vector2(hex_code_box_font_size[fnt].x, hex_code_box_font_size[fnt].y) * Point2(0, 1) + Point2(0, hex_code_box_font_size[fnt].z); - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, dest, hex_code_box_font_tex[fnt]->get_rid(), Rect2(Point2(b * hex_code_box_font_size[fnt].x, 0), dest.size), p_color, false, false); - dest.position = pos + Vector2(hex_code_box_font_size[fnt].x, hex_code_box_font_size[fnt].y) * Point2(1, 1) + Point2(0, hex_code_box_font_size[fnt].z); - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, dest, hex_code_box_font_tex[fnt]->get_rid(), Rect2(Point2(a * hex_code_box_font_size[fnt].x, 0), dest.size), p_color, false, false); + _draw_hex_code_box_number(p_canvas, sz, pos + Point2(2, 2) * sz, d, p_color); + _draw_hex_code_box_number(p_canvas, sz, pos + Point2(6, 2) * sz, c, p_color); + _draw_hex_code_box_number(p_canvas, sz, pos + Point2(2, 8) * sz, b, p_color); + _draw_hex_code_box_number(p_canvas, sz, pos + Point2(6, 8) * sz, a, p_color); } else { - dest.position = pos + Vector2(hex_code_box_font_size[fnt].x, hex_code_box_font_size[fnt].y) * Point2(0, 0); - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, dest, hex_code_box_font_tex[fnt]->get_rid(), Rect2(Point2(f * hex_code_box_font_size[fnt].x, 0), dest.size), p_color, false, false); - dest.position = pos + Vector2(hex_code_box_font_size[fnt].x, hex_code_box_font_size[fnt].y) * Point2(1, 0); - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, dest, hex_code_box_font_tex[fnt]->get_rid(), Rect2(Point2(e * hex_code_box_font_size[fnt].x, 0), dest.size), p_color, false, false); - dest.position = pos + Vector2(hex_code_box_font_size[fnt].x, hex_code_box_font_size[fnt].y) * Point2(2, 0); - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, dest, hex_code_box_font_tex[fnt]->get_rid(), Rect2(Point2(d * hex_code_box_font_size[fnt].x, 0), dest.size), p_color, false, false); - dest.position = pos + Vector2(hex_code_box_font_size[fnt].x, hex_code_box_font_size[fnt].y) * Point2(0, 1) + Point2(0, hex_code_box_font_size[fnt].z); - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, dest, hex_code_box_font_tex[fnt]->get_rid(), Rect2(Point2(c * hex_code_box_font_size[fnt].x, 0), dest.size), p_color, false, false); - dest.position = pos + Vector2(hex_code_box_font_size[fnt].x, hex_code_box_font_size[fnt].y) * Point2(1, 1) + Point2(0, hex_code_box_font_size[fnt].z); - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, dest, hex_code_box_font_tex[fnt]->get_rid(), Rect2(Point2(b * hex_code_box_font_size[fnt].x, 0), dest.size), p_color, false, false); - dest.position = pos + Vector2(hex_code_box_font_size[fnt].x, hex_code_box_font_size[fnt].y) * Point2(2, 1) + Point2(0, hex_code_box_font_size[fnt].z); - RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, dest, hex_code_box_font_tex[fnt]->get_rid(), Rect2(Point2(a * hex_code_box_font_size[fnt].x, 0), dest.size), p_color, false, false); + _draw_hex_code_box_number(p_canvas, sz, pos + Point2(2, 2) * sz, f, p_color); + _draw_hex_code_box_number(p_canvas, sz, pos + Point2(6, 2) * sz, e, p_color); + _draw_hex_code_box_number(p_canvas, sz, pos + Point2(10, 2) * sz, d, p_color); + _draw_hex_code_box_number(p_canvas, sz, pos + Point2(2, 8) * sz, c, p_color); + _draw_hex_code_box_number(p_canvas, sz, pos + Point2(6, 8) * sz, b, p_color); + _draw_hex_code_box_number(p_canvas, sz, pos + Point2(10, 8) * sz, a, p_color); } } -Vector<Vector2i> TextServer::shaped_text_get_line_breaks_adv(RID p_shaped, const Vector<real_t> &p_width, int p_start, bool p_once, uint8_t /*TextBreakFlag*/ p_break_flags) const { - Vector<Vector2i> lines; +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); const_cast<TextServer *>(this)->shaped_text_update_breaks(p_shaped); - const Vector<Glyph> &logical = const_cast<TextServer *>(this)->shaped_text_sort_logical(p_shaped); const Vector2i &range = shaped_text_get_range(p_shaped); real_t width = 0.f; @@ -642,8 +671,8 @@ Vector<Vector2i> TextServer::shaped_text_get_line_breaks_adv(RID p_shaped, const int last_safe_break = -1; int chunk = 0; - int l_size = logical.size(); - const Glyph *l_gl = logical.ptr(); + 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) { @@ -651,7 +680,8 @@ Vector<Vector2i> TextServer::shaped_text_get_line_breaks_adv(RID p_shaped, const } 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(Vector2i(line_start, l_gl[last_safe_break].end)); + lines.push_back(line_start); + lines.push_back(l_gl[last_safe_break].end); line_start = l_gl[last_safe_break].end; i = last_safe_break; last_safe_break = -1; @@ -665,9 +695,10 @@ Vector<Vector2i> TextServer::shaped_text_get_line_breaks_adv(RID p_shaped, const } 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(Vector2i(line_start, l_gl[i].end)); + lines.push_back(line_start); + lines.push_back(l_gl[i].end); line_start = l_gl[i].end; last_safe_break = -1; width = 0; @@ -678,12 +709,12 @@ Vector<Vector2i> TextServer::shaped_text_get_line_breaks_adv(RID p_shaped, const 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; } } - if ((p_break_flags & BREAK_GRAPHEME_BOUND) == BREAK_GRAPHEME_BOUND) { + if (p_break_flags.has_flag(BREAK_GRAPHEME_BOUND)) { last_safe_break = i; } } @@ -691,27 +722,31 @@ Vector<Vector2i> TextServer::shaped_text_get_line_breaks_adv(RID p_shaped, const } if (l_size > 0) { - lines.push_back(Vector2i(line_start, range.y)); + if (lines.size() == 0 || lines[lines.size() - 1] < range.y) { + lines.push_back(line_start); + lines.push_back(range.y); + } } else { - lines.push_back(Vector2i(0, 0)); + lines.push_back(0); + lines.push_back(0); } return lines; } -Vector<Vector2i> TextServer::shaped_text_get_line_breaks(RID p_shaped, real_t p_width, int p_start, uint8_t /*TextBreakFlag*/ p_break_flags) const { - Vector<Vector2i> lines; +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); - const Vector<Glyph> &logical = const_cast<TextServer *>(this)->shaped_text_sort_logical(p_shaped); const Vector2i &range = shaped_text_get_range(p_shaped); - real_t width = 0.f; + double width = 0.f; int line_start = MAX(p_start, range.x); int last_safe_break = -1; int word_count = 0; - int l_size = logical.size(); - const Glyph *l_gl = logical.ptr(); + + 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) { @@ -719,7 +754,8 @@ Vector<Vector2i> TextServer::shaped_text_get_line_breaks(RID p_shaped, real_t p_ } 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(Vector2i(line_start, l_gl[last_safe_break].end)); + lines.push_back(line_start); + lines.push_back(l_gl[last_safe_break].end); line_start = l_gl[last_safe_break].end; i = last_safe_break; last_safe_break = -1; @@ -727,25 +763,26 @@ Vector<Vector2i> TextServer::shaped_text_get_line_breaks(RID p_shaped, real_t p_ 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(Vector2i(line_start, l_gl[i].end)); + lines.push_back(line_start); + lines.push_back(l_gl[i].end); line_start = l_gl[i].end; 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; } } @@ -753,51 +790,53 @@ Vector<Vector2i> TextServer::shaped_text_get_line_breaks(RID p_shaped, real_t p_ } if (l_size > 0) { - if (lines.size() == 0 || lines[lines.size() - 1].y < range.y) { - lines.push_back(Vector2i(line_start, range.y)); + if (lines.size() == 0 || lines[lines.size() - 1] < range.y) { + lines.push_back(line_start); + lines.push_back(range.y); } } else { - lines.push_back(Vector2i(0, 0)); + lines.push_back(0); + lines.push_back(0); } return lines; } -Vector<Vector2i> TextServer::shaped_text_get_word_breaks(RID p_shaped, int p_grapheme_flags) const { - Vector<Vector2i> words; +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); - const Vector<Glyph> &logical = const_cast<TextServer *>(this)->shaped_text_sort_logical(p_shaped); const Vector2i &range = shaped_text_get_range(p_shaped); int word_start = range.x; - int l_size = logical.size(); - const Glyph *l_gl = logical.ptr(); + const 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].count > 0) { if ((l_gl[i].flags & p_grapheme_flags) != 0) { - words.push_back(Vector2i(word_start, l_gl[i].start)); + if (word_start != l_gl[i].start) { + words.push_back(word_start); + words.push_back(l_gl[i].start); + } word_start = l_gl[i].end; } } } if (l_size > 0) { - words.push_back(Vector2i(word_start, range.y)); + if (word_start != range.y) { + words.push_back(word_start); + words.push_back(range.y); + } } return words; } -TextServer::TrimData TextServer::shaped_text_get_trim_data(RID p_shaped) const { - WARN_PRINT("Getting overrun data not supported by this TextServer."); - return TrimData(); -} - -void TextServer::shaped_text_get_carets(RID p_shaped, int p_position, Rect2 &p_leading_caret, Direction &p_leading_dir, Rect2 &p_trailing_caret, Direction &p_trailing_dir) const { +CaretInfo TextServer::shaped_text_get_carets(const RID &p_shaped, int64_t p_position) const { Vector<Rect2> carets; - const Vector<TextServer::Glyph> visual = shaped_text_get_glyphs(p_shaped); + TextServer::Orientation orientation = shaped_text_get_orientation(p_shaped); const Vector2 &range = shaped_text_get_range(p_shaped); real_t ascent = shaped_text_get_ascent(p_shaped); @@ -805,11 +844,12 @@ void TextServer::shaped_text_get_carets(RID p_shaped, int p_position, Rect2 &p_l real_t height = (ascent + descent) / 2; real_t off = 0.0f; - p_leading_dir = DIRECTION_AUTO; - p_trailing_dir = DIRECTION_AUTO; + CaretInfo caret; + caret.l_dir = DIRECTION_AUTO; + caret.t_dir = DIRECTION_AUTO; - int v_size = visual.size(); - const Glyph *glyphs = visual.ptr(); + int v_size = shaped_text_get_glyph_count(p_shaped); + const Glyph *glyphs = shaped_text_get_glyphs(p_shaped); for (int i = 0; i < v_size; i++) { if (glyphs[i].count > 0) { @@ -825,13 +865,13 @@ void TextServer::shaped_text_get_carets(RID p_shaped, int p_position, Rect2 &p_l cr.position.y = -ascent; cr.position.x = off; if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) { - p_trailing_dir = DIRECTION_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; } } else { - p_trailing_dir = DIRECTION_LTR; + 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; } @@ -845,19 +885,19 @@ void TextServer::shaped_text_get_carets(RID p_shaped, int p_position, Rect2 &p_l cr.position.x = -ascent; cr.position.y = off; if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) { - p_trailing_dir = DIRECTION_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; } } else { - p_trailing_dir = DIRECTION_LTR; + 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; } } } - p_trailing_caret = cr; + caret.t_caret = cr; } // Caret after grapheme (bottom / right). if (p_position == glyphs[i].end && ((glyphs[i].flags & GRAPHEME_IS_VIRTUAL) != GRAPHEME_IS_VIRTUAL)) { @@ -872,13 +912,13 @@ void TextServer::shaped_text_get_carets(RID p_shaped, int p_position, Rect2 &p_l } cr.position.x = off; if ((glyphs[i].flags & GRAPHEME_IS_RTL) != GRAPHEME_IS_RTL) { - p_leading_dir = DIRECTION_LTR; + 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; } } else { - p_leading_dir = DIRECTION_RTL; + 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; } @@ -894,19 +934,19 @@ void TextServer::shaped_text_get_carets(RID p_shaped, int p_position, Rect2 &p_l } cr.position.y = off; if ((glyphs[i].flags & GRAPHEME_IS_RTL) != GRAPHEME_IS_RTL) { - p_leading_dir = DIRECTION_LTR; + 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; } } else { - p_leading_dir = DIRECTION_RTL; + 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; } } } - p_leading_caret = cr; + caret.l_caret = cr; } // Caret inside grapheme (middle). if (p_position > glyphs[i].start && p_position < glyphs[i].end && (glyphs[i].flags & GRAPHEME_IS_VIRTUAL) != GRAPHEME_IS_VIRTUAL) { @@ -935,17 +975,29 @@ void TextServer::shaped_text_get_carets(RID p_shaped, int p_position, Rect2 &p_l cr.position.y = off + char_adv * (p_position - glyphs[i].start); } } - p_trailing_caret = cr; - p_leading_caret = cr; + caret.t_caret = cr; + caret.l_caret = cr; } } off += glyphs[i].advance * glyphs[i].repeat; } + return caret; } -TextServer::Direction TextServer::shaped_text_get_dominant_direciton_in_range(RID p_shaped, int p_start, int p_end) const { - const Vector<TextServer::Glyph> visual = shaped_text_get_glyphs(p_shaped); +Dictionary TextServer::_shaped_text_get_carets_wrapper(const RID &p_shaped, int64_t p_position) const { + Dictionary ret; + CaretInfo caret = shaped_text_get_carets(p_shaped, p_position); + + ret["leading_rect"] = caret.l_caret; + ret["leading_direction"] = caret.l_dir; + ret["trailing_rect"] = caret.t_caret; + ret["trailing_direction"] = caret.t_dir; + + return ret; +} + +TextServer::Direction TextServer::shaped_text_get_dominant_direction_in_range(const RID &p_shaped, int64_t p_start, int64_t p_end) const { if (p_start == p_end) { return DIRECTION_AUTO; } @@ -956,8 +1008,8 @@ TextServer::Direction TextServer::shaped_text_get_dominant_direciton_in_range(RI int rtl = 0; int ltr = 0; - int v_size = visual.size(); - const Glyph *glyphs = visual.ptr(); + int v_size = shaped_text_get_glyph_count(p_shaped); + const Glyph *glyphs = shaped_text_get_glyphs(p_shaped); for (int i = 0; i < v_size; i++) { if ((glyphs[i].end > start) && (glyphs[i].start < end)) { @@ -979,9 +1031,16 @@ TextServer::Direction TextServer::shaped_text_get_dominant_direciton_in_range(RI } } -Vector<Vector2> TextServer::shaped_text_get_selection(RID p_shaped, int p_start, int p_end) const { +_FORCE_INLINE_ void _push_range(Vector<Vector2> &r_vector, real_t p_start, real_t p_end) { + if (!r_vector.is_empty() && Math::is_equal_approx(r_vector[r_vector.size() - 1].y, p_start, (real_t)UNIT_EPSILON)) { + r_vector.write[r_vector.size() - 1].y = p_end; + } else { + r_vector.push_back(Vector2(p_start, p_end)); + } +} + +Vector<Vector2> TextServer::shaped_text_get_selection(const RID &p_shaped, int64_t p_start, int64_t p_end) const { Vector<Vector2> ranges; - const Vector<TextServer::Glyph> visual = shaped_text_get_glyphs(p_shaped); if (p_start == p_end) { return ranges; @@ -990,8 +1049,8 @@ Vector<Vector2> TextServer::shaped_text_get_selection(RID p_shaped, int p_start, int start = MIN(p_start, p_end); int end = MAX(p_start, p_end); - int v_size = visual.size(); - const Glyph *glyphs = visual.ptr(); + int v_size = shaped_text_get_glyph_count(p_shaped); + const Glyph *glyphs = shaped_text_get_glyphs(p_shaped); real_t off = 0.0f; for (int i = 0; i < v_size; i++) { @@ -1004,7 +1063,7 @@ Vector<Vector2> TextServer::shaped_text_get_selection(RID p_shaped, int p_start, for (int j = 0; j < glyphs[i].count; j++) { advance += glyphs[i + j].advance; } - ranges.push_back(Vector2(off, off + advance)); + _push_range(ranges, off, off + advance); } // Only start of grapheme is in selection range. if (glyphs[i].start >= start && glyphs[i].end > end) { @@ -1014,9 +1073,9 @@ Vector<Vector2> TextServer::shaped_text_get_selection(RID p_shaped, int p_start, } real_t char_adv = advance / (real_t)(glyphs[i].end - glyphs[i].start); if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) { - ranges.push_back(Vector2(off + char_adv * (glyphs[i].end - end), off + advance)); + _push_range(ranges, off + char_adv * (glyphs[i].end - end), off + advance); } else { - ranges.push_back(Vector2(off, off + char_adv * (end - glyphs[i].start))); + _push_range(ranges, off, off + char_adv * (end - glyphs[i].start)); } } // Only end of grapheme is in selection range. @@ -1027,9 +1086,9 @@ Vector<Vector2> TextServer::shaped_text_get_selection(RID p_shaped, int p_start, } real_t char_adv = advance / (real_t)(glyphs[i].end - glyphs[i].start); if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) { - ranges.push_back(Vector2(off, off + char_adv * (start - glyphs[i].start))); + _push_range(ranges, off, off + char_adv * (glyphs[i].end - start)); } else { - ranges.push_back(Vector2(off + char_adv * (glyphs[i].end - start), off + advance)); + _push_range(ranges, off + char_adv * (start - glyphs[i].start), off + advance); } } // Selection range is within grapheme. @@ -1040,9 +1099,9 @@ Vector<Vector2> TextServer::shaped_text_get_selection(RID p_shaped, int p_start, } real_t char_adv = advance / (real_t)(glyphs[i].end - glyphs[i].start); if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) { - ranges.push_back(Vector2(off + char_adv * (glyphs[i].end - end), off + char_adv * (glyphs[i].end - start))); + _push_range(ranges, off + char_adv * (glyphs[i].end - end), off + char_adv * (glyphs[i].end - start)); } else { - ranges.push_back(Vector2(off + char_adv * (start - glyphs[i].start), off + char_adv * (end - glyphs[i].start))); + _push_range(ranges, off + char_adv * (start - glyphs[i].start), off + char_adv * (end - glyphs[i].start)); } } } @@ -1051,36 +1110,15 @@ Vector<Vector2> TextServer::shaped_text_get_selection(RID p_shaped, int p_start, } } - // Merge intersecting ranges. - int i = 0; - while (i < ranges.size()) { - i++; - } - i = 0; - while (i < ranges.size()) { - int j = i + 1; - while (j < ranges.size()) { - if (Math::is_equal_approx(ranges[i].y, ranges[j].x, (real_t)UNIT_EPSILON)) { - ranges.write[i].y = ranges[j].y; - ranges.remove(j); - continue; - } - j++; - } - i++; - } - return ranges; } -int TextServer::shaped_text_hit_test_grapheme(RID p_shaped, real_t p_coords) const { - const Vector<TextServer::Glyph> visual = shaped_text_get_glyphs(p_shaped); - +int64_t TextServer::shaped_text_hit_test_grapheme(const RID &p_shaped, double p_coords) const { // Exact grapheme hit test, return -1 if missed. - real_t off = 0.0f; + double off = 0.0f; - int v_size = visual.size(); - const Glyph *glyphs = visual.ptr(); + int v_size = shaped_text_get_glyph_count(p_shaped); + const Glyph *glyphs = shaped_text_get_glyphs(p_shaped); for (int i = 0; i < v_size; i++) { for (int j = 0; j < glyphs[i].repeat; j++) { @@ -1093,11 +1131,9 @@ int TextServer::shaped_text_hit_test_grapheme(RID p_shaped, real_t p_coords) con return -1; } -int TextServer::shaped_text_hit_test_position(RID p_shaped, real_t p_coords) const { - const Vector<TextServer::Glyph> visual = shaped_text_get_glyphs(p_shaped); - - int v_size = visual.size(); - const Glyph *glyphs = visual.ptr(); +int64_t TextServer::shaped_text_hit_test_position(const RID &p_shaped, double p_coords) const { + int v_size = shaped_text_get_glyph_count(p_shaped); + const Glyph *glyphs = shaped_text_get_glyphs(p_shaped); // Cursor placement hit test. @@ -1141,6 +1177,31 @@ int TextServer::shaped_text_hit_test_position(RID p_shaped, real_t p_coords) con return glyphs[i].start; } } + // Ligature, handle mid-grapheme hit. + if (p_coords >= off && p_coords < off + advance && glyphs[i].end > glyphs[i].start + 1) { + int cnt = glyphs[i].end - glyphs[i].start; + real_t char_adv = advance / (real_t)(cnt); + real_t sub_off = off; + for (int j = 0; j < cnt; j++) { + // Place caret to the left of clicked sub-grapheme. + if (p_coords >= sub_off && p_coords < sub_off + char_adv / 2) { + if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) { + return glyphs[i].end - j; + } else { + return glyphs[i].start + j; + } + } + // Place caret to the right of clicked sub-grapheme. + if (p_coords >= sub_off + char_adv / 2 && p_coords < sub_off + char_adv) { + if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) { + return glyphs[i].start + (cnt - 1) - j; + } else { + return glyphs[i].end - (cnt - 1) + j; + } + } + sub_off += char_adv; + } + } // Place caret to the left of clicked grapheme. if (p_coords >= off && p_coords < off + advance / 2) { if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) { @@ -1163,10 +1224,30 @@ int TextServer::shaped_text_hit_test_position(RID p_shaped, real_t p_coords) con return 0; } -int TextServer::shaped_text_next_grapheme_pos(RID p_shaped, int p_pos) { - const Vector<TextServer::Glyph> visual = shaped_text_get_glyphs(p_shaped); - int v_size = visual.size(); - const Glyph *glyphs = visual.ptr(); +Vector2 TextServer::shaped_text_get_grapheme_bounds(const RID &p_shaped, int64_t p_pos) const { + int v_size = shaped_text_get_glyph_count(p_shaped); + const Glyph *glyphs = shaped_text_get_glyphs(p_shaped); + + real_t off = 0.0f; + for (int i = 0; i < v_size; i++) { + if ((glyphs[i].count > 0) && ((glyphs[i].index != 0) || ((glyphs[i].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE))) { + if (glyphs[i].start <= p_pos && glyphs[i].end >= p_pos) { + real_t advance = 0.f; + for (int j = 0; j < glyphs[i].count; j++) { + advance += glyphs[i + j].advance; + } + return Vector2(off, off + advance); + } + } + off += glyphs[i].advance * glyphs[i].repeat; + } + + return Vector2(); +} + +int64_t TextServer::shaped_text_next_grapheme_pos(const RID &p_shaped, int64_t p_pos) const { + int v_size = shaped_text_get_glyph_count(p_shaped); + const Glyph *glyphs = shaped_text_get_glyphs(p_shaped); for (int i = 0; i < v_size; i++) { if (p_pos >= glyphs[i].start && p_pos < glyphs[i].end) { return glyphs[i].end; @@ -1175,10 +1256,9 @@ int TextServer::shaped_text_next_grapheme_pos(RID p_shaped, int p_pos) { return p_pos; } -int TextServer::shaped_text_prev_grapheme_pos(RID p_shaped, int p_pos) { - const Vector<TextServer::Glyph> visual = shaped_text_get_glyphs(p_shaped); - int v_size = visual.size(); - const Glyph *glyphs = visual.ptr(); +int64_t TextServer::shaped_text_prev_grapheme_pos(const RID &p_shaped, int64_t p_pos) const { + int v_size = shaped_text_get_glyph_count(p_shaped); + const Glyph *glyphs = shaped_text_get_glyphs(p_shaped); for (int i = 0; i < v_size; i++) { if (p_pos > glyphs[i].start && p_pos <= glyphs[i].end) { return glyphs[i].start; @@ -1188,42 +1268,57 @@ int TextServer::shaped_text_prev_grapheme_pos(RID p_shaped, int p_pos) { return p_pos; } -void TextServer::shaped_text_draw(RID p_shaped, RID p_canvas, const Vector2 &p_pos, real_t p_clip_l, real_t p_clip_r, const Color &p_color) const { - const Vector<TextServer::Glyph> visual = shaped_text_get_glyphs(p_shaped); +void TextServer::shaped_text_draw(const RID &p_shaped, const RID &p_canvas, const Vector2 &p_pos, double p_clip_l, double p_clip_r, const Color &p_color) const { TextServer::Orientation orientation = shaped_text_get_orientation(p_shaped); bool hex_codes = shaped_text_get_preserve_control(p_shaped) || shaped_text_get_preserve_invalid(p_shaped); bool rtl = shaped_text_get_direction(p_shaped) == DIRECTION_RTL; - TrimData trim_data = shaped_text_get_trim_data(p_shaped); - int v_size = visual.size(); - const Glyph *glyphs = visual.ptr(); + int ellipsis_pos = shaped_text_get_ellipsis_pos(p_shaped); + int trim_pos = shaped_text_get_trim_pos(p_shaped); + + const Glyph *ellipsis_glyphs = shaped_text_get_ellipsis_glyphs(p_shaped); + int ellipsis_gl_size = shaped_text_get_ellipsis_glyph_count(p_shaped); + + int v_size = shaped_text_get_glyph_count(p_shaped); + const Glyph *glyphs = shaped_text_get_glyphs(p_shaped); Vector2 ofs = p_pos; // Draw RTL ellipsis string when needed. - if (rtl && trim_data.ellipsis_pos >= 0) { - for (int i = trim_data.ellipsis_glyph_buf.size() - 1; i >= 0; i--) { - for (int j = 0; j < trim_data.ellipsis_glyph_buf[i].repeat; j++) { - font_draw_glyph(trim_data.ellipsis_glyph_buf[i].font_rid, p_canvas, trim_data.ellipsis_glyph_buf[i].font_size, ofs + Vector2(trim_data.ellipsis_glyph_buf[i].x_off, trim_data.ellipsis_glyph_buf[i].y_off), trim_data.ellipsis_glyph_buf[i].index, p_color); + if (rtl && ellipsis_pos >= 0) { + for (int i = ellipsis_gl_size - 1; i >= 0; i--) { + for (int j = 0; j < ellipsis_glyphs[i].repeat; j++) { + font_draw_glyph(ellipsis_glyphs[i].font_rid, p_canvas, ellipsis_glyphs[i].font_size, ofs + Vector2(ellipsis_glyphs[i].x_off, ellipsis_glyphs[i].y_off), ellipsis_glyphs[i].index, p_color); if (orientation == ORIENTATION_HORIZONTAL) { - ofs.x += trim_data.ellipsis_glyph_buf[i].advance; + ofs.x += ellipsis_glyphs[i].advance; } else { - ofs.y += trim_data.ellipsis_glyph_buf[i].advance; + ofs.y += ellipsis_glyphs[i].advance; } } } } // Draw at the baseline. for (int i = 0; i < v_size; i++) { + if (trim_pos >= 0) { + if (rtl) { + if (i < trim_pos) { + continue; + } + } else { + if (i >= trim_pos) { + break; + } + } + } for (int j = 0; j < glyphs[i].repeat; j++) { if (p_clip_r > 0) { // Clip right / bottom. if (orientation == ORIENTATION_HORIZONTAL) { - if (ofs.x - p_pos.x > p_clip_r) { + if (ofs.x - p_pos.x + glyphs[i].advance > p_clip_r) { return; } } else { - if (ofs.y - p_pos.y > p_clip_r) { + if (ofs.y - p_pos.y + glyphs[i].advance > p_clip_r) { return; } } @@ -1242,17 +1337,6 @@ void TextServer::shaped_text_draw(RID p_shaped, RID p_canvas, const Vector2 &p_p } } } - if (trim_data.trim_pos >= 0) { - if (rtl) { - if (i < trim_data.trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) { - continue; - } - } else { - if (i >= trim_data.trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) { - break; - } - } - } if (glyphs[i].font_rid != RID()) { font_draw_glyph(glyphs[i].font_rid, p_canvas, glyphs[i].font_size, ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, p_color); @@ -1267,54 +1351,69 @@ void TextServer::shaped_text_draw(RID p_shaped, RID p_canvas, const Vector2 &p_p } } // Draw LTR ellipsis string when needed. - if (!rtl && trim_data.ellipsis_pos >= 0) { - for (int i = 0; i < trim_data.ellipsis_glyph_buf.size(); i++) { - for (int j = 0; j < trim_data.ellipsis_glyph_buf[i].repeat; j++) { - font_draw_glyph(trim_data.ellipsis_glyph_buf[i].font_rid, p_canvas, trim_data.ellipsis_glyph_buf[i].font_size, ofs + Vector2(trim_data.ellipsis_glyph_buf[i].x_off, trim_data.ellipsis_glyph_buf[i].y_off), trim_data.ellipsis_glyph_buf[i].index, p_color); + if (!rtl && ellipsis_pos >= 0) { + for (int i = 0; i < ellipsis_gl_size; i++) { + for (int j = 0; j < ellipsis_glyphs[i].repeat; j++) { + font_draw_glyph(ellipsis_glyphs[i].font_rid, p_canvas, ellipsis_glyphs[i].font_size, ofs + Vector2(ellipsis_glyphs[i].x_off, ellipsis_glyphs[i].y_off), ellipsis_glyphs[i].index, p_color); if (orientation == ORIENTATION_HORIZONTAL) { - ofs.x += trim_data.ellipsis_glyph_buf[i].advance; + ofs.x += ellipsis_glyphs[i].advance; } else { - ofs.y += trim_data.ellipsis_glyph_buf[i].advance; + ofs.y += ellipsis_glyphs[i].advance; } } } } } -void TextServer::shaped_text_draw_outline(RID p_shaped, RID p_canvas, const Vector2 &p_pos, real_t p_clip_l, real_t p_clip_r, int p_outline_size, const Color &p_color) const { - const Vector<TextServer::Glyph> visual = shaped_text_get_glyphs(p_shaped); +void TextServer::shaped_text_draw_outline(const RID &p_shaped, const RID &p_canvas, const Vector2 &p_pos, double p_clip_l, double p_clip_r, int64_t p_outline_size, const Color &p_color) const { TextServer::Orientation orientation = shaped_text_get_orientation(p_shaped); - bool rtl = (shaped_text_get_direction(p_shaped) == DIRECTION_RTL); - TrimData trim_data = shaped_text_get_trim_data(p_shaped); + bool rtl = (shaped_text_get_inferred_direction(p_shaped) == DIRECTION_RTL); - int v_size = visual.size(); - const Glyph *glyphs = visual.ptr(); + int ellipsis_pos = shaped_text_get_ellipsis_pos(p_shaped); + int trim_pos = shaped_text_get_trim_pos(p_shaped); + + const Glyph *ellipsis_glyphs = shaped_text_get_ellipsis_glyphs(p_shaped); + int ellipsis_gl_size = shaped_text_get_ellipsis_glyph_count(p_shaped); + + int v_size = shaped_text_get_glyph_count(p_shaped); + const Glyph *glyphs = shaped_text_get_glyphs(p_shaped); Vector2 ofs = p_pos; // Draw RTL ellipsis string when needed. - if (rtl && trim_data.ellipsis_pos >= 0) { - for (int i = trim_data.ellipsis_glyph_buf.size() - 1; i >= 0; i--) { - for (int j = 0; j < trim_data.ellipsis_glyph_buf[i].repeat; j++) { - font_draw_glyph(trim_data.ellipsis_glyph_buf[i].font_rid, p_canvas, trim_data.ellipsis_glyph_buf[i].font_size, ofs + Vector2(trim_data.ellipsis_glyph_buf[i].x_off, trim_data.ellipsis_glyph_buf[i].y_off), trim_data.ellipsis_glyph_buf[i].index, p_color); + if (rtl && ellipsis_pos >= 0) { + for (int i = ellipsis_gl_size - 1; i >= 0; i--) { + for (int j = 0; j < ellipsis_glyphs[i].repeat; j++) { + font_draw_glyph(ellipsis_glyphs[i].font_rid, p_canvas, ellipsis_glyphs[i].font_size, ofs + Vector2(ellipsis_glyphs[i].x_off, ellipsis_glyphs[i].y_off), ellipsis_glyphs[i].index, p_color); if (orientation == ORIENTATION_HORIZONTAL) { - ofs.x += trim_data.ellipsis_glyph_buf[i].advance; + ofs.x += ellipsis_glyphs[i].advance; } else { - ofs.y += trim_data.ellipsis_glyph_buf[i].advance; + ofs.y += ellipsis_glyphs[i].advance; } } } } // Draw at the baseline. for (int i = 0; i < v_size; i++) { + if (trim_pos >= 0) { + if (rtl) { + if (i < trim_pos) { + continue; + } + } else { + if (i >= trim_pos) { + break; + } + } + } for (int j = 0; j < glyphs[i].repeat; j++) { if (p_clip_r > 0) { // Clip right / bottom. if (orientation == ORIENTATION_HORIZONTAL) { - if (ofs.x - p_pos.x > p_clip_r) { + if (ofs.x - p_pos.x + glyphs[i].advance > p_clip_r) { return; } } else { - if (ofs.y - p_pos.y > p_clip_r) { + if (ofs.y - p_pos.y + glyphs[i].advance > p_clip_r) { return; } } @@ -1333,17 +1432,6 @@ void TextServer::shaped_text_draw_outline(RID p_shaped, RID p_canvas, const Vect } } } - if (trim_data.trim_pos >= 0) { - if (rtl) { - if (i < trim_data.trim_pos) { - continue; - } - } else { - if (i >= trim_data.trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) { - break; - } - } - } if (glyphs[i].font_rid != RID()) { font_draw_glyph_outline(glyphs[i].font_rid, p_canvas, glyphs[i].font_size, p_outline_size, ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, p_color); } @@ -1355,48 +1443,231 @@ void TextServer::shaped_text_draw_outline(RID p_shaped, RID p_canvas, const Vect } } // Draw LTR ellipsis string when needed. - if (!rtl && trim_data.ellipsis_pos >= 0) { - for (int i = 0; i < trim_data.ellipsis_glyph_buf.size(); i++) { - for (int j = 0; j < trim_data.ellipsis_glyph_buf[i].repeat; j++) { - font_draw_glyph(trim_data.ellipsis_glyph_buf[i].font_rid, p_canvas, trim_data.ellipsis_glyph_buf[i].font_size, ofs + Vector2(trim_data.ellipsis_glyph_buf[i].x_off, trim_data.ellipsis_glyph_buf[i].y_off), trim_data.ellipsis_glyph_buf[i].index, p_color); + if (!rtl && ellipsis_pos >= 0) { + for (int i = 0; i < ellipsis_gl_size; i++) { + for (int j = 0; j < ellipsis_glyphs[i].repeat; j++) { + font_draw_glyph(ellipsis_glyphs[i].font_rid, p_canvas, ellipsis_glyphs[i].font_size, ofs + Vector2(ellipsis_glyphs[i].x_off, ellipsis_glyphs[i].y_off), ellipsis_glyphs[i].index, p_color); if (orientation == ORIENTATION_HORIZONTAL) { - ofs.x += trim_data.ellipsis_glyph_buf[i].advance; + ofs.x += ellipsis_glyphs[i].advance; } else { - ofs.y += trim_data.ellipsis_glyph_buf[i].advance; + ofs.y += ellipsis_glyphs[i].advance; } } } } } -Dictionary TextServer::_font_get_glyph_contours(RID p_font, int p_size, int32_t p_index) const { - Vector<Vector3> points; - Vector<int32_t> contours; - bool orientation; - bool ok = font_get_glyph_contours(p_font, p_size, p_index, points, contours, orientation); - Dictionary out; - - if (ok) { - out["points"] = points; - out["contours"] = contours; - out["orientation"] = orientation; +void TextServer::_diacritics_map_add(const String &p_from, char32_t p_to) { + for (int i = 0; i < p_from.size(); i++) { + diacritics_map[p_from[i]] = p_to; } - return out; } -void TextServer::_shaped_text_set_bidi_override(RID p_shaped, const Array &p_override) { - Vector<Vector2i> overrides; - for (int i = 0; i < p_override.size(); i++) { - overrides.push_back(p_override[i]); +void TextServer::_init_diacritics_map() { + diacritics_map.clear(); + + // Latin. + _diacritics_map_add(U"ÀÁÂÃÄÅĀĂĄǍǞǠǺȀȂȦḀẠẢẤẦẨẪẬẮẰẲẴẶ", U'A'); + _diacritics_map_add(U"àáâãäåāăąǎǟǡǻȁȃȧḁẚạảấầẩẫậắằẳẵặ", U'a'); + _diacritics_map_add(U"ǢǼ", U'Æ'); + _diacritics_map_add(U"ǣǽ", U'æ'); + _diacritics_map_add(U"ḂḄḆ", U'B'); + _diacritics_map_add(U"ḃḅḇ", U'b'); + _diacritics_map_add(U"ÇĆĈĊČḈ", U'C'); + _diacritics_map_add(U"çćĉċčḉ", U'c'); + _diacritics_map_add(U"ĎḊḌḎḐḒ", U'D'); + _diacritics_map_add(U"ďḋḍḏḑḓ", U'd'); + _diacritics_map_add(U"ÈÉÊËĒĔĖĘĚȆȨḔḖḘḚḜẸẺẼẾỀỂỄỆ", U'E'); + _diacritics_map_add(U"èéêëēĕėęěȇȩḕḗḙḛḝẹẻẽếềểễệ", U'e'); + _diacritics_map_add(U"Ḟ", U'F'); + _diacritics_map_add(U"ḟ", U'f'); + _diacritics_map_add(U"ĜĞĠĢǦǴḠ", U'G'); + _diacritics_map_add(U"ĝğġģǧǵḡ", U'g'); + _diacritics_map_add(U"ĤȞḢḤḦḨḪ", U'H'); + _diacritics_map_add(U"ĥȟḣḥḧḩḫẖ", U'h'); + _diacritics_map_add(U"ÌÍÎÏĨĪĬĮİǏȈȊḬḮỈỊ", U'I'); + _diacritics_map_add(U"ìíîïĩīĭįıǐȉȋḭḯỉị", U'i'); + _diacritics_map_add(U"Ĵ", U'J'); + _diacritics_map_add(U"ĵ", U'j'); + _diacritics_map_add(U"ĶǨḰḲḴ", U'K'); + _diacritics_map_add(U"ķĸǩḱḳḵ", U'k'); + _diacritics_map_add(U"ĹĻĽĿḶḸḺḼ", U'L'); + _diacritics_map_add(U"ĺļľŀḷḹḻḽ", U'l'); + _diacritics_map_add(U"ḾṀṂ", U'M'); + _diacritics_map_add(U"ḿṁṃ", U'm'); + _diacritics_map_add(U"ÑŃŅŇǸṄṆṈṊ", U'N'); + _diacritics_map_add(U"ñńņňʼnǹṅṇṉṋ", U'n'); + _diacritics_map_add(U"ÒÓÔÕÖŌŎŐƠǑǪǬȌȎȪȬȮȰṌṎṐṒỌỎỐỒỔỖỘỚỜỞỠỢ", U'O'); + _diacritics_map_add(U"òóôõöōŏőơǒǫǭȍȏȫȭȯȱṍṏṑṓọỏốồổỗộớờởỡợ", U'o'); + _diacritics_map_add(U"ṔṖ", U'P'); + _diacritics_map_add(U"ṗṕ", U'p'); + _diacritics_map_add(U"ŔŖŘȐȒṘṚṜṞ", U'R'); + _diacritics_map_add(U"ŕŗřȑȓṙṛṝṟ", U'r'); + _diacritics_map_add(U"ŚŜŞŠȘṠṢṤṦṨ", U'S'); + _diacritics_map_add(U"śŝşšſșṡṣṥṧṩẛẜẝ", U's'); + _diacritics_map_add(U"ŢŤȚṪṬṮṰ", U'T'); + _diacritics_map_add(U"ţťțṫṭṯṱẗ", U't'); + _diacritics_map_add(U"ÙÚÛÜŨŪŬŮŰŲƯǓǕǗǙǛȔȖṲṴṶṸṺỤỦỨỪỬỮỰ", U'U'); + _diacritics_map_add(U"ùúûüũūŭůűųưǔǖǘǚǜȕȗṳṵṷṹṻụủứừửữự", U'u'); + _diacritics_map_add(U"ṼṾ", U'V'); + _diacritics_map_add(U"ṽṿ", U'v'); + _diacritics_map_add(U"ŴẀẂẄẆẈ", U'W'); + _diacritics_map_add(U"ŵẁẃẅẇẉẘ", U'w'); + _diacritics_map_add(U"ẊẌ", U'X'); + _diacritics_map_add(U"ẋẍ", U'x'); + _diacritics_map_add(U"ÝŶẎỲỴỶỸỾ", U'Y'); + _diacritics_map_add(U"ýÿŷẏẙỳỵỷỹỿ", U'y'); + _diacritics_map_add(U"ŹŻŽẐẒẔ", U'Z'); + _diacritics_map_add(U"źżžẑẓẕ", U'z'); + + // Greek. + _diacritics_map_add(U"ΆἈἉἊἋἌἍἎἏᾈᾉᾊᾋᾌᾍᾎᾏᾸᾹᾺΆᾼ", U'Α'); + _diacritics_map_add(U"άἀἁἂἃἄἅἆἇὰάᾀᾁᾂᾃᾄᾅᾆᾇᾰᾱᾲᾳᾴᾶᾷ", U'α'); + _diacritics_map_add(U"ΈἘἙἚἛἜἝῈΈ", U'Ε'); + _diacritics_map_add(U"έἐἑἒἓἔἕὲέ", U'ε'); + _diacritics_map_add(U"ΉἨἩἪἫἬἭἮἯᾘᾙᾚᾛᾜᾝᾞᾟῊΉῌ", U'Η'); + _diacritics_map_add(U"ήἠἡἢἣἤἥἦἧὴήᾐᾑᾒᾓᾔᾕᾖᾗῂῃῄῆῇ", U'η'); + _diacritics_map_add(U"ΊΪἸἹἺἻἼἽἾἿῘῙῚΊ", U'Ι'); + _diacritics_map_add(U"ίΐϊἰἱἲἳἴἵἶἷὶίῐῑῒΐῖῗ", U'ι'); + _diacritics_map_add(U"ΌὈὉὊὋὌὍῸΌ", U'Ο'); + _diacritics_map_add(U"όὀὁὂὃὄὅὸό", U'ο'); + _diacritics_map_add(U"Ῥ", U'Ρ'); + _diacritics_map_add(U"ῤῥ", U'ρ'); + _diacritics_map_add(U"ΎΫϓϔὙὛὝὟῨῩῪΎ", U'Υ'); + _diacritics_map_add(U"ΰϋύὐὑὒὓὔὕὖὗὺύῠῡῢΰῦῧ", U'υ'); + _diacritics_map_add(U"ΏὨὩὪὫὬὭὮὯᾨᾩᾪᾫᾬᾭᾮᾯῺΏῼ", U'Ω'); + _diacritics_map_add(U"ώὠὡὢὣὤὥὦὧὼώᾠᾡᾢᾣᾤᾥᾦᾧῲῳῴῶῷ", U'ω'); + + // Cyrillic. + _diacritics_map_add(U"ӐӒ", U'А'); + _diacritics_map_add(U"ӑӓ", U'а'); + _diacritics_map_add(U"ЀЁӖ", U'Е'); + _diacritics_map_add(U"ѐёӗ", U'е'); + _diacritics_map_add(U"Ӛ", U'Ә'); + _diacritics_map_add(U"ӛ", U'ә'); + _diacritics_map_add(U"Ӝ", U'Ж'); + _diacritics_map_add(U"ӝ", U'ж'); + _diacritics_map_add(U"Ӟ", U'З'); + _diacritics_map_add(U"ӟ", U'з'); + _diacritics_map_add(U"Ѓ", U'Г'); + _diacritics_map_add(U"ѓ", U'г'); + _diacritics_map_add(U"Ї", U'І'); + _diacritics_map_add(U"ї", U'і'); + _diacritics_map_add(U"ЍӢӤЙ", U'И'); + _diacritics_map_add(U"ѝӣӥй", U'и'); + _diacritics_map_add(U"Ќ", U'К'); + _diacritics_map_add(U"ќ", U'к'); + _diacritics_map_add(U"Ӧ", U'О'); + _diacritics_map_add(U"ӧ", U'о'); + _diacritics_map_add(U"Ӫ", U'Ө'); + _diacritics_map_add(U"ӫ", U'ө'); + _diacritics_map_add(U"Ӭ", U'Э'); + _diacritics_map_add(U"ӭ", U'э'); + _diacritics_map_add(U"ЎӮӰӲ", U'У'); + _diacritics_map_add(U"ўӯӱӳ", U'у'); + _diacritics_map_add(U"Ӵ", U'Ч'); + _diacritics_map_add(U"ӵ", U'ч'); + _diacritics_map_add(U"Ӹ", U'Ы'); + _diacritics_map_add(U"ӹ", U'ы'); +} + +String TextServer::strip_diacritics(const String &p_string) const { + String result; + for (int i = 0; i < p_string.length(); i++) { + if (p_string[i] < 0x02B0 || p_string[i] > 0x036F) { // Skip combining diacritics. + if (diacritics_map.has(p_string[i])) { + result += diacritics_map[p_string[i]]; + } else { + result += p_string[i]; + } + } + } + return result; +} + +Array TextServer::parse_structured_text(StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const { + Array 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(Vector2i(i, i + 1)); + prev = i + 1; + } + } + if (prev != p_text.length()) { + ret.push_back(Vector2i(prev, p_text.length())); + } + } break; + case STRUCTURED_TEXT_FILE: { + int prev = 0; + 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(Vector2i(i, i + 1)); + prev = i + 1; + } + } + if (prev != p_text.length()) { + ret.push_back(Vector2i(prev, p_text.length())); + } + } break; + case STRUCTURED_TEXT_EMAIL: { + bool local = true; + int prev = 0; + 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)); + 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(Vector2i(i, i + 1)); + prev = i + 1; + } + } + if (prev != p_text.length()) { + ret.push_back(Vector2i(prev, p_text.length())); + } + } break; + case STRUCTURED_TEXT_LIST: { + if (p_args.size() == 1 && p_args[0].get_type() == Variant::STRING) { + Vector<String> tags = p_text.split(String(p_args[0])); + 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(Vector2i(prev + tags[i].length(), prev + tags[i].length() + 1)); + prev = prev + tags[i].length() + 1; + } + } + } break; + case STRUCTURED_TEXT_CUSTOM: + case STRUCTURED_TEXT_NONE: + case STRUCTURED_TEXT_DEFAULT: + default: { + ret.push_back(Vector2i(0, p_text.length())); + } } - shaped_text_set_bidi_override(p_shaped, overrides); + return ret; } -Array TextServer::_shaped_text_get_glyphs(RID p_shaped) const { +Array TextServer::_shaped_text_get_glyphs_wrapper(const RID &p_shaped) const { Array ret; - Vector<Glyph> glyphs = shaped_text_get_glyphs(p_shaped); - for (int i = 0; i < glyphs.size(); i++) { + const Glyph *glyphs = shaped_text_get_glyphs(p_shaped); + int gl_size = shaped_text_get_glyph_count(p_shaped); + for (int i = 0; i < gl_size; i++) { Dictionary glyph; glyph["start"] = glyphs[i].start; @@ -1416,66 +1687,78 @@ Array TextServer::_shaped_text_get_glyphs(RID p_shaped) const { return ret; } -Array TextServer::_shaped_text_get_line_breaks_adv(RID p_shaped, const PackedFloat32Array &p_width, int p_start, bool p_once, uint8_t p_break_flags) const { +Array TextServer::_shaped_text_sort_logical_wrapper(const RID &p_shaped) { Array ret; - Vector<Vector2i> lines = shaped_text_get_line_breaks_adv(p_shaped, p_width, p_start, p_once, p_break_flags); - for (int i = 0; i < lines.size(); i++) { - ret.push_back(lines[i]); - } - - return ret; -} + const Glyph *glyphs = shaped_text_sort_logical(p_shaped); + int gl_size = shaped_text_get_glyph_count(p_shaped); + for (int i = 0; i < gl_size; i++) { + Dictionary glyph; -Array TextServer::_shaped_text_get_line_breaks(RID p_shaped, real_t p_width, int p_start, uint8_t p_break_flags) const { - Array ret; + glyph["start"] = glyphs[i].start; + glyph["end"] = glyphs[i].end; + glyph["repeat"] = glyphs[i].repeat; + glyph["count"] = glyphs[i].count; + glyph["flags"] = glyphs[i].flags; + glyph["offset"] = Vector2(glyphs[i].x_off, glyphs[i].y_off); + glyph["advance"] = glyphs[i].advance; + glyph["font_rid"] = glyphs[i].font_rid; + glyph["font_size"] = glyphs[i].font_size; + glyph["index"] = glyphs[i].index; - Vector<Vector2i> lines = shaped_text_get_line_breaks(p_shaped, p_width, p_start, p_break_flags); - for (int i = 0; i < lines.size(); i++) { - ret.push_back(lines[i]); + ret.push_back(glyph); } return ret; } -Array TextServer::_shaped_text_get_word_breaks(RID p_shaped) const { +Array TextServer::_shaped_text_get_ellipsis_glyphs_wrapper(const RID &p_shaped) const { Array ret; - Vector<Vector2i> words = shaped_text_get_word_breaks(p_shaped); - for (int i = 0; i < words.size(); i++) { - ret.push_back(words[i]); - } - - return ret; -} - -Dictionary TextServer::_shaped_text_get_carets(RID p_shaped, int p_position) const { - Dictionary ret; + const Glyph *glyphs = shaped_text_get_ellipsis_glyphs(p_shaped); + int gl_size = shaped_text_get_ellipsis_glyph_count(p_shaped); + for (int i = 0; i < gl_size; i++) { + Dictionary glyph; - Rect2 l_caret, t_caret; - Direction l_dir, t_dir; - shaped_text_get_carets(p_shaped, p_position, l_caret, l_dir, t_caret, t_dir); + glyph["start"] = glyphs[i].start; + glyph["end"] = glyphs[i].end; + glyph["repeat"] = glyphs[i].repeat; + glyph["count"] = glyphs[i].count; + glyph["flags"] = glyphs[i].flags; + glyph["offset"] = Vector2(glyphs[i].x_off, glyphs[i].y_off); + glyph["advance"] = glyphs[i].advance; + glyph["font_rid"] = glyphs[i].font_rid; + glyph["font_size"] = glyphs[i].font_size; + glyph["index"] = glyphs[i].index; - ret["leading_rect"] = l_caret; - ret["leading_direction"] = l_dir; - ret["trailing_rect"] = t_caret; - ret["trailing_direction"] = t_dir; + ret.push_back(glyph); + } return ret; } -Array TextServer::_shaped_text_get_selection(RID p_shaped, int p_start, int p_end) const { - Array ret; +bool TextServer::is_valid_identifier(const String &p_string) const { + const char32_t *str = p_string.ptr(); + int len = p_string.length(); - Vector<Vector2> ranges = shaped_text_get_selection(p_shaped, p_start, p_end); - for (int i = 0; i < ranges.size(); i++) { - ret.push_back(ranges[i]); + if (len == 0) { + return false; // Empty string. } - return ret; + 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(); } TextServer::~TextServer() { |