diff options
Diffstat (limited to 'scene')
39 files changed, 1773 insertions, 2385 deletions
diff --git a/scene/3d/label_3d.cpp b/scene/3d/label_3d.cpp index 1ae59451a1..0e5771bdb1 100644 --- a/scene/3d/label_3d.cpp +++ b/scene/3d/label_3d.cpp @@ -53,10 +53,6 @@ void Label3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &Label3D::set_text_direction); ClassDB::bind_method(D_METHOD("get_text_direction"), &Label3D::get_text_direction); - ClassDB::bind_method(D_METHOD("set_opentype_feature", "tag", "value"), &Label3D::set_opentype_feature); - ClassDB::bind_method(D_METHOD("get_opentype_feature", "tag"), &Label3D::get_opentype_feature); - ClassDB::bind_method(D_METHOD("clear_opentype_features"), &Label3D::clear_opentype_features); - ClassDB::bind_method(D_METHOD("set_language", "language"), &Label3D::set_language); ClassDB::bind_method(D_METHOD("get_language"), &Label3D::get_language); @@ -140,7 +136,7 @@ void Label3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::COLOR, "outline_modulate"), "set_outline_modulate", "get_outline_modulate"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, ""), "set_text", "get_text"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_font", "get_font"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "font_size", PROPERTY_HINT_RANGE, "1,127,1,suffix:px"), "set_font_size", "get_font_size"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "font_size", PROPERTY_HINT_RANGE, "1,256,1,or_greater,suffix:px"), "set_font_size", "get_font_size"); ADD_PROPERTY(PropertyInfo(Variant::INT, "outline_size", PROPERTY_HINT_RANGE, "0,127,1,suffix:px"), "set_outline_size", "get_outline_size"); ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment"); ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_alignment", PROPERTY_HINT_ENUM, "Top,Center,Bottom"), "set_vertical_alignment", "get_vertical_alignment"); @@ -148,12 +144,12 @@ void Label3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "line_spacing", PROPERTY_HINT_NONE, "suffix:px"), "set_line_spacing", "get_line_spacing"); ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "width", PROPERTY_HINT_NONE, "suffix:px"), "set_width", "get_width"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options"); - ADD_GROUP("Locale", ""); + ADD_GROUP("BiDi", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left"), "set_text_direction", "get_text_direction"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options"); BIND_ENUM_CONSTANT(FLAG_SHADED); BIND_ENUM_CONSTANT(FLAG_DOUBLE_SIDED); @@ -166,56 +162,6 @@ void Label3D::_bind_methods() { BIND_ENUM_CONSTANT(ALPHA_CUT_OPAQUE_PREPASS); } -bool Label3D::_set(const StringName &p_name, const Variant &p_value) { - String str = p_name; - if (str.begins_with("opentype_features/")) { - String name = str.get_slicec('/', 1); - int32_t tag = TS->name_to_tag(name); - int value = p_value; - if (value == -1) { - if (opentype_features.has(tag)) { - opentype_features.erase(tag); - dirty_font = true; - _queue_update(); - } - } else { - if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) { - opentype_features[tag] = value; - dirty_font = true; - _queue_update(); - } - } - notify_property_list_changed(); - return true; - } - - return false; -} - -bool Label3D::_get(const StringName &p_name, Variant &r_ret) const { - String str = p_name; - if (str.begins_with("opentype_features/")) { - String name = str.get_slicec('/', 1); - int32_t tag = TS->name_to_tag(name); - if (opentype_features.has(tag)) { - r_ret = opentype_features[tag]; - return true; - } else { - r_ret = -1; - return true; - } - } - return false; -} - -void Label3D::_get_property_list(List<PropertyInfo> *p_list) const { - for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) { - String name = TS->tag_to_name(*ftr); - p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name)); - } - p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); -} - void Label3D::_validate_property(PropertyInfo &property) const { if (property.name == "material_override" || property.name == "material_overlay") { property.usage = PROPERTY_USAGE_NO_EDITOR; @@ -280,7 +226,7 @@ Ref<TriangleMesh> Label3D::generate_triangle_mesh() const { float total_h = 0.0; float max_line_w = 0.0; for (int i = 0; i < lines_rid.size(); i++) { - total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing; + total_h += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing; max_line_w = MAX(max_line_w, TS->shaped_text_get_width(lines_rid[i])); } @@ -471,7 +417,10 @@ void Label3D::_shape() { TS->shaped_text_set_direction(text_rid, text_direction); String text = (uppercase) ? TS->string_to_upper(xl_text, language) : xl_text; - TS->shaped_text_add_string(text_rid, text, font->get_rids(), font_size, opentype_features, language); + TS->shaped_text_add_string(text_rid, text, font->get_rids(), font_size, font->get_opentype_features(), language); + for (int i = 0; i < TextServer::SPACING_MAX; i++) { + TS->shaped_text_set_spacing(text_rid, TextServer::SpacingType(i), font->get_spacing(TextServer::SpacingType(i))); + } Array stt; if (st_parser == TextServer::STRUCTURED_TEXT_CUSTOM) { @@ -487,7 +436,10 @@ void Label3D::_shape() { } else if (dirty_font) { int spans = TS->shaped_get_span_count(text_rid); for (int i = 0; i < spans; i++) { - TS->shaped_set_span_update_font(text_rid, i, font->get_rids(), font_size, opentype_features); + TS->shaped_set_span_update_font(text_rid, i, font->get_rids(), font_size, font->get_opentype_features()); + } + for (int i = 0; i < TextServer::SPACING_MAX; i++) { + TS->shaped_text_set_spacing(text_rid, TextServer::SpacingType(i), font->get_spacing(TextServer::SpacingType(i))); } dirty_font = false; @@ -534,7 +486,7 @@ void Label3D::_shape() { // Generate surfaces and materials. float total_h = 0.0; for (int i = 0; i < lines_rid.size(); i++) { - total_h += (TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing) * pixel_size; + total_h += (TS->shaped_text_get_size(lines_rid[i]).y + line_spacing) * pixel_size; } float vbegin = 0.0; @@ -570,7 +522,7 @@ void Label3D::_shape() { } break; } offset.x += lbl_offset.x * pixel_size; - offset.y -= (TS->shaped_text_get_ascent(lines_rid[i]) + font->get_spacing(TextServer::SPACING_TOP)) * pixel_size; + offset.y -= TS->shaped_text_get_ascent(lines_rid[i]) * pixel_size; if (outline_modulate.a != 0.0 && outline_size > 0) { // Outline surfaces. @@ -584,7 +536,7 @@ void Label3D::_shape() { for (int j = 0; j < gl_size; j++) { _generate_glyph_surfaces(glyphs[j], offset, modulate, render_priority); } - offset.y -= (TS->shaped_text_get_descent(lines_rid[i]) + line_spacing + font->get_spacing(TextServer::SPACING_BOTTOM)) * pixel_size; + offset.y -= (TS->shaped_text_get_descent(lines_rid[i]) + line_spacing) * pixel_size; } for (const KeyValue<SurfaceKey, SurfaceData> &E : surfaces) { @@ -657,29 +609,6 @@ TextServer::Direction Label3D::get_text_direction() const { return text_direction; } -void Label3D::clear_opentype_features() { - opentype_features.clear(); - dirty_font = true; - _queue_update(); -} - -void Label3D::set_opentype_feature(const String &p_name, int p_value) { - int32_t tag = TS->name_to_tag(p_name); - if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) { - opentype_features[tag] = p_value; - dirty_font = true; - _queue_update(); - } -} - -int Label3D::get_opentype_feature(const String &p_name) const { - int32_t tag = TS->name_to_tag(p_name); - if (!opentype_features.has(tag)) { - return -1; - } - return opentype_features[tag]; -} - void Label3D::set_language(const String &p_language) { if (language != p_language) { language = p_language; @@ -781,7 +710,7 @@ Ref<Font> Label3D::_get_font_or_default() const { theme_font.unref(); } - if (font_override.is_valid() && font_override->get_data_count() > 0) { + if (font_override.is_valid()) { return font_override; } diff --git a/scene/3d/label_3d.h b/scene/3d/label_3d.h index 7ac38a9f3c..4498e89517 100644 --- a/scene/3d/label_3d.h +++ b/scene/3d/label_3d.h @@ -122,7 +122,6 @@ private: float line_spacing = 0.f; - Dictionary opentype_features; String language; TextServer::Direction text_direction = TextServer::DIRECTION_AUTO; TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT; @@ -150,9 +149,6 @@ protected: static void _bind_methods(); - bool _set(const StringName &p_name, const Variant &p_value); - bool _get(const StringName &p_name, Variant &r_ret) const; - void _get_property_list(List<PropertyInfo> *p_list) const; void _validate_property(PropertyInfo &property) const override; void _im_update(); @@ -180,10 +176,6 @@ public: void set_text_direction(TextServer::Direction p_text_direction); TextServer::Direction get_text_direction() const; - void set_opentype_feature(const String &p_name, int p_value); - int get_opentype_feature(const String &p_name) const; - void clear_opentype_features(); - void set_language(const String &p_language); String get_language() const; diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index 1371c9cd57..d23a4fff43 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -342,12 +342,7 @@ void Button::_notification(int p_what) { } break; } - Color font_outline_color = get_theme_color(SNAME("font_outline_color")); - int outline_size = get_theme_constant(SNAME("outline_size")); - if (outline_size > 0 && font_outline_color.a > 0) { - text_buf->draw_outline(ci, text_ofs, outline_size, font_outline_color); - } - + text_buf->draw_outline(ci, text_ofs, get_theme_constant(SNAME("outline_size")), get_theme_color(SNAME("font_outline_color"))); text_buf->draw(ci, text_ofs, color); } break; } @@ -363,7 +358,7 @@ void Button::_shape() { } else { text_buf->set_direction((TextServer::Direction)text_direction); } - text_buf->add_string(xl_text, font, font_size, opentype_features, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale()); + text_buf->add_string(xl_text, font, font_size, language); text_buf->set_text_overrun_behavior(overrun_behavior); } @@ -409,29 +404,6 @@ Control::TextDirection Button::get_text_direction() const { return text_direction; } -void Button::clear_opentype_features() { - opentype_features.clear(); - _shape(); - update(); -} - -void Button::set_opentype_feature(const String &p_name, int p_value) { - int32_t tag = TS->name_to_tag(p_name); - if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) { - opentype_features[tag] = p_value; - _shape(); - update(); - } -} - -int Button::get_opentype_feature(const String &p_name) const { - int32_t tag = TS->name_to_tag(p_name); - if (!opentype_features.has(tag)) { - return -1; - } - return opentype_features[tag]; -} - void Button::set_language(const String &p_language) { if (language != p_language) { language = p_language; @@ -512,56 +484,6 @@ HorizontalAlignment Button::get_icon_alignment() const { return icon_alignment; } -bool Button::_set(const StringName &p_name, const Variant &p_value) { - String str = p_name; - if (str.begins_with("opentype_features/")) { - String name = str.get_slicec('/', 1); - int32_t tag = TS->name_to_tag(name); - int value = p_value; - if (value == -1) { - if (opentype_features.has(tag)) { - opentype_features.erase(tag); - _shape(); - update(); - } - } else { - if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) { - opentype_features[tag] = value; - _shape(); - update(); - } - } - notify_property_list_changed(); - return true; - } - - return false; -} - -bool Button::_get(const StringName &p_name, Variant &r_ret) const { - String str = p_name; - if (str.begins_with("opentype_features/")) { - String name = str.get_slicec('/', 1); - int32_t tag = TS->name_to_tag(name); - if (opentype_features.has(tag)) { - r_ret = opentype_features[tag]; - return true; - } else { - r_ret = -1; - return true; - } - } - return false; -} - -void Button::_get_property_list(List<PropertyInfo> *p_list) const { - for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) { - String name = TS->tag_to_name(*ftr); - p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name)); - } - p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); -} - void Button::_bind_methods() { ClassDB::bind_method(D_METHOD("set_text", "text"), &Button::set_text); ClassDB::bind_method(D_METHOD("get_text"), &Button::get_text); @@ -569,9 +491,6 @@ void Button::_bind_methods() { ClassDB::bind_method(D_METHOD("get_text_overrun_behavior"), &Button::get_text_overrun_behavior); ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &Button::set_text_direction); ClassDB::bind_method(D_METHOD("get_text_direction"), &Button::get_text_direction); - ClassDB::bind_method(D_METHOD("set_opentype_feature", "tag", "value"), &Button::set_opentype_feature); - ClassDB::bind_method(D_METHOD("get_opentype_feature", "tag"), &Button::get_opentype_feature); - ClassDB::bind_method(D_METHOD("clear_opentype_features"), &Button::clear_opentype_features); ClassDB::bind_method(D_METHOD("set_language", "language"), &Button::set_language); ClassDB::bind_method(D_METHOD("get_language"), &Button::get_language); ClassDB::bind_method(D_METHOD("set_button_icon", "texture"), &Button::set_icon); @@ -588,8 +507,6 @@ void Button::_bind_methods() { ClassDB::bind_method(D_METHOD("is_expand_icon"), &Button::is_expand_icon); ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_button_icon", "get_button_icon"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "get_clip_text"); @@ -597,6 +514,10 @@ void Button::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis,Word Ellipsis"), "set_text_overrun_behavior", "get_text_overrun_behavior"); ADD_PROPERTY(PropertyInfo(Variant::INT, "icon_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_icon_alignment", "get_icon_alignment"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_icon"), "set_expand_icon", "is_expand_icon"); + + ADD_GROUP("BiDi", ""); + ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); } Button::Button(const String &p_text) { diff --git a/scene/gui/button.h b/scene/gui/button.h index a1d71195cb..7a29cba677 100644 --- a/scene/gui/button.h +++ b/scene/gui/button.h @@ -43,7 +43,6 @@ private: String xl_text; Ref<TextParagraph> text_buf; - Dictionary opentype_features; String language; TextDirection text_direction = TEXT_DIRECTION_AUTO; TextServer::OverrunBehavior overrun_behavior = TextServer::OVERRUN_NO_TRIMMING; @@ -62,10 +61,6 @@ protected: void _notification(int p_what); static void _bind_methods(); - bool _set(const StringName &p_name, const Variant &p_value); - bool _get(const StringName &p_name, Variant &r_ret) const; - void _get_property_list(List<PropertyInfo> *p_list) const; - public: virtual Size2 get_minimum_size() const override; @@ -78,10 +73,6 @@ public: void set_text_direction(TextDirection p_text_direction); TextDirection get_text_direction() const; - void set_opentype_feature(const String &p_name, int p_value); - int get_opentype_feature(const String &p_name) const; - void clear_opentype_features(); - void set_language(const String &p_language); String get_language() const; diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index a710e924f2..22f968eac7 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -46,7 +46,7 @@ void CodeEdit::_notification(int p_what) { line_spacing = get_theme_constant(SNAME("line_spacing")); set_gutter_width(main_gutter, get_line_height()); - set_gutter_width(line_number_gutter, (line_number_digits + 1) * font->get_char_size('0', 0, font_size).width); + set_gutter_width(line_number_gutter, (line_number_digits + 1) * font->get_char_size('0', font_size).width); set_gutter_width(fold_gutter, get_line_height() / 1.2); breakpoint_color = get_theme_color(SNAME("breakpoint_color")); @@ -86,7 +86,7 @@ void CodeEdit::_notification(int p_what) { if (line_length_guideline_columns.size() > 0) { const int xmargin_beg = style_normal->get_margin(SIDE_LEFT) + get_total_gutter_width(); const int xmargin_end = size.width - style_normal->get_margin(SIDE_RIGHT) - (is_drawing_minimap() ? get_minimap_width() : 0); - const float char_size = font->get_char_size('0', 0, font_size).width; + const float char_size = font->get_char_size('0', font_size).width; for (int i = 0; i < line_length_guideline_columns.size(); i++) { const int xoffset = xmargin_beg + char_size * (int)line_length_guideline_columns[i] - get_h_scroll(); @@ -123,7 +123,7 @@ void CodeEdit::_notification(int p_what) { } const int scroll_width = code_completion_options_count > code_completion_max_lines ? code_completion_scroll_width : 0; - const int code_completion_base_width = font->get_string_size(code_completion_base, font_size).width; + const int code_completion_base_width = font->get_string_size(code_completion_base, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width; if (caret_pos.x - code_completion_base_width + code_completion_rect.size.width + scroll_width > get_size().width) { code_completion_rect.position.x = get_size().width - code_completion_rect.size.width - scroll_width; } else { @@ -178,8 +178,8 @@ void CodeEdit::_notification(int p_what) { for (int j = 0; j < code_completion_options[l].matches.size(); j++) { Pair<int, int> match = code_completion_options[l].matches[j]; - int match_offset = font->get_string_size(code_completion_options[l].display.substr(0, match.first), font_size).width; - int match_len = font->get_string_size(code_completion_options[l].display.substr(match.first, match.second), font_size).width; + int match_offset = font->get_string_size(code_completion_options[l].display.substr(0, match.first), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width; + int match_len = font->get_string_size(code_completion_options[l].display.substr(match.first, match.second), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width; draw_rect(Rect2(match_pos + Point2(match_offset, 0), Size2(match_len, row_height)), code_completion_existing_color); } @@ -208,11 +208,11 @@ void CodeEdit::_notification(int p_what) { int max_width = 0; for (int i = 0; i < line_count; i++) { - max_width = MAX(max_width, font->get_string_size(code_hint_lines[i], font_size).x); + max_width = MAX(max_width, font->get_string_size(code_hint_lines[i], HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x); } Size2 minsize = sb->get_minimum_size() + Size2(max_width, line_count * font_height + (line_spacing * line_count - 1)); - int offset = font->get_string_size(code_hint_lines[0].substr(0, code_hint_lines[0].find(String::chr(0xFFFF))), font_size).x; + int offset = font->get_string_size(code_hint_lines[0].substr(0, code_hint_lines[0].find(String::chr(0xFFFF))), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x; if (code_hint_xpos == -0xFFFF) { code_hint_xpos = get_caret_draw_pos().x - offset; } @@ -232,8 +232,8 @@ void CodeEdit::_notification(int p_what) { int begin = 0; int end = 0; if (line.contains(String::chr(0xFFFF))) { - begin = font->get_string_size(line.substr(0, line.find(String::chr(0xFFFF))), font_size).x; - end = font->get_string_size(line.substr(0, line.rfind(String::chr(0xFFFF))), font_size).x; + begin = font->get_string_size(line.substr(0, line.find(String::chr(0xFFFF))), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x; + end = font->get_string_size(line.substr(0, line.rfind(String::chr(0xFFFF))), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x; } Point2 round_ofs = hint_ofs + sb->get_offset() + Vector2(0, font->get_ascent(font_size) + font_height * i + yofs); @@ -2795,7 +2795,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() { offset = line_height; } - max_width = MAX(max_width, font->get_string_size(option.display, font_size).width + offset); + max_width = MAX(max_width, font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width + offset); code_completion_options.push_back(option); } @@ -2906,7 +2906,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() { if (string_to_complete.length() == 0) { code_completion_options.push_back(option); - max_width = MAX(max_width, font->get_string_size(option.display, font_size).width + offset); + max_width = MAX(max_width, font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width + offset); continue; } @@ -3012,7 +3012,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() { option.matches.append_array(ssq_matches); completion_options_subseq.push_back(option); } - max_width = MAX(max_width, font->get_string_size(option.display, font_size).width + offset); + max_width = MAX(max_width, font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width + offset); } else if (!*ssq_lower) { // Matched the whole subsequence in s_lower. option.matches.clear(); @@ -3029,7 +3029,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() { option.matches.append_array(ssq_lower_matches); completion_options_subseq_casei.push_back(option); } - max_width = MAX(max_width, font->get_string_size(option.display, font_size).width + offset); + max_width = MAX(max_width, font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width + offset); } } @@ -3085,7 +3085,7 @@ void CodeEdit::_text_changed() { } if (font.is_valid()) { - set_gutter_width(line_number_gutter, (line_number_digits + 1) * font->get_char_size('0', 0, font_size).width); + set_gutter_width(line_number_gutter, (line_number_digits + 1) * font->get_char_size('0', font_size).width); } lc = get_line_count(); diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 6e2db68db0..15ff1d3ed6 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -1115,7 +1115,7 @@ Ref<StyleBox> Control::get_theme_stylebox(const StringName &p_name, const String Ref<Font> Control::get_theme_font(const StringName &p_name, const StringName &p_theme_type) const { if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { const Ref<Font> *font = data.font_override.getptr(p_name); - if (font && (*font)->get_data_count() > 0) { + if (font) { return *font; } } diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp index 87706cd0d7..92016ca42e 100644 --- a/scene/gui/graph_node.cpp +++ b/scene/gui/graph_node.cpp @@ -44,26 +44,6 @@ struct _MinSizeCache { bool GraphNode::_set(const StringName &p_name, const Variant &p_value) { String str = p_name; - if (str.begins_with("opentype_features/")) { - String name = str.get_slicec('/', 1); - int32_t tag = TS->name_to_tag(name); - int value = p_value; - if (value == -1) { - if (opentype_features.has(tag)) { - opentype_features.erase(tag); - _shape(); - update(); - } - } else { - if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) { - opentype_features[tag] = value; - _shape(); - update(); - } - } - notify_property_list_changed(); - return true; - } if (!str.begins_with("slot/")) { return false; @@ -106,18 +86,6 @@ bool GraphNode::_set(const StringName &p_name, const Variant &p_value) { bool GraphNode::_get(const StringName &p_name, Variant &r_ret) const { String str = p_name; - if (str.begins_with("opentype_features/")) { - String name = str.get_slicec('/', 1); - int32_t tag = TS->name_to_tag(name); - if (opentype_features.has(tag)) { - r_ret = opentype_features[tag]; - return true; - } else { - r_ret = -1; - return true; - } - } - if (!str.begins_with("slot/")) { return false; } @@ -156,12 +124,6 @@ bool GraphNode::_get(const StringName &p_name, Variant &r_ret) const { } void GraphNode::_get_property_list(List<PropertyInfo> *p_list) const { - for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) { - String name = TS->tag_to_name(*ftr); - p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name)); - } - p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); - int idx = 0; for (int i = 0; i < get_child_count(); i++) { Control *c = Object::cast_to<Control>(get_child(i)); @@ -471,7 +433,7 @@ void GraphNode::_shape() { } else { title_buf->set_direction((TextServer::Direction)text_direction); } - title_buf->add_string(title, font, font_size, opentype_features, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale()); + title_buf->add_string(title, font, font_size, language); } #ifdef TOOLS_ENABLED @@ -726,29 +688,6 @@ Control::TextDirection GraphNode::get_text_direction() const { return text_direction; } -void GraphNode::clear_opentype_features() { - opentype_features.clear(); - _shape(); - update(); -} - -void GraphNode::set_opentype_feature(const String &p_name, int p_value) { - int32_t tag = TS->name_to_tag(p_name); - if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) { - opentype_features[tag] = p_value; - _shape(); - update(); - } -} - -int GraphNode::get_opentype_feature(const String &p_name) const { - int32_t tag = TS->name_to_tag(p_name); - if (!opentype_features.has(tag)) { - return -1; - } - return opentype_features[tag]; -} - void GraphNode::set_language(const String &p_language) { if (language != p_language) { language = p_language; @@ -1043,9 +982,6 @@ void GraphNode::_bind_methods() { ClassDB::bind_method(D_METHOD("get_title"), &GraphNode::get_title); ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &GraphNode::set_text_direction); ClassDB::bind_method(D_METHOD("get_text_direction"), &GraphNode::get_text_direction); - ClassDB::bind_method(D_METHOD("set_opentype_feature", "tag", "value"), &GraphNode::set_opentype_feature); - ClassDB::bind_method(D_METHOD("get_opentype_feature", "tag"), &GraphNode::get_opentype_feature); - ClassDB::bind_method(D_METHOD("clear_opentype_features"), &GraphNode::clear_opentype_features); ClassDB::bind_method(D_METHOD("set_language", "language"), &GraphNode::set_language); ClassDB::bind_method(D_METHOD("get_language"), &GraphNode::get_language); @@ -1105,8 +1041,6 @@ void GraphNode::_bind_methods() { ClassDB::bind_method(D_METHOD("get_overlay"), &GraphNode::get_overlay); ADD_PROPERTY(PropertyInfo(Variant::STRING, "title"), "set_title", "get_title"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_position_offset", "get_position_offset"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_close"), "set_show_close_button", "is_close_button_visible"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "resizable"), "set_resizable", "is_resizable"); @@ -1114,6 +1048,10 @@ void GraphNode::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "comment"), "set_comment", "is_comment"); ADD_PROPERTY(PropertyInfo(Variant::INT, "overlay", PROPERTY_HINT_ENUM, "Disabled,Breakpoint,Position"), "set_overlay", "get_overlay"); + ADD_GROUP("BiDi", ""); + ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); + ADD_SIGNAL(MethodInfo("position_offset_changed")); ADD_SIGNAL(MethodInfo("slot_updated", PropertyInfo(Variant::INT, "idx"))); ADD_SIGNAL(MethodInfo("dragged", PropertyInfo(Variant::VECTOR2, "from"), PropertyInfo(Variant::VECTOR2, "to"))); diff --git a/scene/gui/graph_node.h b/scene/gui/graph_node.h index f6c943dc89..0651eb5cc9 100644 --- a/scene/gui/graph_node.h +++ b/scene/gui/graph_node.h @@ -60,7 +60,6 @@ private: String title; Ref<TextLine> title_buf; - Dictionary opentype_features; String language; TextDirection text_direction = TEXT_DIRECTION_AUTO; @@ -148,10 +147,6 @@ public: void set_text_direction(TextDirection p_text_direction); TextDirection get_text_direction() const; - void set_opentype_feature(const String &p_name, int p_value); - int get_opentype_feature(const String &p_name) const; - void clear_opentype_features(); - void set_language(const String &p_language); String get_language() const; diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index aeb5338022..1d4ca4d196 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -43,7 +43,7 @@ void ItemList::_shape(int p_idx) { } else { item.text_buf->set_direction((TextServer::Direction)item.text_direction); } - item.text_buf->add_string(item.text, get_theme_font(SNAME("font")), get_theme_font_size(SNAME("font_size")), item.opentype_features, (!item.language.is_empty()) ? item.language : TranslationServer::get_singleton()->get_tool_locale()); + item.text_buf->add_string(item.text, get_theme_font(SNAME("font")), get_theme_font_size(SNAME("font_size")), item.language); if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) { item.text_buf->set_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND); } else { @@ -117,35 +117,6 @@ Control::TextDirection ItemList::get_item_text_direction(int p_idx) const { return items[p_idx].text_direction; } -void ItemList::clear_item_opentype_features(int p_idx) { - ERR_FAIL_INDEX(p_idx, items.size()); - items.write[p_idx].opentype_features.clear(); - _shape(p_idx); - update(); -} - -void ItemList::set_item_opentype_feature(int p_idx, const String &p_name, int p_value) { - if (p_idx < 0) { - p_idx += get_item_count(); - } - ERR_FAIL_INDEX(p_idx, items.size()); - int32_t tag = TS->name_to_tag(p_name); - if (!items[p_idx].opentype_features.has(tag) || (int)items[p_idx].opentype_features[tag] != p_value) { - items.write[p_idx].opentype_features[tag] = p_value; - _shape(p_idx); - update(); - } -} - -int ItemList::get_item_opentype_feature(int p_idx, const String &p_name) const { - ERR_FAIL_INDEX_V(p_idx, items.size(), -1); - int32_t tag = TS->name_to_tag(p_name); - if (!items[p_idx].opentype_features.has(tag)) { - return -1; - } - return items[p_idx].opentype_features[tag]; -} - void ItemList::set_item_language(int p_idx, const String &p_language) { if (p_idx < 0) { p_idx += get_item_count(); @@ -1656,10 +1627,6 @@ void ItemList::_bind_methods() { ClassDB::bind_method(D_METHOD("set_item_text_direction", "idx", "direction"), &ItemList::set_item_text_direction); ClassDB::bind_method(D_METHOD("get_item_text_direction", "idx"), &ItemList::get_item_text_direction); - ClassDB::bind_method(D_METHOD("set_item_opentype_feature", "idx", "tag", "value"), &ItemList::set_item_opentype_feature); - ClassDB::bind_method(D_METHOD("get_item_opentype_feature", "idx", "tag"), &ItemList::get_item_opentype_feature); - ClassDB::bind_method(D_METHOD("clear_item_opentype_features", "idx"), &ItemList::clear_item_opentype_features); - ClassDB::bind_method(D_METHOD("set_item_language", "idx", "language"), &ItemList::set_item_language); ClassDB::bind_method(D_METHOD("get_item_language", "idx"), &ItemList::get_item_language); diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h index a15b090149..c7d87da0b5 100644 --- a/scene/gui/item_list.h +++ b/scene/gui/item_list.h @@ -58,7 +58,6 @@ private: Ref<Texture2D> tag_icon; String text; Ref<TextParagraph> text_buf; - Dictionary opentype_features; String language; TextDirection text_direction = TEXT_DIRECTION_AUTO; @@ -145,10 +144,6 @@ public: void set_item_text_direction(int p_idx, TextDirection p_text_direction); TextDirection get_item_text_direction(int p_idx) const; - void set_item_opentype_feature(int p_idx, const String &p_name, int p_value); - int get_item_opentype_feature(int p_idx, const String &p_name) const; - void clear_item_opentype_features(int p_idx); - void set_item_language(int p_idx, const String &p_language); String get_item_language(int p_idx) const; diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index 82ab7c2e18..5dec1df4a5 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -66,11 +66,11 @@ bool Label::is_uppercase() const { int Label::get_line_height(int p_line) const { Ref<Font> font = get_theme_font(SNAME("font")); if (p_line >= 0 && p_line < lines_rid.size()) { - return TS->shaped_text_get_size(lines_rid[p_line]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM); + return TS->shaped_text_get_size(lines_rid[p_line]).y; } else if (lines_rid.size() > 0) { int h = 0; for (int i = 0; i < lines_rid.size(); i++) { - h = MAX(h, TS->shaped_text_get_size(lines_rid[i]).y) + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM); + h = MAX(h, TS->shaped_text_get_size(lines_rid[i]).y); } return h; } else { @@ -83,7 +83,6 @@ void Label::_shape() { int width = (get_size().width - style->get_minimum_size().width); if (dirty || font_dirty) { - String lang = (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale(); if (dirty) { TS->shaped_text_clear(text_rid); } @@ -95,18 +94,21 @@ void Label::_shape() { const Ref<Font> &font = get_theme_font(SNAME("font")); int font_size = get_theme_font_size(SNAME("font_size")); ERR_FAIL_COND(font.is_null()); - String text = (uppercase) ? TS->string_to_upper(xl_text, lang) : xl_text; + String text = (uppercase) ? TS->string_to_upper(xl_text, language) : xl_text; if (visible_chars >= 0 && visible_chars_behavior == TextServer::VC_CHARS_BEFORE_SHAPING) { text = text.substr(0, visible_chars); } if (dirty) { - TS->shaped_text_add_string(text_rid, text, font->get_rids(), font_size, opentype_features, lang); + TS->shaped_text_add_string(text_rid, text, font->get_rids(), font_size, font->get_opentype_features(), language); } else { int spans = TS->shaped_get_span_count(text_rid); for (int i = 0; i < spans; i++) { - TS->shaped_set_span_update_font(text_rid, i, font->get_rids(), font_size, opentype_features); + TS->shaped_set_span_update_font(text_rid, i, font->get_rids(), font_size, font->get_opentype_features()); } } + for (int i = 0; i < TextServer::SPACING_MAX; i++) { + TS->shaped_text_set_spacing(text_rid, TextServer::SpacingType(i), font->get_spacing(TextServer::SpacingType(i))); + } TS->shaped_text_set_bidi_override(text_rid, structured_text_parser(st_parser, st_args, text)); dirty = false; font_dirty = false; @@ -233,7 +235,7 @@ void Label::_update_visible() { minsize.height = 0; int last_line = MIN(lines_rid.size(), lines_visible + lines_skipped); for (int64_t i = lines_skipped; i < last_line; i++) { - minsize.height += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing; + minsize.height += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing; if (minsize.height > (get_size().height - style->get_minimum_size().height + line_spacing)) { break; } @@ -314,7 +316,7 @@ void Label::_notification(int p_what) { // Get number of lines to fit to the height. for (int64_t i = lines_skipped; i < lines_rid.size(); i++) { - total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing; + total_h += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing; if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) { break; } @@ -334,7 +336,7 @@ void Label::_notification(int p_what) { int total_glyphs = 0; total_h = 0; for (int64_t i = lines_skipped; i < last_line; i++) { - total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing; + total_h += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing; total_glyphs += TS->shaped_text_get_glyph_count(lines_rid[i]) + TS->shaped_text_get_ellipsis_glyph_count(lines_rid[i]); } int visible_glyphs = total_glyphs * percent_visible; @@ -374,7 +376,7 @@ void Label::_notification(int p_what) { for (int i = lines_skipped; i < last_line; i++) { Size2 line_size = TS->shaped_text_get_size(lines_rid[i]); ofs.x = 0; - ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + font->get_spacing(TextServer::SPACING_TOP); + ofs.y += TS->shaped_text_get_ascent(lines_rid[i]); switch (horizontal_alignment) { case HORIZONTAL_ALIGNMENT_FILL: if (rtl && autowrap_mode != TextServer::AUTOWRAP_OFF) { @@ -527,7 +529,7 @@ void Label::_notification(int p_what) { } } } - ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + vsep + line_spacing + font->get_spacing(TextServer::SPACING_BOTTOM); + ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + vsep + line_spacing; } } break; @@ -551,7 +553,7 @@ Size2 Label::get_minimum_size() const { Size2 min_size = minsize; Ref<Font> font = get_theme_font(SNAME("font")); - min_size.height = MAX(min_size.height, font->get_height(get_theme_font_size(SNAME("font_size"))) + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM)); + min_size.height = MAX(min_size.height, font->get_height(get_theme_font_size(SNAME("font_size")))); Size2 min_style = get_theme_stylebox(SNAME("normal"))->get_minimum_size(); if (autowrap_mode != TextServer::AUTOWRAP_OFF) { @@ -582,7 +584,7 @@ int Label::get_visible_line_count() const { int lines_visible = 0; float total_h = 0.0; for (int64_t i = lines_skipped; i < lines_rid.size(); i++) { - total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing; + total_h += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing; if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) { break; } @@ -674,29 +676,6 @@ Control::TextDirection Label::get_text_direction() const { return text_direction; } -void Label::clear_opentype_features() { - opentype_features.clear(); - font_dirty = true; - update(); -} - -void Label::set_opentype_feature(const String &p_name, int p_value) { - int32_t tag = TS->name_to_tag(p_name); - if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) { - opentype_features[tag] = p_value; - font_dirty = true; - update(); - } -} - -int Label::get_opentype_feature(const String &p_name) const { - int32_t tag = TS->name_to_tag(p_name); - if (!opentype_features.has(tag)) { - return -1; - } - return opentype_features[tag]; -} - void Label::set_language(const String &p_language) { if (language != p_language) { language = p_language; @@ -818,56 +797,6 @@ int Label::get_total_character_count() const { return xl_text.length(); } -bool Label::_set(const StringName &p_name, const Variant &p_value) { - String str = p_name; - if (str.begins_with("opentype_features/")) { - String name = str.get_slicec('/', 1); - int32_t tag = TS->name_to_tag(name); - int value = p_value; - if (value == -1) { - if (opentype_features.has(tag)) { - opentype_features.erase(tag); - font_dirty = true; - update(); - } - } else { - if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) { - opentype_features[tag] = value; - font_dirty = true; - update(); - } - } - notify_property_list_changed(); - return true; - } - - return false; -} - -bool Label::_get(const StringName &p_name, Variant &r_ret) const { - String str = p_name; - if (str.begins_with("opentype_features/")) { - String name = str.get_slicec('/', 1); - int32_t tag = TS->name_to_tag(name); - if (opentype_features.has(tag)) { - r_ret = opentype_features[tag]; - return true; - } else { - r_ret = -1; - return true; - } - } - return false; -} - -void Label::_get_property_list(List<PropertyInfo> *p_list) const { - for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) { - String name = TS->tag_to_name(*ftr); - p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name)); - } - p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); -} - void Label::_bind_methods() { ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &Label::set_horizontal_alignment); ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &Label::get_horizontal_alignment); @@ -877,9 +806,6 @@ void Label::_bind_methods() { ClassDB::bind_method(D_METHOD("get_text"), &Label::get_text); ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &Label::set_text_direction); ClassDB::bind_method(D_METHOD("get_text_direction"), &Label::get_text_direction); - ClassDB::bind_method(D_METHOD("set_opentype_feature", "tag", "value"), &Label::set_opentype_feature); - ClassDB::bind_method(D_METHOD("get_opentype_feature", "tag"), &Label::get_opentype_feature); - ClassDB::bind_method(D_METHOD("clear_opentype_features"), &Label::clear_opentype_features); ClassDB::bind_method(D_METHOD("set_language", "language"), &Label::set_language); ClassDB::bind_method(D_METHOD("get_language"), &Label::get_language); ClassDB::bind_method(D_METHOD("set_autowrap_mode", "autowrap_mode"), &Label::set_autowrap_mode); @@ -924,11 +850,9 @@ void Label::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters_behavior", PROPERTY_HINT_ENUM, "Characters Before Shaping,Characters After Shaping,Glyphs (Layout Direction),Glyphs (Left-to-Right),Glyphs (Right-to-Left)"), "set_visible_characters_behavior", "get_visible_characters_behavior"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "percent_visible", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_percent_visible", "get_percent_visible"); - ADD_GROUP("Locale", ""); + ADD_GROUP("BiDi", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); - - ADD_GROUP("Structured Text", "structured_text_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options"); } diff --git a/scene/gui/label.h b/scene/gui/label.h index fac3d75a1b..a59d35950d 100644 --- a/scene/gui/label.h +++ b/scene/gui/label.h @@ -53,7 +53,6 @@ private: RID text_rid; Vector<RID> lines_rid; - Dictionary opentype_features; String language; TextDirection text_direction = TEXT_DIRECTION_AUTO; TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT; @@ -74,10 +73,6 @@ protected: static void _bind_methods(); - bool _set(const StringName &p_name, const Variant &p_value); - bool _get(const StringName &p_name, Variant &r_ret) const; - void _get_property_list(List<PropertyInfo> *p_list) const; - public: virtual Size2 get_minimum_size() const override; @@ -93,10 +88,6 @@ public: void set_text_direction(TextDirection p_text_direction); TextDirection get_text_direction() const; - void set_opentype_feature(const String &p_name, int p_value); - int get_opentype_feature(const String &p_name) const; - void clear_opentype_features(); - void set_language(const String &p_language); String get_language() const; diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 377a9c45c5..39f8f23cd8 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -787,7 +787,7 @@ void LineEdit::_notification(int p_what) { int x_ofs = 0; bool using_placeholder = text.is_empty() && ime_text.is_empty(); float text_width = TS->shaped_text_get_size(text_rid).x; - float text_height = TS->shaped_text_get_size(text_rid).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM); + float text_height = TS->shaped_text_get_size(text_rid).y; switch (alignment) { case HORIZONTAL_ALIGNMENT_FILL: @@ -1451,29 +1451,6 @@ Control::TextDirection LineEdit::get_text_direction() const { return text_direction; } -void LineEdit::clear_opentype_features() { - opentype_features.clear(); - _shape(); - update(); -} - -void LineEdit::set_opentype_feature(const String &p_name, int p_value) { - int32_t tag = TS->name_to_tag(p_name); - if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) { - opentype_features[tag] = p_value; - _shape(); - update(); - } -} - -int LineEdit::get_opentype_feature(const String &p_name) const { - int32_t tag = TS->name_to_tag(p_name); - if (!opentype_features.has(tag)) { - return -1; - } - return opentype_features[tag]; -} - void LineEdit::set_language(const String &p_language) { if (language != p_language) { language = p_language; @@ -1688,7 +1665,7 @@ Size2 LineEdit::get_minimum_size() const { Size2 min_size; // Minimum size of text. - float em_space_size = font->get_char_size('M', 0, font_size).x; + float em_space_size = font->get_char_size('M', font_size).x; min_size.width = get_theme_constant(SNAME("minimum_character_width")) * em_space_size; if (expand_to_text_length) { @@ -1696,7 +1673,7 @@ Size2 LineEdit::get_minimum_size() const { min_size.width = MAX(min_size.width, full_width + em_space_size); } - min_size.height = MAX(TS->shaped_text_get_size(text_rid).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM), font->get_height(font_size)); + min_size.height = MAX(TS->shaped_text_get_size(text_rid).y, font->get_height(font_size)); // Take icons into account. int icon_max_width = 0; @@ -2155,7 +2132,10 @@ void LineEdit::_shape() { const Ref<Font> &font = get_theme_font(SNAME("font")); int font_size = get_theme_font_size(SNAME("font_size")); ERR_FAIL_COND(font.is_null()); - TS->shaped_text_add_string(text_rid, t, font->get_rids(), font_size, opentype_features, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale()); + TS->shaped_text_add_string(text_rid, t, font->get_rids(), font_size, font->get_opentype_features(), language); + for (int i = 0; i < TextServer::SPACING_MAX; i++) { + TS->shaped_text_set_spacing(text_rid, TextServer::SpacingType(i), font->get_spacing(TextServer::SpacingType(i))); + } TS->shaped_text_set_bidi_override(text_rid, structured_text_parser(st_parser, st_args, t)); full_width = TS->shaped_text_get_size(text_rid).x; @@ -2236,56 +2216,6 @@ Key LineEdit::_get_menu_action_accelerator(const String &p_action) { } } -bool LineEdit::_set(const StringName &p_name, const Variant &p_value) { - String str = p_name; - if (str.begins_with("opentype_features/")) { - String name = str.get_slicec('/', 1); - int32_t tag = TS->name_to_tag(name); - int value = p_value; - if (value == -1) { - if (opentype_features.has(tag)) { - opentype_features.erase(tag); - _shape(); - update(); - } - } else { - if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) { - opentype_features[tag] = value; - _shape(); - update(); - } - } - notify_property_list_changed(); - return true; - } - - return false; -} - -bool LineEdit::_get(const StringName &p_name, Variant &r_ret) const { - String str = p_name; - if (str.begins_with("opentype_features/")) { - String name = str.get_slicec('/', 1); - int32_t tag = TS->name_to_tag(name); - if (opentype_features.has(tag)) { - r_ret = opentype_features[tag]; - return true; - } else { - r_ret = -1; - return true; - } - } - return false; -} - -void LineEdit::_get_property_list(List<PropertyInfo> *p_list) const { - for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) { - String name = TS->tag_to_name(*ftr); - p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name)); - } - p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); -} - void LineEdit::_validate_property(PropertyInfo &property) const { if (!caret_blink_enabled && property.name == "caret_blink_speed") { property.usage = PROPERTY_USAGE_NO_EDITOR; @@ -2311,9 +2241,6 @@ void LineEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_draw_control_chars", "enable"), &LineEdit::set_draw_control_chars); ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &LineEdit::set_text_direction); ClassDB::bind_method(D_METHOD("get_text_direction"), &LineEdit::get_text_direction); - ClassDB::bind_method(D_METHOD("set_opentype_feature", "tag", "value"), &LineEdit::set_opentype_feature); - ClassDB::bind_method(D_METHOD("get_opentype_feature", "tag"), &LineEdit::get_opentype_feature); - ClassDB::bind_method(D_METHOD("clear_opentype_features"), &LineEdit::clear_opentype_features); ClassDB::bind_method(D_METHOD("set_language", "language"), &LineEdit::set_language); ClassDB::bind_method(D_METHOD("get_language"), &LineEdit::get_language); ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override", "parser"), &LineEdit::set_structured_text_bidi_override); @@ -2419,18 +2346,20 @@ void LineEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deselect_on_focus_loss_enabled"), "set_deselect_on_focus_loss_enabled", "is_deselect_on_focus_loss_enabled"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "right_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_right_icon", "get_right_icon"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_control_chars"), "set_draw_control_chars", "get_draw_control_chars"); - ADD_GROUP("Structured Text", "structured_text_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options"); + ADD_GROUP("Caret", "caret_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "set_caret_blink_enabled", "is_caret_blink_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "set_caret_blink_speed", "get_caret_blink_speed"); ADD_PROPERTY(PropertyInfo(Variant::INT, "caret_column", PROPERTY_HINT_RANGE, "0,1000,1,or_greater"), "set_caret_column", "get_caret_column"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_force_displayed"), "set_caret_force_displayed", "is_caret_force_displayed"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_mid_grapheme"), "set_caret_mid_grapheme_enabled", "is_caret_mid_grapheme_enabled"); + + ADD_GROUP("BiDi", ""); + ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options"); } void LineEdit::_ensure_menu() { diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index 63c57640dc..557da35bfd 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -105,7 +105,6 @@ private: int scroll_offset = 0; int max_length = 0; // 0 for no maximum. - Dictionary opentype_features; String language; TextDirection text_direction = TEXT_DIRECTION_AUTO; TextDirection input_direction = TEXT_DIRECTION_LTR; @@ -210,9 +209,6 @@ protected: virtual void unhandled_key_input(const Ref<InputEvent> &p_event) override; virtual void gui_input(const Ref<InputEvent> &p_event) override; - bool _set(const StringName &p_name, const Variant &p_value); - bool _get(const StringName &p_name, Variant &r_ret) const; - void _get_property_list(List<PropertyInfo> *p_list) const; void _validate_property(PropertyInfo &property) const override; public: @@ -248,10 +244,6 @@ public: void set_text_direction(TextDirection p_text_direction); TextDirection get_text_direction() const; - void set_opentype_feature(const String &p_name, int p_value); - int get_opentype_feature(const String &p_name) const; - void clear_opentype_features(); - void set_language(const String &p_language); String get_language() const; diff --git a/scene/gui/link_button.cpp b/scene/gui/link_button.cpp index dca6437519..30c0bb3321 100644 --- a/scene/gui/link_button.cpp +++ b/scene/gui/link_button.cpp @@ -43,7 +43,7 @@ void LinkButton::_shape() { text_buf->set_direction((TextServer::Direction)text_direction); } TS->shaped_text_set_bidi_override(text_buf->get_rid(), structured_text_parser(st_parser, st_args, xl_text)); - text_buf->add_string(xl_text, font, font_size, opentype_features, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale()); + text_buf->add_string(xl_text, font, font_size, language); } void LinkButton::set_text(const String &p_text) { @@ -96,29 +96,6 @@ Control::TextDirection LinkButton::get_text_direction() const { return text_direction; } -void LinkButton::clear_opentype_features() { - opentype_features.clear(); - _shape(); - update(); -} - -void LinkButton::set_opentype_feature(const String &p_name, int p_value) { - int32_t tag = TS->name_to_tag(p_name); - if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) { - opentype_features[tag] = p_value; - _shape(); - update(); - } -} - -int LinkButton::get_opentype_feature(const String &p_name) const { - int32_t tag = TS->name_to_tag(p_name); - if (!opentype_features.has(tag)) { - return -1; - } - return opentype_features[tag]; -} - void LinkButton::set_language(const String &p_language) { if (language != p_language) { language = p_language; @@ -237,64 +214,11 @@ void LinkButton::_notification(int p_what) { } } -bool LinkButton::_set(const StringName &p_name, const Variant &p_value) { - String str = p_name; - if (str.begins_with("opentype_features/")) { - String name = str.get_slicec('/', 1); - int32_t tag = TS->name_to_tag(name); - int value = p_value; - if (value == -1) { - if (opentype_features.has(tag)) { - opentype_features.erase(tag); - _shape(); - update(); - } - } else { - if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) { - opentype_features[tag] = value; - _shape(); - update(); - } - } - notify_property_list_changed(); - return true; - } - - return false; -} - -bool LinkButton::_get(const StringName &p_name, Variant &r_ret) const { - String str = p_name; - if (str.begins_with("opentype_features/")) { - String name = str.get_slicec('/', 1); - int32_t tag = TS->name_to_tag(name); - if (opentype_features.has(tag)) { - r_ret = opentype_features[tag]; - return true; - } else { - r_ret = -1; - return true; - } - } - return false; -} - -void LinkButton::_get_property_list(List<PropertyInfo> *p_list) const { - for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) { - String name = TS->tag_to_name(*ftr); - p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name)); - } - p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); -} - void LinkButton::_bind_methods() { ClassDB::bind_method(D_METHOD("set_text", "text"), &LinkButton::set_text); ClassDB::bind_method(D_METHOD("get_text"), &LinkButton::get_text); ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &LinkButton::set_text_direction); ClassDB::bind_method(D_METHOD("get_text_direction"), &LinkButton::get_text_direction); - ClassDB::bind_method(D_METHOD("set_opentype_feature", "tag", "value"), &LinkButton::set_opentype_feature); - ClassDB::bind_method(D_METHOD("get_opentype_feature", "tag"), &LinkButton::get_opentype_feature); - ClassDB::bind_method(D_METHOD("clear_opentype_features"), &LinkButton::clear_opentype_features); ClassDB::bind_method(D_METHOD("set_language", "language"), &LinkButton::set_language); ClassDB::bind_method(D_METHOD("get_language"), &LinkButton::get_language); ClassDB::bind_method(D_METHOD("set_underline_mode", "underline_mode"), &LinkButton::set_underline_mode); @@ -309,10 +233,11 @@ void LinkButton::_bind_methods() { BIND_ENUM_CONSTANT(UNDERLINE_MODE_NEVER); ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "underline", PROPERTY_HINT_ENUM, "Always,On Hover,Never"), "set_underline_mode", "get_underline_mode"); + + ADD_GROUP("BiDi", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "underline", PROPERTY_HINT_ENUM, "Always,On Hover,Never"), "set_underline_mode", "get_underline_mode"); - ADD_GROUP("Structured Text", "structured_text_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options"); } diff --git a/scene/gui/link_button.h b/scene/gui/link_button.h index 6d2dcbde84..54a31f06ce 100644 --- a/scene/gui/link_button.h +++ b/scene/gui/link_button.h @@ -50,7 +50,6 @@ private: Ref<TextLine> text_buf; UnderlineMode underline_mode = UNDERLINE_MODE_ALWAYS; - Dictionary opentype_features; String language; TextDirection text_direction = TEXT_DIRECTION_AUTO; TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT; @@ -63,10 +62,6 @@ protected: void _notification(int p_what); static void _bind_methods(); - bool _set(const StringName &p_name, const Variant &p_value); - bool _get(const StringName &p_name, Variant &r_ret) const; - void _get_property_list(List<PropertyInfo> *p_list) const; - public: void set_text(const String &p_text); String get_text() const; @@ -80,10 +75,6 @@ public: void set_text_direction(TextDirection p_text_direction); TextDirection get_text_direction() const; - void set_opentype_feature(const String &p_name, int p_value); - int get_opentype_feature(const String &p_name) const; - void clear_opentype_features(); - void set_language(const String &p_language); String get_language() const; diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 5931c112eb..3a31246b17 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -760,11 +760,11 @@ void PopupMenu::_shape_item(int p_item) { } else { items.write[p_item].text_buf->set_direction((TextServer::Direction)items[p_item].text_direction); } - items.write[p_item].text_buf->add_string(items.write[p_item].xl_text, font, font_size, items[p_item].opentype_features, !items[p_item].language.is_empty() ? items[p_item].language : TranslationServer::get_singleton()->get_tool_locale()); + items.write[p_item].text_buf->add_string(items.write[p_item].xl_text, font, font_size, items[p_item].language); items.write[p_item].accel_text_buf->clear(); items.write[p_item].accel_text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); - items.write[p_item].accel_text_buf->add_string(_get_accel_text(items.write[p_item]), font, font_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); + items.write[p_item].accel_text_buf->add_string(_get_accel_text(items.write[p_item]), font, font_size); items.write[p_item].dirty = false; } } @@ -1067,29 +1067,6 @@ void PopupMenu::set_item_text_direction(int p_item, Control::TextDirection p_tex } } -void PopupMenu::clear_item_opentype_features(int p_item) { - if (p_item < 0) { - p_item += get_item_count(); - } - ERR_FAIL_INDEX(p_item, items.size()); - items.write[p_item].opentype_features.clear(); - items.write[p_item].dirty = true; - control->update(); -} - -void PopupMenu::set_item_opentype_feature(int p_item, const String &p_name, int p_value) { - if (p_item < 0) { - p_item += get_item_count(); - } - ERR_FAIL_INDEX(p_item, items.size()); - int32_t tag = TS->name_to_tag(p_name); - if (!items[p_item].opentype_features.has(tag) || (int)items[p_item].opentype_features[tag] != p_value) { - items.write[p_item].opentype_features[tag] = p_value; - items.write[p_item].dirty = true; - control->update(); - } -} - void PopupMenu::set_item_language(int p_item, const String &p_language) { if (p_item < 0) { p_item += get_item_count(); @@ -1195,15 +1172,6 @@ Control::TextDirection PopupMenu::get_item_text_direction(int p_item) const { return items[p_item].text_direction; } -int PopupMenu::get_item_opentype_feature(int p_item, const String &p_name) const { - ERR_FAIL_INDEX_V(p_item, items.size(), -1); - int32_t tag = TS->name_to_tag(p_name); - if (!items[p_item].opentype_features.has(tag)) { - return -1; - } - return items[p_item].opentype_features[tag]; -} - String PopupMenu::get_item_language(int p_item) const { ERR_FAIL_INDEX_V(p_item, items.size(), ""); return items[p_item].language; @@ -1853,7 +1821,6 @@ void PopupMenu::_bind_methods() { ClassDB::bind_method(D_METHOD("set_item_text", "index", "text"), &PopupMenu::set_item_text); ClassDB::bind_method(D_METHOD("set_item_text_direction", "index", "direction"), &PopupMenu::set_item_text_direction); - ClassDB::bind_method(D_METHOD("set_item_opentype_feature", "index", "tag", "value"), &PopupMenu::set_item_opentype_feature); ClassDB::bind_method(D_METHOD("set_item_language", "index", "language"), &PopupMenu::set_item_language); ClassDB::bind_method(D_METHOD("set_item_icon", "index", "icon"), &PopupMenu::set_item_icon); ClassDB::bind_method(D_METHOD("set_item_checked", "index", "checked"), &PopupMenu::set_item_checked); @@ -1876,8 +1843,6 @@ void PopupMenu::_bind_methods() { ClassDB::bind_method(D_METHOD("get_item_text", "index"), &PopupMenu::get_item_text); ClassDB::bind_method(D_METHOD("get_item_text_direction", "index"), &PopupMenu::get_item_text_direction); - ClassDB::bind_method(D_METHOD("get_item_opentype_feature", "index", "tag"), &PopupMenu::get_item_opentype_feature); - ClassDB::bind_method(D_METHOD("clear_item_opentype_features", "index"), &PopupMenu::clear_item_opentype_features); ClassDB::bind_method(D_METHOD("get_item_language", "index"), &PopupMenu::get_item_language); ClassDB::bind_method(D_METHOD("get_item_icon", "index"), &PopupMenu::get_item_icon); ClassDB::bind_method(D_METHOD("is_item_checked", "index"), &PopupMenu::is_item_checked); diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h index 8218c6122e..daa38b0e6d 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -47,7 +47,6 @@ class PopupMenu : public Popup { Ref<TextLine> text_buf; Ref<TextLine> accel_text_buf; - Dictionary opentype_features; String language; Control::TextDirection text_direction = Control::TEXT_DIRECTION_AUTO; @@ -171,8 +170,6 @@ public: void set_item_text(int p_idx, const String &p_text); void set_item_text_direction(int p_idx, Control::TextDirection p_text_direction); - void set_item_opentype_feature(int p_idx, const String &p_name, int p_value); - void clear_item_opentype_features(int p_idx); void set_item_language(int p_idx, const String &p_language); void set_item_icon(int p_idx, const Ref<Texture2D> &p_icon); void set_item_checked(int p_idx, bool p_checked); @@ -195,7 +192,6 @@ public: String get_item_text(int p_idx) const; Control::TextDirection get_item_text_direction(int p_idx) const; - int get_item_opentype_feature(int p_idx, const String &p_name) const; String get_item_language(int p_idx) const; int get_item_idx_from_text(const String &text) const; Ref<Texture2D> get_item_icon(int p_idx) const; diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 0516c8e722..2441279797 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -227,8 +227,10 @@ void RichTextLabel::_update_line_font(ItemFrame *p_frame, int p_line, const Ref< if (font_size == -1) { font_size = p_base_font_size; } - Dictionary font_ftr = _find_font_features(it); - TS->shaped_set_span_update_font(t, i, font->get_rids(), font_size, font_ftr); + TS->shaped_set_span_update_font(t, i, font->get_rids(), font_size, font->get_opentype_features()); + for (int j = 0; j < TextServer::SPACING_MAX; j++) { + TS->shaped_text_set_spacing(t, TextServer::SpacingType(j), font->get_spacing(TextServer::SpacingType(j))); + } } } @@ -263,7 +265,7 @@ float RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font if (tab_size > 0) { // Align inline tabs. Vector<float> tabs; - tabs.push_back(tab_size * p_base_font->get_char_size(' ', 0, p_base_font_size).width); + tabs.push_back(tab_size * p_base_font->get_char_size(' ', p_base_font_size).width); l.text_buf->tab_align(tabs); } @@ -453,7 +455,7 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> if (tab_size > 0) { // Align inline tabs. Vector<float> tabs; - tabs.push_back(tab_size * p_base_font->get_char_size(' ', 0, p_base_font_size).width); + tabs.push_back(tab_size * p_base_font->get_char_size(' ', p_base_font_size).width); l.text_buf->tab_align(tabs); } @@ -483,7 +485,7 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> if (font_size == -1) { font_size = p_base_font_size; } - l.text_buf->add_string("\n", font, font_size, Dictionary(), ""); + l.text_buf->add_string("\n", font, font_size); text += "\n"; l.char_count++; remaining_characters--; @@ -498,7 +500,6 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> if (font_size == -1) { font_size = p_base_font_size; } - Dictionary font_ftr = _find_font_features(it); String lang = _find_language(it); String tx = t->text; if (visible_chars_behavior == TextServer::VC_CHARS_BEFORE_SHAPING && visible_characters >= 0 && remaining_characters >= 0) { @@ -506,7 +507,7 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> } remaining_characters -= tx.length(); - l.text_buf->add_string(tx, font, font_size, font_ftr, lang, (uint64_t)it); + l.text_buf->add_string(tx, font, font_size, lang, (uint64_t)it); text += tx; l.char_count += tx.length(); } break; @@ -837,7 +838,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o //draw_rect(Rect2(p_ofs + off, TS->shaped_text_get_size(rid)), Color(1,0,0), false, 2); //DEBUG_RECTS - off.y += TS->shaped_text_get_ascent(rid) + l.text_buf->get_spacing_top(); + off.y += TS->shaped_text_get_ascent(rid); // Draw inlined objects. Array objects = TS->shaped_text_get_objects(rid); for (int i = 0; i < objects.size(); i++) { @@ -1299,7 +1300,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o // Draw foreground color box _draw_fbg_boxes(ci, rid, fbg_line_off, it_from, it_to, chr_range.x, chr_range.y, 1); - off.y += TS->shaped_text_get_descent(rid) + l.text_buf->get_spacing_bottom(); + off.y += TS->shaped_text_get_descent(rid); } return line_count; @@ -1394,7 +1395,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V } break; } - off.y += TS->shaped_text_get_ascent(rid) + l.text_buf->get_spacing_top(); + off.y += TS->shaped_text_get_ascent(rid); Array objects = TS->shaped_text_get_objects(rid); for (int i = 0; i < objects.size(); i++) { @@ -1491,7 +1492,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V return table_offy; } - off.y += TS->shaped_text_get_descent(rid) + l.text_buf->get_spacing_bottom() + get_theme_constant(SNAME("line_separation")); + off.y += TS->shaped_text_get_descent(rid) + get_theme_constant(SNAME("line_separation")); } // Text line hit. @@ -2080,7 +2081,7 @@ Ref<Font> RichTextLabel::_find_font(Item *p_item) { fontitem = fontitem->parent; } - return Ref<Font>(); + return Ref<FontFile>(); } int RichTextLabel::_find_font_size(Item *p_item) { @@ -2113,21 +2114,6 @@ int RichTextLabel::_find_outline_size(Item *p_item, int p_default) { return p_default; } -Dictionary RichTextLabel::_find_font_features(Item *p_item) { - Item *ffitem = p_item; - - while (ffitem) { - if (ffitem->type == ITEM_FONT_FEATURES) { - ItemFontFeatures *fi = static_cast<ItemFontFeatures *>(ffitem); - return fi->opentype_features; - } - - ffitem = ffitem->parent; - } - - return Dictionary(); -} - RichTextLabel::ItemDropcap *RichTextLabel::_find_dc_item(Item *p_item) { Item *item = p_item; @@ -2204,7 +2190,7 @@ int RichTextLabel::_find_margin(Item *p_item, const Ref<Font> &p_base_font, int if (font_size == -1) { font_size = p_base_font_size; } - margin += tab_size * font->get_char_size(' ', 0, font_size).width; + margin += tab_size * font->get_char_size(' ', font_size).width; } else if (item->type == ITEM_LIST) { Ref<Font> font = _find_font(item); @@ -2215,7 +2201,7 @@ int RichTextLabel::_find_margin(Item *p_item, const Ref<Font> &p_base_font, int if (font_size == -1) { font_size = p_base_font_size; } - margin += tab_size * font->get_char_size(' ', 0, font_size).width; + margin += tab_size * font->get_char_size(' ', font_size).width; } item = item->parent; @@ -2584,8 +2570,8 @@ void RichTextLabel::_process_line_caches() { MutexLock data_lock(data_mutex); Rect2 text_rect = _get_text_rect(); - Ref<Font> base_font = get_theme_font(SNAME("normal_font")); int base_font_size = get_theme_font_size(SNAME("normal_font_size")); + Ref<Font> base_font = get_theme_font(SNAME("normal_font")); int ctrl_height = get_size().height; int fi = main->first_invalid_line.load(); int total_chars = (fi == 0) ? 0 : (main->lines[fi].char_offset + main->lines[fi].char_count); @@ -2950,25 +2936,14 @@ void RichTextLabel::push_font_size(int p_font_size) { _add_item(item, true); } -void RichTextLabel::push_font_features(const Dictionary &p_features) { - _stop_thread(); - MutexLock data_lock(data_mutex); - - ERR_FAIL_COND(current->type == ITEM_TABLE); - ItemFontFeatures *item = memnew(ItemFontFeatures); - - item->opentype_features = p_features; - _add_item(item, true); -} - -void RichTextLabel::push_outline_size(int p_font_size) { +void RichTextLabel::push_outline_size(int p_ol_size) { _stop_thread(); MutexLock data_lock(data_mutex); ERR_FAIL_COND(current->type == ITEM_TABLE); ItemOutlineSize *item = memnew(ItemOutlineSize); - item->outline_size = p_font_size; + item->outline_size = p_ol_size; _add_item(item, true); } @@ -3840,8 +3815,8 @@ void RichTextLabel::append_text(const String &p_bbcode) { tag_stack.push_front("hint"); } else if (tag.begins_with("dropcap")) { Vector<String> subtag = tag.substr(5, tag.length()).split(" "); - Ref<Font> f = get_theme_font(SNAME("normal_font")); int fs = get_theme_font_size(SNAME("normal_font_size")) * 3; + Ref<Font> f = get_theme_font(SNAME("normal_font")); Color color = get_theme_color(SNAME("default_color")); Color outline_color = get_theme_color(SNAME("outline_color")); int outline_size = get_theme_constant(SNAME("outline_size")); @@ -3974,64 +3949,127 @@ void RichTextLabel::append_text(const String &p_bbcode) { pos = brk_end + 1; tag_stack.push_front("outline_color"); - } else if (tag.begins_with("font=")) { - String fnt = tag.substr(5, tag.length()); - - Ref<Font> font = ResourceLoader::load(fnt, "Font"); - if (font.is_valid()) { - push_font(font); - } else { - push_font(normal_font); - } - - pos = brk_end + 1; - tag_stack.push_front("font"); } else if (tag.begins_with("font_size=")) { int fnt_size = tag.substr(10, tag.length()).to_int(); push_font_size(fnt_size); pos = brk_end + 1; tag_stack.push_front("font_size"); + } else if (tag.begins_with("opentype_features=")) { String fnt_ftr = tag.substr(18, tag.length()); Vector<String> subtag = fnt_ftr.split(","); - Dictionary ftrs; - for (int i = 0; i < subtag.size(); i++) { - Vector<String> subtag_a = subtag[i].split("="); - if (subtag_a.size() == 2) { - ftrs[TS->name_to_tag(subtag_a[0])] = subtag_a[1].to_int(); - } else if (subtag_a.size() == 1) { - ftrs[TS->name_to_tag(subtag_a[0])] = 1; + if (subtag.size() > 0) { + Ref<Font> font = _find_font(current); + if (font.is_null()) { + font = normal_font; } + Ref<FontVariation> fc; + fc.instantiate(); + fc->set_base_font(font); + Dictionary features; + for (int i = 0; i < subtag.size(); i++) { + Vector<String> subtag_a = subtag[i].split("="); + if (subtag_a.size() == 2) { + features[TS->name_to_tag(subtag_a[0])] = subtag_a[1].to_int(); + } else if (subtag_a.size() == 1) { + features[TS->name_to_tag(subtag_a[0])] = 1; + } + } + fc->set_opentype_features(features); + push_font(fc); } - push_font_features(ftrs); pos = brk_end + 1; tag_stack.push_front("opentype_features"); + + } else if (tag.begins_with("font=")) { + String fnt = tag.substr(5, tag.length()); + + Ref<Font> fc = ResourceLoader::load(fnt, "Font"); + if (fc.is_valid()) { + push_font(fc); + } + + pos = brk_end + 1; + tag_stack.push_front("font"); + } else if (tag.begins_with("font ")) { Vector<String> subtag = tag.substr(2, tag.length()).split(" "); + Ref<FontVariation> fc; + fc.instantiate(); for (int i = 1; i < subtag.size(); i++) { Vector<String> subtag_a = subtag[i].split("=", true, 2); if (subtag_a.size() == 2) { if (subtag_a[0] == "name" || subtag_a[0] == "n") { String fnt = subtag_a[1]; - Ref<Font> font = ResourceLoader::load(fnt, "Font"); - if (font.is_valid()) { - push_font(font); - } else { - push_font(normal_font); + Ref<Font> font_data = ResourceLoader::load(fnt, "FontFile"); + if (font_data.is_valid()) { + fc->set_base_font(font_data); } } else if (subtag_a[0] == "size" || subtag_a[0] == "s") { int fnt_size = subtag_a[1].to_int(); - push_font_size(fnt_size); + if (fnt_size > 0) { + push_font_size(fnt_size); + } + } else if (subtag_a[0] == "glyph_spacing" || subtag_a[0] == "gl") { + int spacing = subtag_a[1].to_int(); + fc->set_spacing(TextServer::SPACING_GLYPH, spacing); + } else if (subtag_a[0] == "space_spacing" || subtag_a[0] == "sp") { + int spacing = subtag_a[1].to_int(); + fc->set_spacing(TextServer::SPACING_SPACE, spacing); + } else if (subtag_a[0] == "top_spacing" || subtag_a[0] == "top") { + int spacing = subtag_a[1].to_int(); + fc->set_spacing(TextServer::SPACING_TOP, spacing); + } else if (subtag_a[0] == "bottom_spacing" || subtag_a[0] == "bt") { + int spacing = subtag_a[1].to_int(); + fc->set_spacing(TextServer::SPACING_BOTTOM, spacing); + } else if (subtag_a[0] == "embolden" || subtag_a[0] == "emb") { + float emb = subtag_a[1].to_float(); + fc->set_variation_embolden(emb); + } else if (subtag_a[0] == "face_index" || subtag_a[0] == "fi") { + int fi = subtag_a[1].to_int(); + fc->set_variation_face_index(fi); + } else if (subtag_a[0] == "slant" || subtag_a[0] == "sln") { + float slant = subtag_a[1].to_float(); + fc->set_variation_transform(Transform2D(1.0, slant, 0.0, 1.0, 0.0, 0.0)); + } else if (subtag_a[0] == "opentype_variation" || subtag_a[0] == "otv") { + Dictionary variations; + if (!subtag_a[1].is_empty()) { + Vector<String> variation_tags = subtag_a[1].split(","); + for (int j = 0; j < variation_tags.size(); j++) { + Vector<String> subtag_b = variation_tags[j].split("="); + if (subtag_b.size() == 2) { + variations[TS->name_to_tag(subtag_b[0])] = subtag_b[1].to_float(); + } + } + fc->set_variation_opentype(variations); + } + } else if (subtag_a[0] == "opentype_features" || subtag_a[0] == "otf") { + Dictionary features; + if (!subtag_a[1].is_empty()) { + Vector<String> feature_tags = subtag_a[1].split(","); + for (int j = 0; j < feature_tags.size(); j++) { + Vector<String> subtag_b = feature_tags[j].split("="); + if (subtag_b.size() == 2) { + features[TS->name_to_tag(subtag_b[0])] = subtag_b[1].to_float(); + } else if (subtag_b.size() == 1) { + features[TS->name_to_tag(subtag_b[0])] = 1; + } + } + fc->set_opentype_features(features); + } } } } - + push_font(fc); pos = brk_end + 1; tag_stack.push_front("font"); + } else if (tag.begins_with("outline_size=")) { int fnt_size = tag.substr(13, tag.length()).to_int(); - push_outline_size(fnt_size); + if (fnt_size > 0) { + push_outline_size(fnt_size); + } pos = brk_end + 1; tag_stack.push_front("outline_size"); @@ -4854,7 +4892,6 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("remove_line", "line"), &RichTextLabel::remove_line); ClassDB::bind_method(D_METHOD("push_font", "font"), &RichTextLabel::push_font); ClassDB::bind_method(D_METHOD("push_font_size", "font_size"), &RichTextLabel::push_font_size); - ClassDB::bind_method(D_METHOD("push_font_features", "opentype_features"), &RichTextLabel::push_font_features); ClassDB::bind_method(D_METHOD("push_normal"), &RichTextLabel::push_normal); ClassDB::bind_method(D_METHOD("push_bold"), &RichTextLabel::push_bold); ClassDB::bind_method(D_METHOD("push_bold_italics"), &RichTextLabel::push_bold_italics); @@ -5018,11 +5055,9 @@ void RichTextLabel::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled"); - ADD_GROUP("Locale", ""); + ADD_GROUP("BiDi", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); - - ADD_GROUP("Structured Text", "structured_text_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options"); diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index c697320976..3b6175e9cf 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -186,11 +186,6 @@ private: ItemFontSize() { type = ITEM_FONT_SIZE; } }; - struct ItemFontFeatures : public Item { - Dictionary opentype_features; - ItemFontFeatures() { type = ITEM_FONT_FEATURES; } - }; - struct ItemColor : public Item { Color color; ItemColor() { type = ITEM_COLOR; } @@ -466,9 +461,8 @@ private: Item *_get_item_at_pos(Item *p_item_from, Item *p_item_to, int p_position); void _find_frame(Item *p_item, ItemFrame **r_frame, int *r_line); - Ref<Font> _find_font(Item *p_item); int _find_font_size(Item *p_item); - Dictionary _find_font_features(Item *p_item); + Ref<Font> _find_font(Item *p_item); int _find_outline_size(Item *p_item, int p_default); ItemList *_find_list_item(Item *p_item); ItemDropcap *_find_dc_item(Item *p_item); @@ -525,7 +519,6 @@ public: void push_dropcap(const String &p_string, const Ref<Font> &p_font, int p_size, const Rect2 &p_dropcap_margins = Rect2(), const Color &p_color = Color(1, 1, 1), int p_ol_size = 0, const Color &p_ol_color = Color(0, 0, 0, 0)); void push_font(const Ref<Font> &p_font); void push_font_size(int p_font_size); - void push_font_features(const Dictionary &p_features); void push_outline_size(int p_font_size); void push_normal(); void push_bold(); diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp index b4c90596f9..d36a364677 100644 --- a/scene/gui/tab_bar.cpp +++ b/scene/gui/tab_bar.cpp @@ -311,7 +311,7 @@ void TabBar::_shape(int p_tab) { tabs.write[p_tab].text_buf->set_direction((TextServer::Direction)tabs[p_tab].text_direction); } - tabs.write[p_tab].text_buf->add_string(tabs[p_tab].xl_text, font, font_size, tabs[p_tab].opentype_features, !tabs[p_tab].language.is_empty() ? tabs[p_tab].language : TranslationServer::get_singleton()->get_tool_locale()); + tabs.write[p_tab].text_buf->add_string(tabs[p_tab].xl_text, font, font_size, tabs[p_tab].language); } void TabBar::_notification(int p_what) { @@ -667,48 +667,6 @@ Control::TextDirection TabBar::get_tab_text_direction(int p_tab) const { return tabs[p_tab].text_direction; } -void TabBar::clear_tab_opentype_features(int p_tab) { - ERR_FAIL_INDEX(p_tab, tabs.size()); - tabs.write[p_tab].opentype_features.clear(); - - _shape(p_tab); - _update_cache(); - _ensure_no_over_offset(); - if (scroll_to_selected) { - ensure_tab_visible(current); - } - update(); - update_minimum_size(); -} - -void TabBar::set_tab_opentype_feature(int p_tab, const String &p_name, int p_value) { - ERR_FAIL_INDEX(p_tab, tabs.size()); - - int32_t tag = TS->name_to_tag(p_name); - if (!tabs[p_tab].opentype_features.has(tag) || (int)tabs[p_tab].opentype_features[tag] != p_value) { - tabs.write[p_tab].opentype_features[tag] = p_value; - - _shape(p_tab); - _update_cache(); - _ensure_no_over_offset(); - if (scroll_to_selected) { - ensure_tab_visible(current); - } - update(); - update_minimum_size(); - } -} - -int TabBar::get_tab_opentype_feature(int p_tab, const String &p_name) const { - ERR_FAIL_INDEX_V(p_tab, tabs.size(), -1); - - int32_t tag = TS->name_to_tag(p_name); - if (!tabs[p_tab].opentype_features.has(tag)) { - return -1; - } - return tabs[p_tab].opentype_features[tag]; -} - void TabBar::set_tab_language(int p_tab, const String &p_language) { ERR_FAIL_INDEX(p_tab, tabs.size()); @@ -1553,9 +1511,6 @@ void TabBar::_bind_methods() { ClassDB::bind_method(D_METHOD("get_tab_title", "tab_idx"), &TabBar::get_tab_title); ClassDB::bind_method(D_METHOD("set_tab_text_direction", "tab_idx", "direction"), &TabBar::set_tab_text_direction); ClassDB::bind_method(D_METHOD("get_tab_text_direction", "tab_idx"), &TabBar::get_tab_text_direction); - ClassDB::bind_method(D_METHOD("set_tab_opentype_feature", "tab_idx", "tag", "values"), &TabBar::set_tab_opentype_feature); - ClassDB::bind_method(D_METHOD("get_tab_opentype_feature", "tab_idx", "tag"), &TabBar::get_tab_opentype_feature); - ClassDB::bind_method(D_METHOD("clear_tab_opentype_features", "tab_idx"), &TabBar::clear_tab_opentype_features); ClassDB::bind_method(D_METHOD("set_tab_language", "tab_idx", "language"), &TabBar::set_tab_language); ClassDB::bind_method(D_METHOD("get_tab_language", "tab_idx"), &TabBar::get_tab_language); ClassDB::bind_method(D_METHOD("set_tab_icon", "tab_idx", "icon"), &TabBar::set_tab_icon); diff --git a/scene/gui/tab_bar.h b/scene/gui/tab_bar.h index 548a2e62af..d123385e47 100644 --- a/scene/gui/tab_bar.h +++ b/scene/gui/tab_bar.h @@ -57,7 +57,6 @@ private: String text; String xl_text; - Dictionary opentype_features; String language; Control::TextDirection text_direction = Control::TEXT_DIRECTION_INHERITED; @@ -137,10 +136,6 @@ public: void set_tab_text_direction(int p_tab, TextDirection p_text_direction); TextDirection get_tab_text_direction(int p_tab) const; - void set_tab_opentype_feature(int p_tab, const String &p_name, int p_value); - int get_tab_opentype_feature(int p_tab, const String &p_name) const; - void clear_tab_opentype_features(int p_tab); - void set_tab_language(int p_tab, const String &p_language); String get_tab_language(int p_tab) const; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index a8542c4346..9c6cd6bdb2 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -75,14 +75,6 @@ int TextEdit::Text::get_tab_size() const { return tab_size; } -void TextEdit::Text::set_font_features(const Dictionary &p_features) { - if (opentype_features.hash() == p_features.hash()) { - return; - } - opentype_features = p_features; - is_dirty = true; -} - void TextEdit::Text::set_direction_and_language(TextServer::Direction p_direction, const String &p_language) { if (direction == p_direction && language == p_language) { return; @@ -178,7 +170,7 @@ void TextEdit::Text::_calculate_max_line_width() { void TextEdit::Text::invalidate_cache(int p_line, int p_column, bool p_text_changed, const String &p_ime_text, const Array &p_bidi_override) { ERR_FAIL_INDEX(p_line, text.size()); - if (font.is_null() || font_size <= 0) { + if (font.is_null()) { return; // Not in tree? } @@ -191,14 +183,14 @@ void TextEdit::Text::invalidate_cache(int p_line, int p_column, bool p_text_chan text.write[p_line].data_buf->set_preserve_control(draw_control_chars); if (p_ime_text.length() > 0) { if (p_text_changed) { - text.write[p_line].data_buf->add_string(p_ime_text, font, font_size, opentype_features, language); + text.write[p_line].data_buf->add_string(p_ime_text, font, font_size, language); } if (!p_bidi_override.is_empty()) { TS->shaped_text_set_bidi_override(text.write[p_line].data_buf->get_rid(), p_bidi_override); } } else { if (p_text_changed) { - text.write[p_line].data_buf->add_string(text[p_line].data, font, font_size, opentype_features, language); + text.write[p_line].data_buf->add_string(text[p_line].data, font, font_size, language); } if (!text[p_line].bidi_override.is_empty()) { TS->shaped_text_set_bidi_override(text.write[p_line].data_buf->get_rid(), text[p_line].bidi_override); @@ -209,14 +201,17 @@ void TextEdit::Text::invalidate_cache(int p_line, int p_column, bool p_text_chan RID r = text.write[p_line].data_buf->get_rid(); int spans = TS->shaped_get_span_count(r); for (int i = 0; i < spans; i++) { - TS->shaped_set_span_update_font(r, i, font->get_rids(), font_size, opentype_features); + TS->shaped_set_span_update_font(r, i, font->get_rids(), font_size, font->get_opentype_features()); + } + for (int i = 0; i < TextServer::SPACING_MAX; i++) { + TS->shaped_text_set_spacing(r, TextServer::SpacingType(i), font->get_spacing(TextServer::SpacingType(i))); } } // Apply tab align. if (tab_size > 0) { Vector<float> tabs; - tabs.push_back(font->get_char_size(' ', 0, font_size).width * tab_size); + tabs.push_back(font->get_char_size(' ', font_size).width * tab_size); text.write[p_line].data_buf->tab_align(tabs); } @@ -255,7 +250,7 @@ void TextEdit::Text::invalidate_all_lines() { if (tab_size_dirty) { if (tab_size > 0) { Vector<float> tabs; - tabs.push_back(font->get_char_size(' ', 0, font_size).width * tab_size); + tabs.push_back(font->get_char_size(' ', font_size).width * tab_size); text.write[i].data_buf->tab_align(tabs); } // Tabs have changes, force width update. @@ -277,7 +272,7 @@ void TextEdit::Text::invalidate_font() { max_width = -1; line_height = -1; - if (!font.is_null() && font_size > 0) { + if (font.is_valid() && font_size > 0) { font_height = font->get_height(font_size); } @@ -295,7 +290,7 @@ void TextEdit::Text::invalidate_all() { max_width = -1; line_height = -1; - if (!font.is_null() && font_size > 0) { + if (font.is_valid() && font_size > 0) { font_height = font->get_height(font_size); } @@ -973,7 +968,7 @@ void TextEdit::_notification(int p_what) { // Give visual indication of empty selected line. if (selection.active && line >= selection.from_line && line <= selection.to_line && char_margin >= xmargin_beg) { - float char_w = font->get_char_size(' ', 0, font_size).width; + float char_w = font->get_char_size(' ', font_size).width; if (rtl) { RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - xmargin_beg - ofs_x - char_w, ofs_y, char_w, row_height), selection_color); } else { @@ -1071,7 +1066,7 @@ void TextEdit::_notification(int p_what) { // Draw line. RID rid = ldata->get_line_rid(line_wrap_index); - float text_height = TS->shaped_text_get_size(rid).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM); + float text_height = TS->shaped_text_get_size(rid).y; if (rtl) { char_margin = size.width - char_margin - TS->shaped_text_get_size(rid).x; @@ -1355,7 +1350,7 @@ void TextEdit::_notification(int p_what) { ts_caret.l_caret.size.y = caret_width; } if (ts_caret.l_caret.position.x >= TS->shaped_text_get_size(rid).x) { - ts_caret.l_caret.size.x = font->get_char_size('m', 0, font_size).x; + ts_caret.l_caret.size.x = font->get_char_size('m', font_size).x; } else { ts_caret.l_caret.size.x = 3 * caret_width; } @@ -2573,7 +2568,7 @@ void TextEdit::_update_placeholder() { placeholder_data_buf->set_width(text.get_width()); placeholder_data_buf->set_direction((TextServer::Direction)text_direction); placeholder_data_buf->set_preserve_control(draw_control_chars); - placeholder_data_buf->add_string(placeholder_text, font, font_size, opentype_features, language); + placeholder_data_buf->add_string(placeholder_text, font, font_size, language); placeholder_bidi_override = structured_text_parser(st_parser, st_args, placeholder_text); if (placeholder_bidi_override.is_empty()) { @@ -2582,7 +2577,7 @@ void TextEdit::_update_placeholder() { if (get_tab_size() > 0) { Vector<float> tabs; - tabs.push_back(font->get_char_size(' ', 0, font_size).width * get_tab_size()); + tabs.push_back(font->get_char_size(' ', font_size).width * get_tab_size()); placeholder_data_buf->tab_align(tabs); } @@ -2653,7 +2648,6 @@ void TextEdit::_update_caches() { dir = (TextServer::Direction)text_direction; } text.set_direction_and_language(dir, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale()); - text.set_font_features(opentype_features); text.set_draw_control_chars(draw_control_chars); text.set_font(font); text.set_font_size(font_size); @@ -2858,33 +2852,6 @@ Control::TextDirection TextEdit::get_text_direction() const { return text_direction; } -void TextEdit::set_opentype_feature(const String &p_name, int p_value) { - int32_t tag = TS->name_to_tag(p_name); - if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) { - opentype_features[tag] = p_value; - text.set_font_features(opentype_features); - text.invalidate_font(); - _update_placeholder(); - update(); - } -} - -int TextEdit::get_opentype_feature(const String &p_name) const { - int32_t tag = TS->name_to_tag(p_name); - if (!opentype_features.has(tag)) { - return -1; - } - return opentype_features[tag]; -} - -void TextEdit::clear_opentype_features() { - opentype_features.clear(); - text.set_font_features(opentype_features); - text.invalidate_font(); - _update_placeholder(); - update(); -} - void TextEdit::set_language(const String &p_language) { if (language != p_language) { language = p_language; @@ -5071,10 +5038,6 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &TextEdit::set_text_direction); ClassDB::bind_method(D_METHOD("get_text_direction"), &TextEdit::get_text_direction); - ClassDB::bind_method(D_METHOD("set_opentype_feature", "tag", "value"), &TextEdit::set_opentype_feature); - ClassDB::bind_method(D_METHOD("get_opentype_feature", "tag"), &TextEdit::get_opentype_feature); - ClassDB::bind_method(D_METHOD("clear_opentype_features"), &TextEdit::clear_opentype_features); - ClassDB::bind_method(D_METHOD("set_language", "language"), &TextEdit::set_language); ClassDB::bind_method(D_METHOD("get_language"), &TextEdit::get_language); @@ -5107,6 +5070,7 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_text", "text"), &TextEdit::set_text); ClassDB::bind_method(D_METHOD("get_text"), &TextEdit::get_text); + ClassDB::bind_method(D_METHOD("get_line_count"), &TextEdit::get_line_count); ClassDB::bind_method(D_METHOD("set_placeholder", "text"), &TextEdit::set_placeholder); @@ -5427,8 +5391,6 @@ void TextEdit::_bind_methods() { /* Inspector */ ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "placeholder_text", PROPERTY_HINT_MULTILINE_TEXT), "set_placeholder", "get_placeholder"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled"); @@ -5468,7 +5430,9 @@ void TextEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_move_on_right_click"), "set_move_caret_on_right_click_enabled", "is_move_caret_on_right_click_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_mid_grapheme"), "set_caret_mid_grapheme_enabled", "is_caret_mid_grapheme_enabled"); - ADD_GROUP("Structured Text", "structured_text_"); + ADD_GROUP("BiDi", ""); + ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options"); @@ -5493,60 +5457,6 @@ void TextEdit::_bind_methods() { ProjectSettings::get_singleton()->set_custom_property_info("gui/common/text_edit_undo_stack_max_size", PropertyInfo(Variant::INT, "gui/common/text_edit_undo_stack_max_size", PROPERTY_HINT_RANGE, "0,10000,1,or_greater")); // No negative numbers. } -bool TextEdit::_set(const StringName &p_name, const Variant &p_value) { - String str = p_name; - if (str.begins_with("opentype_features/")) { - String name = str.get_slicec('/', 1); - int32_t tag = TS->name_to_tag(name); - int value = p_value; - if (value == -1) { - if (opentype_features.has(tag)) { - opentype_features.erase(tag); - text.set_font_features(opentype_features); - text.invalidate_font(); - _update_placeholder(); - update(); - } - } else { - if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) { - opentype_features[tag] = value; - text.set_font_features(opentype_features); - text.invalidate_font(); - _update_placeholder(); - update(); - } - } - notify_property_list_changed(); - return true; - } - - return false; -} - -bool TextEdit::_get(const StringName &p_name, Variant &r_ret) const { - String str = p_name; - if (str.begins_with("opentype_features/")) { - String name = str.get_slicec('/', 1); - int32_t tag = TS->name_to_tag(name); - if (opentype_features.has(tag)) { - r_ret = opentype_features[tag]; - return true; - } else { - r_ret = -1; - return true; - } - } - return false; -} - -void TextEdit::_get_property_list(List<PropertyInfo> *p_list) const { - for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) { - String name = TS->tag_to_name(*ftr); - p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name)); - } - p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); -} - /* Internal API for CodeEdit. */ // Line hiding. void TextEdit::_set_hiding_enabled(bool p_enabled) { diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 6ba6e9cf20..6711cf8c7f 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -160,7 +160,6 @@ private: int font_size = -1; int font_height = 0; - Dictionary opentype_features; String language; TextServer::Direction direction = TextServer::DIRECTION_AUTO; bool draw_control_chars = false; @@ -180,7 +179,6 @@ private: int get_tab_size() const; void set_font(const Ref<Font> &p_font); void set_font_size(int p_font_size); - void set_font_features(const Dictionary &p_features); void set_direction_and_language(TextServer::Direction p_direction, const String &p_language); void set_draw_control_chars(bool p_enabled); @@ -271,7 +269,6 @@ private: TextDirection text_direction = TEXT_DIRECTION_AUTO; TextDirection input_direction = TEXT_DIRECTION_LTR; - Dictionary opentype_features; String language = ""; TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT; @@ -581,10 +578,6 @@ protected: static void _bind_methods(); - bool _set(const StringName &p_name, const Variant &p_value); - bool _get(const StringName &p_name, Variant &r_ret) const; - void _get_property_list(List<PropertyInfo> *p_list) const; - /* Internal API for CodeEdit, pending public API. */ // brace matching bool highlight_matching_braces_enabled = false; @@ -650,10 +643,6 @@ public: void set_text_direction(TextDirection p_text_direction); TextDirection get_text_direction() const; - void set_opentype_feature(const String &p_name, int p_value); - int get_opentype_feature(const String &p_name) const; - void clear_opentype_features(); - void set_language(const String &p_language); String get_language() const; @@ -686,6 +675,7 @@ public: void set_text(const String &p_text); String get_text() const; + int get_line_count() const; void set_placeholder(const String &p_text); diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index d3e7540790..32d348c121 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -303,37 +303,6 @@ Control::TextDirection TreeItem::get_text_direction(int p_column) const { return cells[p_column].text_direction; } -void TreeItem::clear_opentype_features(int p_column) { - ERR_FAIL_INDEX(p_column, cells.size()); - - cells.write[p_column].opentype_features.clear(); - cells.write[p_column].dirty = true; - cells.write[p_column].cached_minimum_size_dirty = true; - - _changed_notify(p_column); -} - -void TreeItem::set_opentype_feature(int p_column, const String &p_name, int p_value) { - ERR_FAIL_INDEX(p_column, cells.size()); - int32_t tag = TS->name_to_tag(p_name); - if (!cells[p_column].opentype_features.has(tag) || (int)cells[p_column].opentype_features[tag] != p_value) { - cells.write[p_column].opentype_features[tag] = p_value; - cells.write[p_column].dirty = true; - cells.write[p_column].cached_minimum_size_dirty = true; - - _changed_notify(p_column); - } -} - -int TreeItem::get_opentype_feature(int p_column, const String &p_name) const { - ERR_FAIL_INDEX_V(p_column, cells.size(), -1); - int32_t tag = TS->name_to_tag(p_name); - if (!cells[p_column].opentype_features.has(tag)) { - return -1; - } - return cells[p_column].opentype_features[tag]; -} - void TreeItem::set_structured_text_bidi_override(int p_column, TextServer::StructuredTextParser p_parser) { ERR_FAIL_INDEX(p_column, cells.size()); @@ -1269,10 +1238,6 @@ void TreeItem::_bind_methods() { ClassDB::bind_method(D_METHOD("set_text_direction", "column", "direction"), &TreeItem::set_text_direction); ClassDB::bind_method(D_METHOD("get_text_direction", "column"), &TreeItem::get_text_direction); - ClassDB::bind_method(D_METHOD("set_opentype_feature", "column", "tag", "value"), &TreeItem::set_opentype_feature); - ClassDB::bind_method(D_METHOD("get_opentype_feature", "column", "tag"), &TreeItem::get_opentype_feature); - ClassDB::bind_method(D_METHOD("clear_opentype_features", "column"), &TreeItem::clear_opentype_features); - ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override", "column", "parser"), &TreeItem::set_structured_text_bidi_override); ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override", "column"), &TreeItem::get_structured_text_bidi_override); @@ -1667,7 +1632,7 @@ void Tree::update_column(int p_col) { columns.write[p_col].text_buf->set_direction((TextServer::Direction)columns[p_col].text_direction); } - columns.write[p_col].text_buf->add_string(columns[p_col].title, cache.font, cache.font_size, columns[p_col].opentype_features, !columns[p_col].language.is_empty() ? columns[p_col].language : TranslationServer::get_singleton()->get_tool_locale()); + columns.write[p_col].text_buf->add_string(columns[p_col].title, cache.font, cache.font_size, columns[p_col].language); } void Tree::update_item_cell(TreeItem *p_item, int p_col) { @@ -1725,7 +1690,7 @@ void Tree::update_item_cell(TreeItem *p_item, int p_col) { } else { font_size = cache.font_size; } - p_item->cells.write[p_col].text_buf->add_string(valtext, font, font_size, p_item->cells[p_col].opentype_features, !p_item->cells[p_col].language.is_empty() ? p_item->cells[p_col].language : TranslationServer::get_singleton()->get_tool_locale()); + p_item->cells.write[p_col].text_buf->add_string(valtext, font, font_size, p_item->cells[p_col].language); TS->shaped_text_set_bidi_override(p_item->cells[p_col].text_buf->get_rid(), structured_text_parser(p_item->cells[p_col].st_parser, p_item->cells[p_col].st_args, valtext)); p_item->cells.write[p_col].dirty = false; } @@ -4192,7 +4157,7 @@ int Tree::get_column_minimum_width(int p_column) const { // Check if the visible title of the column is wider. if (show_column_titles) { - min_width = MAX(cache.font->get_string_size(columns[p_column].title, cache.font_size).width + cache.bg->get_margin(SIDE_LEFT) + cache.bg->get_margin(SIDE_RIGHT), min_width); + min_width = MAX(cache.font->get_string_size(columns[p_column].title, HORIZONTAL_ALIGNMENT_LEFT, -1, cache.font_size).width + cache.bg->get_margin(SIDE_LEFT) + cache.bg->get_margin(SIDE_RIGHT), min_width); } if (!columns[p_column].clip_content) { @@ -4471,32 +4436,6 @@ Control::TextDirection Tree::get_column_title_direction(int p_column) const { return columns[p_column].text_direction; } -void Tree::clear_column_title_opentype_features(int p_column) { - ERR_FAIL_INDEX(p_column, columns.size()); - columns.write[p_column].opentype_features.clear(); - update_column(p_column); - update(); -} - -void Tree::set_column_title_opentype_feature(int p_column, const String &p_name, int p_value) { - ERR_FAIL_INDEX(p_column, columns.size()); - int32_t tag = TS->name_to_tag(p_name); - if (!columns[p_column].opentype_features.has(tag) || (int)columns[p_column].opentype_features[tag] != p_value) { - columns.write[p_column].opentype_features[tag] = p_value; - update_column(p_column); - update(); - } -} - -int Tree::get_column_title_opentype_feature(int p_column, const String &p_name) const { - ERR_FAIL_INDEX_V(p_column, columns.size(), -1); - int32_t tag = TS->name_to_tag(p_name); - if (!columns[p_column].opentype_features.has(tag)) { - return -1; - } - return columns[p_column].opentype_features[tag]; -} - void Tree::set_column_title_language(int p_column, const String &p_language) { ERR_FAIL_INDEX(p_column, columns.size()); if (columns[p_column].language != p_language) { @@ -4983,10 +4922,6 @@ void Tree::_bind_methods() { ClassDB::bind_method(D_METHOD("set_column_title_direction", "column", "direction"), &Tree::set_column_title_direction); ClassDB::bind_method(D_METHOD("get_column_title_direction", "column"), &Tree::get_column_title_direction); - ClassDB::bind_method(D_METHOD("set_column_title_opentype_feature", "column", "tag", "value"), &Tree::set_column_title_opentype_feature); - ClassDB::bind_method(D_METHOD("get_column_title_opentype_feature", "column", "tag"), &Tree::get_column_title_opentype_feature); - ClassDB::bind_method(D_METHOD("clear_column_title_opentype_features", "column"), &Tree::clear_column_title_opentype_features); - ClassDB::bind_method(D_METHOD("set_column_title_language", "column", "language"), &Tree::set_column_title_language); ClassDB::bind_method(D_METHOD("get_column_title_language", "column"), &Tree::get_column_title_language); diff --git a/scene/gui/tree.h b/scene/gui/tree.h index 0a8dd3204a..65f7ab185c 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -63,7 +63,6 @@ private: String text; String suffix; Ref<TextLine> text_buf; - Dictionary opentype_features; String language; TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT; Array st_args; @@ -220,10 +219,6 @@ public: void set_text_direction(int p_column, Control::TextDirection p_text_direction); Control::TextDirection get_text_direction(int p_column) const; - void set_opentype_feature(int p_column, const String &p_name, int p_value); - int get_opentype_feature(int p_column, const String &p_name) const; - void clear_opentype_features(int p_column); - void set_structured_text_bidi_override(int p_column, TextServer::StructuredTextParser p_parser); TextServer::StructuredTextParser get_structured_text_bidi_override(int p_column) const; @@ -429,7 +424,6 @@ private: bool clip_content = false; String title; Ref<TextLine> text_buf; - Dictionary opentype_features; String language; Control::TextDirection text_direction = Control::TEXT_DIRECTION_INHERITED; ColumnInfo() { @@ -666,10 +660,6 @@ public: void set_column_title_direction(int p_column, Control::TextDirection p_text_direction); Control::TextDirection get_column_title_direction(int p_column) const; - void set_column_title_opentype_feature(int p_column, const String &p_name, int p_value); - int get_column_title_opentype_feature(int p_column, const String &p_name) const; - void clear_column_title_opentype_features(int p_column); - void set_column_title_language(int p_column, const String &p_language); String get_column_title_language(int p_column) const; diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index 3dc358a6c2..5e90615ac1 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -658,24 +658,48 @@ void CanvasItem::draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Tex RenderingServer::get_singleton()->canvas_item_add_multimesh(canvas_item, p_multimesh->get_rid(), texture_rid); } -void CanvasItem::draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, real_t p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint16_t p_flags) const { +void CanvasItem::draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, const Color &p_modulate, uint16_t p_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); ERR_FAIL_COND(p_font.is_null()); - p_font->draw_string(canvas_item, p_pos, p_text, p_alignment, p_width, p_size, p_modulate, p_outline_size, p_outline_modulate, p_flags); + + p_font->draw_string(canvas_item, p_pos, p_text, p_alignment, p_width, p_font_size, p_modulate, p_flags, p_direction, p_orientation); +} + +void CanvasItem::draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, const Color &p_modulate, uint16_t p_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { + ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + ERR_FAIL_COND(p_font.is_null()); + + p_font->draw_multiline_string(canvas_item, p_pos, p_text, p_alignment, p_width, p_font_size, p_max_lines, p_modulate, p_flags, p_direction, p_orientation); } -void CanvasItem::draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, real_t p_width, int p_max_lines, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint16_t p_flags) const { +void CanvasItem::draw_string_outline(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_size, const Color &p_modulate, uint16_t p_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); ERR_FAIL_COND(p_font.is_null()); - p_font->draw_multiline_string(canvas_item, p_pos, p_text, p_alignment, p_width, p_max_lines, p_size, p_modulate, p_outline_size, p_outline_modulate, p_flags); + + p_font->draw_string_outline(canvas_item, p_pos, p_text, p_alignment, p_width, p_font_size, p_size, p_modulate, p_flags, p_direction, p_orientation); } -real_t CanvasItem::draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, const String &p_next, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate) const { - ERR_FAIL_COND_V_MSG(!drawing, 0.f, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); - ERR_FAIL_COND_V(p_font.is_null(), 0.f); - ERR_FAIL_COND_V(p_char.length() != 1, 0.f); +void CanvasItem::draw_multiline_string_outline(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, int p_size, const Color &p_modulate, uint16_t p_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { + ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + ERR_FAIL_COND(p_font.is_null()); + + p_font->draw_multiline_string_outline(canvas_item, p_pos, p_text, p_alignment, p_width, p_font_size, p_max_lines, p_size, p_modulate, p_flags, p_direction, p_orientation); +} + +void CanvasItem::draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, int p_font_size, const Color &p_modulate) const { + ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + ERR_FAIL_COND(p_char.length() != 1); + ERR_FAIL_COND(p_font.is_null()); + + p_font->draw_char(canvas_item, p_pos, p_char[0], p_font_size, p_modulate); +} + +void CanvasItem::draw_char_outline(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, int p_font_size, int p_size, const Color &p_modulate) const { + ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal."); + ERR_FAIL_COND(p_char.length() != 1); + ERR_FAIL_COND(p_font.is_null()); - return p_font->draw_char(canvas_item, p_pos, p_char[0], p_next.get_data()[0], p_size, p_modulate, p_outline_size, p_outline_modulate); + p_font->draw_char_outline(canvas_item, p_pos, p_char[0], p_font_size, p_size, p_modulate); } void CanvasItem::_notify_transform(CanvasItem *p_node) { @@ -900,9 +924,12 @@ void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("draw_primitive", "points", "colors", "uvs", "texture", "width"), &CanvasItem::draw_primitive, DEFVAL(Ref<Texture2D>()), DEFVAL(1.0)); ClassDB::bind_method(D_METHOD("draw_polygon", "points", "colors", "uvs", "texture"), &CanvasItem::draw_polygon, DEFVAL(PackedVector2Array()), DEFVAL(Ref<Texture2D>())); ClassDB::bind_method(D_METHOD("draw_colored_polygon", "points", "color", "uvs", "texture"), &CanvasItem::draw_colored_polygon, DEFVAL(PackedVector2Array()), DEFVAL(Ref<Texture2D>())); - ClassDB::bind_method(D_METHOD("draw_string", "font", "pos", "text", "alignment", "width", "size", "modulate", "outline_size", "outline_modulate", "flags"), &CanvasItem::draw_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND)); - ClassDB::bind_method(D_METHOD("draw_multiline_string", "font", "pos", "text", "alignment", "width", "max_lines", "size", "modulate", "outline_size", "outline_modulate", "flags"), &CanvasItem::draw_multiline_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND)); - ClassDB::bind_method(D_METHOD("draw_char", "font", "pos", "char", "next", "size", "modulate", "outline_size", "outline_modulate"), &CanvasItem::draw_char, DEFVAL(""), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0))); + ClassDB::bind_method(D_METHOD("draw_string", "font", "pos", "text", "alignment", "width", "font_size", "modulate", "flags", "direction", "orientation"), &CanvasItem::draw_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("draw_multiline_string", "font", "pos", "text", "alignment", "width", "font_size", "max_lines", "modulate", "flags", "direction", "orientation"), &CanvasItem::draw_multiline_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("draw_string_outline", "font", "pos", "text", "alignment", "width", "font_size", "size", "modulate", "flags", "direction", "orientation"), &CanvasItem::draw_string_outline, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("draw_multiline_string_outline", "font", "pos", "text", "alignment", "width", "font_size", "max_lines", "size", "modulate", "flags", "direction", "orientation"), &CanvasItem::draw_multiline_string_outline, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("draw_char", "font", "pos", "char", "font_size", "modulate"), &CanvasItem::draw_char, DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(Color(1.0, 1.0, 1.0))); + ClassDB::bind_method(D_METHOD("draw_char_outline", "font", "pos", "char", "font_size", "size", "modulate"), &CanvasItem::draw_char_outline, DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(Color(1.0, 1.0, 1.0))); ClassDB::bind_method(D_METHOD("draw_mesh", "mesh", "texture", "transform", "modulate"), &CanvasItem::draw_mesh, DEFVAL(Transform2D()), DEFVAL(Color(1, 1, 1, 1))); ClassDB::bind_method(D_METHOD("draw_multimesh", "multimesh", "texture"), &CanvasItem::draw_multimesh); ClassDB::bind_method(D_METHOD("draw_set_transform", "position", "rotation", "scale"), &CanvasItem::draw_set_transform, DEFVAL(0.0), DEFVAL(Size2(1.0, 1.0))); diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h index ad64f1ab5e..c88878725f 100644 --- a/scene/main/canvas_item.h +++ b/scene/main/canvas_item.h @@ -235,9 +235,14 @@ public: void draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture2D> &p_texture, const Transform2D &p_transform = Transform2D(), const Color &p_modulate = Color(1, 1, 1)); void draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Texture2D> &p_texture); - void draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, real_t p_width = -1, int p_size = Font::DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; - void draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, real_t p_width = -1, int p_max_lines = -1, int p_size = Font::DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; - real_t draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, const String &p_next = "", int p_size = Font::DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0)) const; + void draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = Font::DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1.0, 1.0, 1.0), uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; + void draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = Font::DEFAULT_FONT_SIZE, int p_max_lines = -1, const Color &p_modulate = Color(1.0, 1.0, 1.0), uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; + + void draw_string_outline(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_size = 1, int p_font_size = Font::DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1.0, 1.0, 1.0), uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; + void draw_multiline_string_outline(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = Font::DEFAULT_FONT_SIZE, int p_max_lines = -1, int p_size = 1, const Color &p_modulate = Color(1.0, 1.0, 1.0), uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; + + void draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, int p_font_size = Font::DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1.0, 1.0, 1.0)) const; + void draw_char_outline(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, int p_font_size = Font::DEFAULT_FONT_SIZE, int p_size = 1, const Color &p_modulate = Color(1.0, 1.0, 1.0)) const; void draw_set_transform(const Point2 &p_offset, real_t p_rot = 0.0, const Size2 &p_scale = Size2(1.0, 1.0)); void draw_set_transform_matrix(const Transform2D &p_matrix); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 1ad011f867..0080e899c3 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -219,7 +219,7 @@ void Viewport::_sub_window_update(Window *p_window) { int close_h_ofs = p_window->get_theme_constant(SNAME("close_h_offset")); int close_v_ofs = p_window->get_theme_constant(SNAME("close_v_offset")); - TextLine title_text = TextLine(p_window->atr(p_window->get_title()), title_font, font_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); + TextLine title_text = TextLine(p_window->atr(p_window->get_title()), title_font, font_size); title_text.set_width(r.size.width - panel->get_minimum_size().x - close_h_ofs); title_text.set_direction(p_window->is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); int x = (r.size.width - title_text.get_size().x) / 2; diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 4d0d6111ec..5c5b60df63 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -853,8 +853,11 @@ void register_scene_types() { GDREGISTER_CLASS(Animation); GDREGISTER_CLASS(AnimationLibrary); - GDREGISTER_CLASS(FontData); - GDREGISTER_CLASS(Font); + + GDREGISTER_ABSTRACT_CLASS(Font); + GDREGISTER_CLASS(FontFile); + GDREGISTER_CLASS(FontVariation); + GDREGISTER_CLASS(Curve); GDREGISTER_CLASS(SceneReplicationConfig); @@ -921,9 +924,9 @@ void register_scene_types() { ClassDB::add_compatibility_class("AnimationTreePlayer", "AnimationTree"); ClassDB::add_compatibility_class("BakedLightmap", "LightmapGI"); ClassDB::add_compatibility_class("BakedLightmapData", "LightmapGIData"); - ClassDB::add_compatibility_class("BitmapFont", "Font"); - ClassDB::add_compatibility_class("DynamicFont", "Font"); - ClassDB::add_compatibility_class("DynamicFontData", "FontData"); + ClassDB::add_compatibility_class("BitmapFont", "FontFile"); + ClassDB::add_compatibility_class("DynamicFont", "FontFile"); + ClassDB::add_compatibility_class("DynamicFontData", "FontFile"); ClassDB::add_compatibility_class("Navigation3D", "Node3D"); ClassDB::add_compatibility_class("Navigation2D", "Node2D"); ClassDB::add_compatibility_class("OpenSimplexNoise", "FastNoiseLite"); @@ -1113,7 +1116,7 @@ void initialize_theme() { ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/custom", PropertyInfo(Variant::STRING, "gui/theme/custom", PROPERTY_HINT_FILE, "*.tres,*.res,*.theme", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); String font_path = GLOBAL_DEF_RST("gui/theme/custom_font", ""); - ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/custom_font", PropertyInfo(Variant::STRING, "gui/theme/custom_font", PROPERTY_HINT_FILE, "*.tres,*.res,*.font", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); + ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/custom_font", PropertyInfo(Variant::STRING, "gui/theme/custom_font", PROPERTY_HINT_FILE, "*.tres,*.res", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); bool font_antialiased = (bool)GLOBAL_DEF_RST("gui/theme/default_font_antialiased", true); ProjectSettings::get_singleton()->set_custom_property_info("gui/theme/default_font_antialiased", PropertyInfo(Variant::BOOL, "gui/theme/default_font_antialiased", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED)); diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index e0e2641f16..9d13ac3a38 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -918,8 +918,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("panel", "TooltipPanel", make_flat_stylebox(Color(0, 0, 0, 0.5), 2 * default_margin, 0.5 * default_margin, 2 * default_margin, 0.5 * default_margin)); - theme->set_font("font", "TooltipLabel", Ref<Font>()); theme->set_font_size("font_size", "TooltipLabel", -1); + theme->set_font("font", "TooltipLabel", Ref<Font>()); theme->set_color("font_color", "TooltipLabel", control_font_color); theme->set_color("font_shadow_color", "TooltipLabel", Color(0, 0, 0, 0)); @@ -939,7 +939,6 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_font("italics_font", "RichTextLabel", italics_font); theme->set_font("bold_italics_font", "RichTextLabel", bold_italics_font); theme->set_font("mono_font", "RichTextLabel", Ref<Font>()); - theme->set_font_size("normal_font_size", "RichTextLabel", -1); theme->set_font_size("bold_font_size", "RichTextLabel", -1); theme->set_font_size("italics_font_size", "RichTextLabel", -1); @@ -1034,9 +1033,9 @@ void make_default_theme(float p_scale, Ref<Font> p_font, TextServer::SubpixelPos Ref<StyleBox> default_style; Ref<Texture2D> default_icon; Ref<Font> default_font; - Ref<Font> bold_font; - Ref<Font> bold_italics_font; - Ref<Font> italics_font; + Ref<FontVariation> bold_font; + Ref<FontVariation> bold_italics_font; + Ref<FontVariation> italics_font; float default_scale = CLAMP(p_scale, 0.5, 8.0); if (p_font.is_valid()) { @@ -1046,48 +1045,31 @@ void make_default_theme(float p_scale, Ref<Font> p_font, TextServer::SubpixelPos // Use the default DynamicFont (separate from the editor font). // The default DynamicFont is chosen to have a small file size since it's // embedded in both editor and export template binaries. - Ref<Font> dynamic_font; + Ref<FontFile> dynamic_font; dynamic_font.instantiate(); - - Ref<FontData> dynamic_font_data; - dynamic_font_data.instantiate(); - dynamic_font_data->set_data_ptr(_font_OpenSans_SemiBold, _font_OpenSans_SemiBold_size); - dynamic_font_data->set_subpixel_positioning(p_font_subpixel); - dynamic_font_data->set_hinting(p_font_hinting); - dynamic_font_data->set_antialiased(p_font_antialiased); - dynamic_font_data->set_multichannel_signed_distance_field(p_font_msdf); - dynamic_font_data->set_generate_mipmaps(p_font_generate_mipmaps); - - dynamic_font->add_data(dynamic_font_data); + dynamic_font->set_data_ptr(_font_OpenSans_SemiBold, _font_OpenSans_SemiBold_size); + dynamic_font->set_subpixel_positioning(p_font_subpixel); + dynamic_font->set_hinting(p_font_hinting); + dynamic_font->set_antialiased(p_font_antialiased); + dynamic_font->set_multichannel_signed_distance_field(p_font_msdf); + dynamic_font->set_generate_mipmaps(p_font_generate_mipmaps); default_font = dynamic_font; } if (default_font.is_valid()) { bold_font.instantiate(); - for (int i = 0; i < default_font->get_data_count(); i++) { - Ref<FontData> data = default_font->get_data(i)->duplicate(); - // Try to match OpenSans ExtraBold. - data->set_embolden(1.2); - bold_font->add_data(data); - } + bold_font->set_base_font(default_font); + bold_font->set_variation_embolden(1.2); bold_italics_font.instantiate(); - for (int i = 0; i < default_font->get_data_count(); i++) { - Ref<FontData> data = default_font->get_data(i)->duplicate(); - // Try to match OpenSans ExtraBold Italic. - data->set_embolden(1.2); - data->set_transform(Transform2D(1.0, 0.2, 0.0, 1.0, 0.0, 0.0)); - bold_italics_font->add_data(data); - } + bold_italics_font->set_base_font(default_font); + bold_italics_font->set_variation_embolden(1.2); + bold_italics_font->set_variation_transform(Transform2D(1.0, 0.2, 0.0, 1.0, 0.0, 0.0)); italics_font.instantiate(); - for (int i = 0; i < default_font->get_data_count(); i++) { - Ref<FontData> data = default_font->get_data(i)->duplicate(); - // Try to match OpenSans Italic. - data->set_transform(Transform2D(1.0, 0.2, 0.0, 1.0, 0.0, 0.0)); - italics_font->add_data(data); - } + italics_font->set_base_font(default_font); + italics_font->set_variation_transform(Transform2D(1.0, 0.2, 0.0, 1.0, 0.0, 0.0)); } fill_default_theme(t, default_font, bold_font, bold_italics_font, italics_font, default_icon, default_style, default_scale); diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index 46f23424dd..6053d27ef7 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -30,6 +30,7 @@ #include "font.h" +#include "core/core_string_names.h" #include "core/io/image_loader.h" #include "core/io/resource_loader.h" #include "core/string/translation.h" @@ -37,439 +38,539 @@ #include "core/templates/hashfuncs.h" #include "scene/resources/text_line.h" #include "scene/resources/text_paragraph.h" +#include "scene/resources/theme.h" -_FORCE_INLINE_ void FontData::_clear_cache() { - for (int i = 0; i < cache.size(); i++) { - if (cache[i].is_valid()) { - TS->free_rid(cache[i]); - cache.write[i] = RID(); - } - } -} +/*************************************************************************/ +/* Font */ +/*************************************************************************/ -_FORCE_INLINE_ void FontData::_ensure_rid(int p_cache_index) const { - if (unlikely(p_cache_index >= cache.size())) { - cache.resize(p_cache_index + 1); - } - if (unlikely(!cache[p_cache_index].is_valid())) { - cache.write[p_cache_index] = TS->create_font(); - TS->font_set_data_ptr(cache[p_cache_index], data_ptr, data_size); - TS->font_set_face_index(cache[p_cache_index], face_index); - TS->font_set_antialiased(cache[p_cache_index], antialiased); - TS->font_set_generate_mipmaps(cache[p_cache_index], mipmaps); - TS->font_set_multichannel_signed_distance_field(cache[p_cache_index], msdf); - TS->font_set_msdf_pixel_range(cache[p_cache_index], msdf_pixel_range); - TS->font_set_msdf_size(cache[p_cache_index], msdf_size); - TS->font_set_fixed_size(cache[p_cache_index], fixed_size); - TS->font_set_force_autohinter(cache[p_cache_index], force_autohinter); - TS->font_set_hinting(cache[p_cache_index], hinting); - TS->font_set_subpixel_positioning(cache[p_cache_index], subpixel_positioning); - TS->font_set_embolden(cache[p_cache_index], embolden); - TS->font_set_transform(cache[p_cache_index], transform); - TS->font_set_oversampling(cache[p_cache_index], oversampling); - } -} +void Font::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_fallbacks", "fallbacks"), &Font::set_fallbacks); + ClassDB::bind_method(D_METHOD("get_fallbacks"), &Font::get_fallbacks); -void FontData::_bind_methods() { - ClassDB::bind_method(D_METHOD("load_bitmap_font", "path"), &FontData::load_bitmap_font); - ClassDB::bind_method(D_METHOD("load_dynamic_font", "path"), &FontData::load_dynamic_font); + // Output. + ClassDB::bind_method(D_METHOD("find_variation", "variation_coordinates", "face_index", "strength", "transform"), &Font::find_variation, DEFVAL(0), DEFVAL(0.0), DEFVAL(Transform2D())); + ClassDB::bind_method(D_METHOD("get_rids"), &Font::get_rids); - ClassDB::bind_method(D_METHOD("set_data", "data"), &FontData::set_data); - ClassDB::bind_method(D_METHOD("get_data"), &FontData::get_data); + // Font metrics. + ClassDB::bind_method(D_METHOD("get_height", "font_size"), &Font::get_height, DEFVAL(DEFAULT_FONT_SIZE)); + ClassDB::bind_method(D_METHOD("get_ascent", "font_size"), &Font::get_ascent, DEFVAL(DEFAULT_FONT_SIZE)); + ClassDB::bind_method(D_METHOD("get_descent", "font_size"), &Font::get_descent, DEFVAL(DEFAULT_FONT_SIZE)); + ClassDB::bind_method(D_METHOD("get_underline_position", "font_size"), &Font::get_underline_position, DEFVAL(DEFAULT_FONT_SIZE)); + ClassDB::bind_method(D_METHOD("get_underline_thickness", "font_size"), &Font::get_underline_thickness, DEFVAL(DEFAULT_FONT_SIZE)); - ClassDB::bind_method(D_METHOD("set_face_index", "face_index"), &FontData::set_face_index); - ClassDB::bind_method(D_METHOD("get_face_index"), &FontData::get_face_index); + ClassDB::bind_method(D_METHOD("get_font_name"), &Font::get_font_name); + ClassDB::bind_method(D_METHOD("get_font_style_name"), &Font::get_font_style_name); + ClassDB::bind_method(D_METHOD("get_font_style"), &Font::get_font_style); - ClassDB::bind_method(D_METHOD("get_face_count"), &FontData::get_face_count); + ClassDB::bind_method(D_METHOD("get_spacing", "spacing"), &Font::get_spacing); + ClassDB::bind_method(D_METHOD("get_opentype_features"), &Font::get_opentype_features); - ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &FontData::set_antialiased); - ClassDB::bind_method(D_METHOD("is_antialiased"), &FontData::is_antialiased); + // Drawing string. + ClassDB::bind_method(D_METHOD("set_cache_capacity", "single_line", "multi_line"), &Font::set_cache_capacity); - ClassDB::bind_method(D_METHOD("set_generate_mipmaps", "generate_mipmaps"), &FontData::set_generate_mipmaps); - ClassDB::bind_method(D_METHOD("get_generate_mipmaps"), &FontData::get_generate_mipmaps); + ClassDB::bind_method(D_METHOD("get_string_size", "text", "alignment", "width", "font_size", "flags", "direction", "orientation"), &Font::get_string_size, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("get_multiline_string_size", "text", "alignment", "width", "font_size", "max_lines", "flags", "direction", "orientation"), &Font::get_multiline_string_size, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); - ClassDB::bind_method(D_METHOD("set_font_name", "name"), &FontData::set_font_name); - ClassDB::bind_method(D_METHOD("get_font_name"), &FontData::get_font_name); + ClassDB::bind_method(D_METHOD("draw_string", "canvas_item", "pos", "text", "alignment", "width", "font_size", "modulate", "flags", "direction", "orientation"), &Font::draw_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("draw_multiline_string", "canvas_item", "pos", "text", "alignment", "width", "font_size", "max_lines", "modulate", "flags", "direction", "orientation"), &Font::draw_multiline_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); - ClassDB::bind_method(D_METHOD("set_font_style_name", "name"), &FontData::set_font_style_name); - ClassDB::bind_method(D_METHOD("get_font_style_name"), &FontData::get_font_style_name); + ClassDB::bind_method(D_METHOD("draw_string_outline", "canvas_item", "pos", "text", "alignment", "width", "font_size", "size", "modulate", "flags", "direction", "orientation"), &Font::draw_string_outline, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); + ClassDB::bind_method(D_METHOD("draw_multiline_string_outline", "canvas_item", "pos", "text", "alignment", "width", "font_size", "max_lines", "size", "modulate", "flags", "direction", "orientation"), &Font::draw_multiline_string_outline, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL)); - ClassDB::bind_method(D_METHOD("set_font_style", "style"), &FontData::set_font_style); - ClassDB::bind_method(D_METHOD("get_font_style"), &FontData::get_font_style); + // Drawing char. + ClassDB::bind_method(D_METHOD("get_char_size", "char"), &Font::get_char_size); + ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "pos", "char", "modulate"), &Font::draw_char, DEFVAL(Color(1.0, 1.0, 1.0))); + ClassDB::bind_method(D_METHOD("draw_char_outline", "canvas_item", "pos", "char", "size", "modulate"), &Font::draw_char_outline, DEFVAL(-1), DEFVAL(Color(1.0, 1.0, 1.0))); - ClassDB::bind_method(D_METHOD("set_multichannel_signed_distance_field", "msdf"), &FontData::set_multichannel_signed_distance_field); - ClassDB::bind_method(D_METHOD("is_multichannel_signed_distance_field"), &FontData::is_multichannel_signed_distance_field); + // Helper functions. + ClassDB::bind_method(D_METHOD("has_char", "char"), &Font::has_char); + ClassDB::bind_method(D_METHOD("get_supported_chars"), &Font::get_supported_chars); - ClassDB::bind_method(D_METHOD("set_msdf_pixel_range", "msdf_pixel_range"), &FontData::set_msdf_pixel_range); - ClassDB::bind_method(D_METHOD("get_msdf_pixel_range"), &FontData::get_msdf_pixel_range); + ClassDB::bind_method(D_METHOD("is_language_supported", "language"), &Font::is_language_supported); + ClassDB::bind_method(D_METHOD("is_script_supported", "script"), &Font::is_script_supported); - ClassDB::bind_method(D_METHOD("set_msdf_size", "msdf_size"), &FontData::set_msdf_size); - ClassDB::bind_method(D_METHOD("get_msdf_size"), &FontData::get_msdf_size); + ClassDB::bind_method(D_METHOD("get_supported_feature_list"), &Font::get_supported_feature_list); + ClassDB::bind_method(D_METHOD("get_supported_variation_list"), &Font::get_supported_variation_list); + ClassDB::bind_method(D_METHOD("get_face_count"), &Font::get_face_count); +} - ClassDB::bind_method(D_METHOD("set_fixed_size", "fixed_size"), &FontData::set_fixed_size); - ClassDB::bind_method(D_METHOD("get_fixed_size"), &FontData::get_fixed_size); +void Font::_update_rids_fb(const Ref<Font> &p_f, int p_depth) const { + ERR_FAIL_COND(p_depth > MAX_FALLBACK_DEPTH); + if (p_f.is_valid()) { + RID rid = p_f->_get_rid(); + if (rid.is_valid()) { + rids.push_back(rid); + } + const TypedArray<Font> &_fallbacks = p_f->get_fallbacks(); + for (int i = 0; i < _fallbacks.size(); i++) { + _update_rids_fb(_fallbacks[i], p_depth + 1); + } + } +} - ClassDB::bind_method(D_METHOD("set_force_autohinter", "force_autohinter"), &FontData::set_force_autohinter); - ClassDB::bind_method(D_METHOD("is_force_autohinter"), &FontData::is_force_autohinter); +void Font::_update_rids() const { + rids.clear(); + _update_rids_fb(const_cast<Font *>(this), 0); + dirty_rids = false; +} - ClassDB::bind_method(D_METHOD("set_hinting", "hinting"), &FontData::set_hinting); - ClassDB::bind_method(D_METHOD("get_hinting"), &FontData::get_hinting); +void Font::_invalidate_rids() { + rids.clear(); + dirty_rids = true; - ClassDB::bind_method(D_METHOD("set_subpixel_positioning", "subpixel_positioning"), &FontData::set_subpixel_positioning); - ClassDB::bind_method(D_METHOD("get_subpixel_positioning"), &FontData::get_subpixel_positioning); + cache.clear(); + cache_wrap.clear(); - ClassDB::bind_method(D_METHOD("set_embolden", "strength"), &FontData::set_embolden); - ClassDB::bind_method(D_METHOD("get_embolden"), &FontData::get_embolden); + emit_changed(); +} - ClassDB::bind_method(D_METHOD("set_transform", "transform"), &FontData::set_transform); - ClassDB::bind_method(D_METHOD("get_transform"), &FontData::get_transform); +bool Font::_is_cyclic(const Ref<Font> &p_f, int p_depth) const { + ERR_FAIL_COND_V(p_depth > MAX_FALLBACK_DEPTH, false); + if (p_f.is_null()) { + return false; + } + for (int i = 0; i < p_f->fallbacks.size(); i++) { + const Ref<Font> &f = p_f->fallbacks[i]; + if (f == this) { + return true; + } + return _is_cyclic(f, p_depth + 1); + } + return false; +} - ClassDB::bind_method(D_METHOD("set_oversampling", "oversampling"), &FontData::set_oversampling); - ClassDB::bind_method(D_METHOD("get_oversampling"), &FontData::get_oversampling); +void Font::reset_state() { + _invalidate_rids(); +} - ClassDB::bind_method(D_METHOD("find_cache", "variation_coordinates"), &FontData::find_cache); +// Fallbacks. +void Font::set_fallbacks(const TypedArray<Font> &p_fallbacks) { + ERR_FAIL_COND(_is_cyclic(this, 0)); + for (int i = 0; i < fallbacks.size(); i++) { + Ref<Font> f = fallbacks[i]; + if (f.is_valid()) { + f->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Font::_invalidate_rids)); + } + } + fallbacks = p_fallbacks; + for (int i = 0; i < fallbacks.size(); i++) { + Ref<Font> f = fallbacks[i]; + if (f.is_valid()) { + f->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED); + } + } + _invalidate_rids(); +} - ClassDB::bind_method(D_METHOD("get_cache_count"), &FontData::get_cache_count); - ClassDB::bind_method(D_METHOD("clear_cache"), &FontData::clear_cache); - ClassDB::bind_method(D_METHOD("remove_cache", "cache_index"), &FontData::remove_cache); +TypedArray<Font> Font::get_fallbacks() const { + return fallbacks; +} - ClassDB::bind_method(D_METHOD("get_size_cache_list", "cache_index"), &FontData::get_size_cache_list); - ClassDB::bind_method(D_METHOD("clear_size_cache", "cache_index"), &FontData::clear_size_cache); - ClassDB::bind_method(D_METHOD("remove_size_cache", "cache_index", "size"), &FontData::remove_size_cache); +// Output. +TypedArray<RID> Font::get_rids() const { + if (dirty_rids) { + _update_rids(); + } + return rids; +} - ClassDB::bind_method(D_METHOD("set_variation_coordinates", "cache_index", "variation_coordinates"), &FontData::set_variation_coordinates); - ClassDB::bind_method(D_METHOD("get_variation_coordinates", "cache_index"), &FontData::get_variation_coordinates); +// Drawing string. +real_t Font::get_height(int p_font_size) const { + if (dirty_rids) { + _update_rids(); + } + real_t ret = 0.f; + for (int i = 0; i < rids.size(); i++) { + ret = MAX(ret, TS->font_get_ascent(rids[i], p_font_size) + TS->font_get_descent(rids[i], p_font_size)); + } + return ret + get_spacing(TextServer::SPACING_BOTTOM) + get_spacing(TextServer::SPACING_TOP); +} - ClassDB::bind_method(D_METHOD("set_ascent", "cache_index", "size", "ascent"), &FontData::set_ascent); - ClassDB::bind_method(D_METHOD("get_ascent", "cache_index", "size"), &FontData::get_ascent); +real_t Font::get_ascent(int p_font_size) const { + if (dirty_rids) { + _update_rids(); + } + real_t ret = 0.f; + for (int i = 0; i < rids.size(); i++) { + ret = MAX(ret, TS->font_get_ascent(rids[i], p_font_size)); + } + return ret + get_spacing(TextServer::SPACING_TOP); +} + +real_t Font::get_descent(int p_font_size) const { + if (dirty_rids) { + _update_rids(); + } + real_t ret = 0.f; + for (int i = 0; i < rids.size(); i++) { + ret = MAX(ret, TS->font_get_descent(rids[i], p_font_size)); + } + return ret + get_spacing(TextServer::SPACING_BOTTOM); +} + +real_t Font::get_underline_position(int p_font_size) const { + if (dirty_rids) { + _update_rids(); + } + real_t ret = 0.f; + for (int i = 0; i < rids.size(); i++) { + ret = MAX(ret, TS->font_get_underline_position(rids[i], p_font_size)); + } + return ret + get_spacing(TextServer::SPACING_TOP); +} - ClassDB::bind_method(D_METHOD("set_descent", "cache_index", "size", "descent"), &FontData::set_descent); - ClassDB::bind_method(D_METHOD("get_descent", "cache_index", "size"), &FontData::get_descent); +real_t Font::get_underline_thickness(int p_font_size) const { + if (dirty_rids) { + _update_rids(); + } + real_t ret = 0.f; + for (int i = 0; i < rids.size(); i++) { + ret = MAX(ret, TS->font_get_underline_thickness(rids[i], p_font_size)); + } + return ret; +} - ClassDB::bind_method(D_METHOD("set_underline_position", "cache_index", "size", "underline_position"), &FontData::set_underline_position); - ClassDB::bind_method(D_METHOD("get_underline_position", "cache_index", "size"), &FontData::get_underline_position); +String Font::get_font_name() const { + return TS->font_get_name(_get_rid()); +} - ClassDB::bind_method(D_METHOD("set_underline_thickness", "cache_index", "size", "underline_thickness"), &FontData::set_underline_thickness); - ClassDB::bind_method(D_METHOD("get_underline_thickness", "cache_index", "size"), &FontData::get_underline_thickness); +String Font::get_font_style_name() const { + return TS->font_get_style_name(_get_rid()); +} - ClassDB::bind_method(D_METHOD("set_scale", "cache_index", "size", "scale"), &FontData::set_scale); - ClassDB::bind_method(D_METHOD("get_scale", "cache_index", "size"), &FontData::get_scale); +uint32_t Font::get_font_style() const { + return TS->font_get_style(_get_rid()); +} - ClassDB::bind_method(D_METHOD("set_spacing", "cache_index", "size", "spacing_type", "value"), &FontData::set_spacing); - ClassDB::bind_method(D_METHOD("get_spacing", "cache_index", "size", "spacing_type"), &FontData::get_spacing); +Dictionary Font::get_opentype_features() const { + return Dictionary(); +} + +// Drawing string. +void Font::set_cache_capacity(int p_single_line, int p_multi_line) { + cache.set_capacity(p_single_line); + cache_wrap.set_capacity(p_multi_line); +} - ClassDB::bind_method(D_METHOD("get_texture_count", "cache_index", "size"), &FontData::get_texture_count); - ClassDB::bind_method(D_METHOD("clear_textures", "cache_index", "size"), &FontData::clear_textures); - ClassDB::bind_method(D_METHOD("remove_texture", "cache_index", "size", "texture_index"), &FontData::remove_texture); +Size2 Font::get_string_size(const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, uint16_t p_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { + uint64_t hash = p_text.hash64(); + hash = hash_djb2_one_64(p_font_size, hash); + if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { + hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); + hash = hash_djb2_one_64(p_flags, hash); + hash = hash_djb2_one_64(p_direction, hash); + hash = hash_djb2_one_64(p_orientation, hash); + } - ClassDB::bind_method(D_METHOD("set_texture_image", "cache_index", "size", "texture_index", "image"), &FontData::set_texture_image); - ClassDB::bind_method(D_METHOD("get_texture_image", "cache_index", "size", "texture_index"), &FontData::get_texture_image); + Ref<TextLine> buffer; + if (cache.has(hash)) { + buffer = cache.get(hash); + } else { + buffer.instantiate(); + buffer->set_direction(p_direction); + buffer->set_orientation(p_orientation); + buffer->add_string(p_text, Ref<Font>(this), p_font_size); + cache.insert(hash, buffer); + } + return buffer->get_size(); +} + +Size2 Font::get_multiline_string_size(const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, uint16_t p_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { + uint64_t hash = p_text.hash64(); + hash = hash_djb2_one_64(p_font_size, hash); + hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); + hash = hash_djb2_one_64(p_flags, hash); + hash = hash_djb2_one_64(p_direction, hash); + hash = hash_djb2_one_64(p_orientation, hash); + + Ref<TextParagraph> lines_buffer; + if (cache_wrap.has(hash)) { + lines_buffer = cache_wrap.get(hash); + } else { + lines_buffer.instantiate(); + lines_buffer->set_direction(p_direction); + lines_buffer->set_orientation(p_orientation); + lines_buffer->add_string(p_text, Ref<Font>(this), p_font_size); + lines_buffer->set_width(p_width); + lines_buffer->set_flags(p_flags); + cache_wrap.insert(hash, lines_buffer); + } + + lines_buffer->set_alignment(p_alignment); + lines_buffer->set_max_lines_visible(p_max_lines); + + return lines_buffer->get_size(); +} + +void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, const Color &p_modulate, uint16_t p_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { + uint64_t hash = p_text.hash64(); + hash = hash_djb2_one_64(p_font_size, hash); + if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { + hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); + hash = hash_djb2_one_64(p_flags, hash); + } + + Ref<TextLine> buffer; + if (cache.has(hash)) { + buffer = cache.get(hash); + } else { + buffer.instantiate(); + buffer->set_direction(p_direction); + buffer->set_orientation(p_orientation); + buffer->add_string(p_text, Ref<Font>(this), p_font_size); + cache.insert(hash, buffer); + } - ClassDB::bind_method(D_METHOD("set_texture_offsets", "cache_index", "size", "texture_index", "offset"), &FontData::set_texture_offsets); - ClassDB::bind_method(D_METHOD("get_texture_offsets", "cache_index", "size", "texture_index"), &FontData::get_texture_offsets); + Vector2 ofs = p_pos; + if (p_orientation == TextServer::ORIENTATION_HORIZONTAL) { + ofs.y -= buffer->get_line_ascent(); + } else { + ofs.x -= buffer->get_line_ascent(); + } - ClassDB::bind_method(D_METHOD("get_glyph_list", "cache_index", "size"), &FontData::get_glyph_list); - ClassDB::bind_method(D_METHOD("clear_glyphs", "cache_index", "size"), &FontData::clear_glyphs); - ClassDB::bind_method(D_METHOD("remove_glyph", "cache_index", "size", "glyph"), &FontData::remove_glyph); + buffer->set_width(p_width); + buffer->set_horizontal_alignment(p_alignment); + buffer->set_flags(p_flags); - ClassDB::bind_method(D_METHOD("set_glyph_advance", "cache_index", "size", "glyph", "advance"), &FontData::set_glyph_advance); - ClassDB::bind_method(D_METHOD("get_glyph_advance", "cache_index", "size", "glyph"), &FontData::get_glyph_advance); + buffer->draw(p_canvas_item, ofs, p_modulate); +} - ClassDB::bind_method(D_METHOD("set_glyph_offset", "cache_index", "size", "glyph", "offset"), &FontData::set_glyph_offset); - ClassDB::bind_method(D_METHOD("get_glyph_offset", "cache_index", "size", "glyph"), &FontData::get_glyph_offset); +void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, const Color &p_modulate, uint16_t p_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { + uint64_t hash = p_text.hash64(); + hash = hash_djb2_one_64(p_font_size, hash); + hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); + hash = hash_djb2_one_64(p_flags, hash); + hash = hash_djb2_one_64(p_direction, hash); + hash = hash_djb2_one_64(p_orientation, hash); - ClassDB::bind_method(D_METHOD("set_glyph_size", "cache_index", "size", "glyph", "gl_size"), &FontData::set_glyph_size); - ClassDB::bind_method(D_METHOD("get_glyph_size", "cache_index", "size", "glyph"), &FontData::get_glyph_size); + Ref<TextParagraph> lines_buffer; + if (cache_wrap.has(hash)) { + lines_buffer = cache_wrap.get(hash); + } else { + lines_buffer.instantiate(); + lines_buffer->set_direction(p_direction); + lines_buffer->set_orientation(p_orientation); + lines_buffer->add_string(p_text, Ref<Font>(this), p_font_size); + lines_buffer->set_width(p_width); + lines_buffer->set_flags(p_flags); + cache_wrap.insert(hash, lines_buffer); + } - ClassDB::bind_method(D_METHOD("set_glyph_uv_rect", "cache_index", "size", "glyph", "uv_rect"), &FontData::set_glyph_uv_rect); - ClassDB::bind_method(D_METHOD("get_glyph_uv_rect", "cache_index", "size", "glyph"), &FontData::get_glyph_uv_rect); + Vector2 ofs = p_pos; + if (p_orientation == TextServer::ORIENTATION_HORIZONTAL) { + ofs.y -= lines_buffer->get_line_ascent(0); + } else { + ofs.x -= lines_buffer->get_line_ascent(0); + } - ClassDB::bind_method(D_METHOD("set_glyph_texture_idx", "cache_index", "size", "glyph", "texture_idx"), &FontData::set_glyph_texture_idx); - ClassDB::bind_method(D_METHOD("get_glyph_texture_idx", "cache_index", "size", "glyph"), &FontData::get_glyph_texture_idx); + lines_buffer->set_alignment(p_alignment); + lines_buffer->set_max_lines_visible(p_max_lines); - ClassDB::bind_method(D_METHOD("get_kerning_list", "cache_index", "size"), &FontData::get_kerning_list); - ClassDB::bind_method(D_METHOD("clear_kerning_map", "cache_index", "size"), &FontData::clear_kerning_map); - ClassDB::bind_method(D_METHOD("remove_kerning", "cache_index", "size", "glyph_pair"), &FontData::remove_kerning); + lines_buffer->draw(p_canvas_item, ofs, p_modulate); +} - ClassDB::bind_method(D_METHOD("set_kerning", "cache_index", "size", "glyph_pair", "kerning"), &FontData::set_kerning); - ClassDB::bind_method(D_METHOD("get_kerning", "cache_index", "size", "glyph_pair"), &FontData::get_kerning); +void Font::draw_string_outline(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_size, const Color &p_modulate, uint16_t p_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { + uint64_t hash = p_text.hash64(); + hash = hash_djb2_one_64(p_font_size, hash); + if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { + hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); + hash = hash_djb2_one_64(p_flags, hash); + } - ClassDB::bind_method(D_METHOD("render_range", "cache_index", "size", "start", "end"), &FontData::render_range); - ClassDB::bind_method(D_METHOD("render_glyph", "cache_index", "size", "index"), &FontData::render_glyph); + Ref<TextLine> buffer; + if (cache.has(hash)) { + buffer = cache.get(hash); + } else { + buffer.instantiate(); + buffer->set_direction(p_direction); + buffer->set_orientation(p_orientation); + buffer->add_string(p_text, Ref<Font>(this), p_font_size); + cache.insert(hash, buffer); + } - ClassDB::bind_method(D_METHOD("get_cache_rid", "cache_index"), &FontData::get_cache_rid); + Vector2 ofs = p_pos; + if (p_orientation == TextServer::ORIENTATION_HORIZONTAL) { + ofs.y -= buffer->get_line_ascent(); + } else { + ofs.x -= buffer->get_line_ascent(); + } - ClassDB::bind_method(D_METHOD("is_language_supported", "language"), &FontData::is_language_supported); - ClassDB::bind_method(D_METHOD("set_language_support_override", "language", "supported"), &FontData::set_language_support_override); - ClassDB::bind_method(D_METHOD("get_language_support_override", "language"), &FontData::get_language_support_override); - ClassDB::bind_method(D_METHOD("remove_language_support_override", "language"), &FontData::remove_language_support_override); - ClassDB::bind_method(D_METHOD("get_language_support_overrides"), &FontData::get_language_support_overrides); + buffer->set_width(p_width); + buffer->set_horizontal_alignment(p_alignment); + buffer->set_flags(p_flags); - ClassDB::bind_method(D_METHOD("is_script_supported", "script"), &FontData::is_script_supported); - ClassDB::bind_method(D_METHOD("set_script_support_override", "script", "supported"), &FontData::set_script_support_override); - ClassDB::bind_method(D_METHOD("get_script_support_override", "script"), &FontData::get_script_support_override); - ClassDB::bind_method(D_METHOD("remove_script_support_override", "script"), &FontData::remove_script_support_override); - ClassDB::bind_method(D_METHOD("get_script_support_overrides"), &FontData::get_script_support_overrides); + buffer->draw_outline(p_canvas_item, ofs, p_size, p_modulate); +} - ClassDB::bind_method(D_METHOD("set_opentype_feature_overrides", "overrides"), &FontData::set_opentype_feature_overrides); - ClassDB::bind_method(D_METHOD("get_opentype_feature_overrides"), &FontData::get_opentype_feature_overrides); +void Font::draw_multiline_string_outline(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, int p_size, const Color &p_modulate, uint16_t p_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const { + uint64_t hash = p_text.hash64(); + hash = hash_djb2_one_64(p_font_size, hash); + hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); + hash = hash_djb2_one_64(p_flags, hash); + hash = hash_djb2_one_64(p_direction, hash); + hash = hash_djb2_one_64(p_orientation, hash); - ClassDB::bind_method(D_METHOD("has_char", "char"), &FontData::has_char); - ClassDB::bind_method(D_METHOD("get_supported_chars"), &FontData::get_supported_chars); + Ref<TextParagraph> lines_buffer; + if (cache_wrap.has(hash)) { + lines_buffer = cache_wrap.get(hash); + } else { + lines_buffer.instantiate(); + lines_buffer->set_direction(p_direction); + lines_buffer->set_orientation(p_orientation); + lines_buffer->add_string(p_text, Ref<Font>(this), p_font_size); + lines_buffer->set_width(p_width); + lines_buffer->set_flags(p_flags); + cache_wrap.insert(hash, lines_buffer); + } - ClassDB::bind_method(D_METHOD("get_glyph_index", "size", "char", "variation_selector"), &FontData::get_glyph_index); + Vector2 ofs = p_pos; + if (p_orientation == TextServer::ORIENTATION_HORIZONTAL) { + ofs.y -= lines_buffer->get_line_ascent(0); + } else { + ofs.x -= lines_buffer->get_line_ascent(0); + } - ClassDB::bind_method(D_METHOD("get_supported_feature_list"), &FontData::get_supported_feature_list); - ClassDB::bind_method(D_METHOD("get_supported_variation_list"), &FontData::get_supported_variation_list); + lines_buffer->set_alignment(p_alignment); + lines_buffer->set_max_lines_visible(p_max_lines); - ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_data", "get_data"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "face_index", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_face_index", "get_face_index"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "generate_mipmaps", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_generate_mipmaps", "get_generate_mipmaps"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "antialiased", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_antialiased", "is_antialiased"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "font_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_name", "get_font_name"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "style_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_style_name", "get_font_style_name"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "font_style", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_style", "get_font_style"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One half of a pixel,One quarter of a pixel", PROPERTY_USAGE_STORAGE), "set_subpixel_positioning", "get_subpixel_positioning"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "embolden", PROPERTY_HINT_RANGE, "-2,2,0.01", PROPERTY_USAGE_STORAGE), "set_embolden", "get_embolden"); - ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_transform", "get_transform"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_multichannel_signed_distance_field", "is_multichannel_signed_distance_field"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_pixel_range", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_msdf_pixel_range", "get_msdf_pixel_range"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_msdf_size", "get_msdf_size"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "force_autohinter", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_force_autohinter", "is_force_autohinter"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_STORAGE), "set_hinting", "get_hinting"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_oversampling", "get_oversampling"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_fixed_size", "get_fixed_size"); - ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "opentype_feature_overrides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_opentype_feature_overrides", "get_opentype_feature_overrides"); + lines_buffer->draw_outline(p_canvas_item, ofs, p_size, p_modulate); } -bool FontData::_set(const StringName &p_name, const Variant &p_value) { - Vector<String> tokens = p_name.operator String().split("/"); - if (tokens.size() == 2 && tokens[0] == "language_support_override") { - String lang = tokens[1]; - set_language_support_override(lang, p_value); - return true; - } else if (tokens.size() == 2 && tokens[0] == "script_support_override") { - String script = tokens[1]; - set_script_support_override(script, p_value); - return true; - } else if (tokens.size() >= 3 && tokens[0] == "cache") { - int cache_index = tokens[1].to_int(); - if (tokens.size() == 3 && tokens[2] == "variation_coordinates") { - set_variation_coordinates(cache_index, p_value); - return true; - } - if (tokens.size() >= 5) { - Vector2i sz = Vector2i(tokens[2].to_int(), tokens[3].to_int()); - if (tokens[4] == "ascent") { - set_ascent(cache_index, sz.x, p_value); - return true; - } else if (tokens[4] == "descent") { - set_descent(cache_index, sz.x, p_value); - return true; - } else if (tokens[4] == "underline_position") { - set_underline_position(cache_index, sz.x, p_value); - return true; - } else if (tokens[4] == "underline_thickness") { - set_underline_thickness(cache_index, sz.x, p_value); - return true; - } else if (tokens[4] == "scale") { - set_scale(cache_index, sz.x, p_value); - return true; - } else if (tokens[4] == "spacing_glyph") { - set_spacing(cache_index, sz.x, TextServer::SPACING_GLYPH, p_value); - return true; - } else if (tokens[4] == "spacing_space") { - set_spacing(cache_index, sz.x, TextServer::SPACING_SPACE, p_value); - return true; - } else if (tokens.size() == 7 && tokens[4] == "textures") { - int texture_index = tokens[5].to_int(); - if (tokens[6] == "image") { - set_texture_image(cache_index, sz, texture_index, p_value); - return true; - } else if (tokens[6] == "offsets") { - set_texture_offsets(cache_index, sz, texture_index, p_value); - return true; - } - } else if (tokens.size() == 7 && tokens[4] == "glyphs") { - int32_t glyph_index = tokens[5].to_int(); - if (tokens[6] == "advance") { - set_glyph_advance(cache_index, sz.x, glyph_index, p_value); - return true; - } else if (tokens[6] == "offset") { - set_glyph_offset(cache_index, sz, glyph_index, p_value); - return true; - } else if (tokens[6] == "size") { - set_glyph_size(cache_index, sz, glyph_index, p_value); - return true; - } else if (tokens[6] == "uv_rect") { - set_glyph_uv_rect(cache_index, sz, glyph_index, p_value); - return true; - } else if (tokens[6] == "texture_idx") { - set_glyph_texture_idx(cache_index, sz, glyph_index, p_value); - return true; - } - } else if (tokens.size() == 7 && tokens[4] == "kerning_overrides") { - Vector2i gp = Vector2i(tokens[5].to_int(), tokens[6].to_int()); - set_kerning(cache_index, sz.x, gp, p_value); - return true; - } +// Drawing char. +Size2 Font::get_char_size(char32_t p_char, int p_font_size) const { + if (dirty_rids) { + _update_rids(); + } + for (int i = 0; i < rids.size(); i++) { + if (TS->font_has_char(rids[i], p_char)) { + int32_t glyph = TS->font_get_glyph_index(rids[i], p_font_size, p_char, 0); + return Size2(TS->font_get_glyph_advance(rids[i], p_font_size, glyph).x, get_height(p_font_size)); } } - return false; + return Size2(); } -bool FontData::_get(const StringName &p_name, Variant &r_ret) const { - Vector<String> tokens = p_name.operator String().split("/"); - if (tokens.size() == 2 && tokens[0] == "language_support_override") { - String lang = tokens[1]; - r_ret = get_language_support_override(lang); - return true; - } else if (tokens.size() == 2 && tokens[0] == "script_support_override") { - String script = tokens[1]; - r_ret = get_script_support_override(script); - return true; - } else if (tokens.size() >= 3 && tokens[0] == "cache") { - int cache_index = tokens[1].to_int(); - if (tokens.size() == 3 && tokens[2] == "variation_coordinates") { - r_ret = get_variation_coordinates(cache_index); - return true; +real_t Font::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, int p_font_size, const Color &p_modulate) const { + if (dirty_rids) { + _update_rids(); + } + for (int i = 0; i < rids.size(); i++) { + if (TS->font_has_char(rids[i], p_char)) { + int32_t glyph = TS->font_get_glyph_index(rids[i], p_font_size, p_char, 0); + TS->font_draw_glyph(rids[i], p_canvas_item, p_font_size, p_pos, glyph, p_modulate); + return TS->font_get_glyph_advance(rids[i], p_font_size, glyph).x; } - if (tokens.size() >= 5) { - Vector2i sz = Vector2i(tokens[2].to_int(), tokens[3].to_int()); - if (tokens[4] == "ascent") { - r_ret = get_ascent(cache_index, sz.x); - return true; - } else if (tokens[4] == "descent") { - r_ret = get_descent(cache_index, sz.x); - return true; - } else if (tokens[4] == "underline_position") { - r_ret = get_underline_position(cache_index, sz.x); - return true; - } else if (tokens[4] == "underline_thickness") { - r_ret = get_underline_thickness(cache_index, sz.x); - return true; - } else if (tokens[4] == "scale") { - r_ret = get_scale(cache_index, sz.x); - return true; - } else if (tokens[4] == "spacing_glyph") { - r_ret = get_spacing(cache_index, sz.x, TextServer::SPACING_GLYPH); - return true; - } else if (tokens[4] == "spacing_space") { - r_ret = get_spacing(cache_index, sz.x, TextServer::SPACING_SPACE); - return true; - } else if (tokens.size() == 7 && tokens[4] == "textures") { - int texture_index = tokens[5].to_int(); - if (tokens[6] == "image") { - r_ret = get_texture_image(cache_index, sz, texture_index); - return true; - } else if (tokens[6] == "offsets") { - r_ret = get_texture_offsets(cache_index, sz, texture_index); - return true; - } - } else if (tokens.size() == 7 && tokens[4] == "glyphs") { - int32_t glyph_index = tokens[5].to_int(); - if (tokens[6] == "advance") { - r_ret = get_glyph_advance(cache_index, sz.x, glyph_index); - return true; - } else if (tokens[6] == "offset") { - r_ret = get_glyph_offset(cache_index, sz, glyph_index); - return true; - } else if (tokens[6] == "size") { - r_ret = get_glyph_size(cache_index, sz, glyph_index); - return true; - } else if (tokens[6] == "uv_rect") { - r_ret = get_glyph_uv_rect(cache_index, sz, glyph_index); - return true; - } else if (tokens[6] == "texture_idx") { - r_ret = get_glyph_texture_idx(cache_index, sz, glyph_index); - return true; - } - } else if (tokens.size() == 7 && tokens[4] == "kerning_overrides") { - Vector2i gp = Vector2i(tokens[5].to_int(), tokens[6].to_int()); - r_ret = get_kerning(cache_index, sz.x, gp); - return true; - } + } + return 0.f; +} + +real_t Font::draw_char_outline(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, int p_font_size, int p_size, const Color &p_modulate) const { + if (dirty_rids) { + _update_rids(); + } + for (int i = 0; i < rids.size(); i++) { + if (TS->font_has_char(rids[i], p_char)) { + int32_t glyph = TS->font_get_glyph_index(rids[i], p_font_size, p_char, 0); + TS->font_draw_glyph_outline(rids[i], p_canvas_item, p_font_size, p_size, p_pos, glyph, p_modulate); + return TS->font_get_glyph_advance(rids[i], p_font_size, glyph).x; } } - return false; + return 0.f; } -void FontData::_get_property_list(List<PropertyInfo> *p_list) const { - Vector<String> lang_over = get_language_support_overrides(); - for (int i = 0; i < lang_over.size(); i++) { - p_list->push_back(PropertyInfo(Variant::BOOL, "language_support_override/" + lang_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); +// Helper functions. +bool Font::has_char(char32_t p_char) const { + if (dirty_rids) { + _update_rids(); } - Vector<String> scr_over = get_script_support_overrides(); - for (int i = 0; i < scr_over.size(); i++) { - p_list->push_back(PropertyInfo(Variant::BOOL, "script_support_override/" + scr_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + for (int i = 0; i < rids.size(); i++) { + if (TS->font_has_char(rids[i], p_char)) { + return true; + } } - for (int i = 0; i < cache.size(); i++) { - String prefix = "cache/" + itos(i) + "/"; - Array sizes = get_size_cache_list(i); - p_list->push_back(PropertyInfo(Variant::DICTIONARY, prefix + "variation_coordinates", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - for (int j = 0; j < sizes.size(); j++) { - Vector2i sz = sizes[j]; - String prefix_sz = prefix + itos(sz.x) + "/" + itos(sz.y) + "/"; - if (sz.y == 0) { - p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "ascent", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "descent", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "underline_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "underline_thickness", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - p_list->push_back(PropertyInfo(Variant::BOOL, prefix_sz + "spacing_glyph", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - p_list->push_back(PropertyInfo(Variant::BOOL, prefix_sz + "spacing_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - } + return false; +} - int tx_cnt = get_texture_count(i, sz); - for (int k = 0; k < tx_cnt; k++) { - p_list->push_back(PropertyInfo(Variant::PACKED_INT32_ARRAY, prefix_sz + "textures/" + itos(k) + "/offsets", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - p_list->push_back(PropertyInfo(Variant::OBJECT, prefix_sz + "textures/" + itos(k) + "/image", PROPERTY_HINT_RESOURCE_TYPE, "Image", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT)); - } - Array glyphs = get_glyph_list(i, sz); - for (int k = 0; k < glyphs.size(); k++) { - const int32_t &gl = glyphs[k]; - if (sz.y == 0) { - p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "glyphs/" + itos(gl) + "/advance", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - } - p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "glyphs/" + itos(gl) + "/offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "glyphs/" + itos(gl) + "/size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - p_list->push_back(PropertyInfo(Variant::RECT2, prefix_sz + "glyphs/" + itos(gl) + "/uv_rect", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - p_list->push_back(PropertyInfo(Variant::INT, prefix_sz + "glyphs/" + itos(gl) + "/texture_idx", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - } - if (sz.y == 0) { - Array kerning_map = get_kerning_list(i, sz.x); - for (int k = 0; k < kerning_map.size(); k++) { - const Vector2i &gl_pair = kerning_map[k]; - p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "kerning_overrides/" + itos(gl_pair.x) + "/" + itos(gl_pair.y), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); - } +String Font::get_supported_chars() const { + if (dirty_rids) { + _update_rids(); + } + String chars; + for (int i = 0; i < rids.size(); i++) { + String data_chars = TS->font_get_supported_chars(rids[i]); + for (int j = 0; j < data_chars.length(); j++) { + if (chars.find_char(data_chars[j]) == -1) { + chars += data_chars[j]; } } } + return chars; } -void FontData::reset_state() { - _clear_cache(); - data.clear(); - data_ptr = nullptr; - data_size = 0; - face_index = 0; - cache.clear(); +bool Font::is_language_supported(const String &p_language) const { + return TS->font_is_language_supported(_get_rid(), p_language); +} - antialiased = true; - mipmaps = false; - msdf = false; - force_autohinter = false; - hinting = TextServer::HINTING_LIGHT; - subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_DISABLED; - msdf_pixel_range = 14; - msdf_size = 128; - fixed_size = 0; - embolden = 0.f; - transform = Transform2D(); - oversampling = 0.f; +bool Font::is_script_supported(const String &p_script) const { + return TS->font_is_script_supported(_get_rid(), p_script); +} + +Dictionary Font::get_supported_feature_list() const { + return TS->font_supported_feature_list(_get_rid()); +} + +Dictionary Font::get_supported_variation_list() const { + return TS->font_supported_variation_list(_get_rid()); +} + +int64_t Font::get_face_count() const { + return TS->font_get_face_count(_get_rid()); +} + +Font::Font() { + cache.set_capacity(64); + cache_wrap.set_capacity(16); +} + +Font::~Font() { + reset_state(); } -void FontData::_convert_packed_8bit(Ref<Image> &p_source, int p_page, int p_sz) { +/*************************************************************************/ +/* FontFile */ +/*************************************************************************/ + +_FORCE_INLINE_ void FontFile::_clear_cache() { + for (int i = 0; i < cache.size(); i++) { + if (cache[i].is_valid()) { + TS->free_rid(cache[i]); + cache.write[i] = RID(); + } + } +} + +_FORCE_INLINE_ void FontFile::_ensure_rid(int p_cache_index) const { + if (unlikely(p_cache_index >= cache.size())) { + cache.resize(p_cache_index + 1); + } + if (unlikely(!cache[p_cache_index].is_valid())) { + cache.write[p_cache_index] = TS->create_font(); + TS->font_set_data_ptr(cache[p_cache_index], data_ptr, data_size); + TS->font_set_antialiased(cache[p_cache_index], antialiased); + TS->font_set_generate_mipmaps(cache[p_cache_index], mipmaps); + TS->font_set_multichannel_signed_distance_field(cache[p_cache_index], msdf); + TS->font_set_msdf_pixel_range(cache[p_cache_index], msdf_pixel_range); + TS->font_set_msdf_size(cache[p_cache_index], msdf_size); + TS->font_set_fixed_size(cache[p_cache_index], fixed_size); + TS->font_set_force_autohinter(cache[p_cache_index], force_autohinter); + TS->font_set_hinting(cache[p_cache_index], hinting); + TS->font_set_subpixel_positioning(cache[p_cache_index], subpixel_positioning); + TS->font_set_oversampling(cache[p_cache_index], oversampling); + } +} + +void FontFile::_convert_packed_8bit(Ref<Image> &p_source, int p_page, int p_sz) { int w = p_source->get_width(); int h = p_source->get_height(); @@ -516,7 +617,7 @@ void FontData::_convert_packed_8bit(Ref<Image> &p_source, int p_page, int p_sz) set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 3, img_a); } -void FontData::_convert_packed_4bit(Ref<Image> &p_source, int p_page, int p_sz) { +void FontFile::_convert_packed_4bit(Ref<Image> &p_source, int p_page, int p_sz) { int w = p_source->get_width(); int h = p_source->get_height(); @@ -616,7 +717,7 @@ void FontData::_convert_packed_4bit(Ref<Image> &p_source, int p_page, int p_sz) set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 3, img_ao); } -void FontData::_convert_rgba_4bit(Ref<Image> &p_source, int p_page, int p_sz) { +void FontFile::_convert_rgba_4bit(Ref<Image> &p_source, int p_page, int p_sz) { int w = p_source->get_width(); int h = p_source->get_height(); @@ -672,7 +773,7 @@ void FontData::_convert_rgba_4bit(Ref<Image> &p_source, int p_page, int p_sz) { set_texture_image(0, Vector2i(p_sz, 1), p_page, img_o); } -void FontData::_convert_mono_8bit(Ref<Image> &p_source, int p_page, int p_ch, int p_sz, int p_ol) { +void FontFile::_convert_mono_8bit(Ref<Image> &p_source, int p_page, int p_ch, int p_sz, int p_ol) { int w = p_source->get_width(); int h = p_source->get_height(); @@ -701,7 +802,7 @@ void FontData::_convert_mono_8bit(Ref<Image> &p_source, int p_page, int p_ch, in set_texture_image(0, Vector2i(p_sz, p_ol), p_page, img_g); } -void FontData::_convert_mono_4bit(Ref<Image> &p_source, int p_page, int p_ch, int p_sz, int p_ol) { +void FontFile::_convert_mono_4bit(Ref<Image> &p_source, int p_page, int p_ch, int p_sz, int p_ol) { int w = p_source->get_width(); int h = p_source->get_height(); @@ -744,9 +845,477 @@ void FontData::_convert_mono_4bit(Ref<Image> &p_source, int p_page, int p_ch, in set_texture_image(0, Vector2i(p_sz, p_ol), p_page, img_o); } +void FontFile::_bind_methods() { + ClassDB::bind_method(D_METHOD("load_bitmap_font", "path"), &FontFile::load_bitmap_font); + ClassDB::bind_method(D_METHOD("load_dynamic_font", "path"), &FontFile::load_dynamic_font); + + ClassDB::bind_method(D_METHOD("set_data", "data"), &FontFile::set_data); + ClassDB::bind_method(D_METHOD("get_data"), &FontFile::get_data); + + ClassDB::bind_method(D_METHOD("set_font_name", "name"), &FontFile::set_font_name); + ClassDB::bind_method(D_METHOD("set_font_style_name", "name"), &FontFile::set_font_style_name); + ClassDB::bind_method(D_METHOD("set_font_style", "style"), &FontFile::set_font_style); + + ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &FontFile::set_antialiased); + ClassDB::bind_method(D_METHOD("is_antialiased"), &FontFile::is_antialiased); + + ClassDB::bind_method(D_METHOD("set_generate_mipmaps", "generate_mipmaps"), &FontFile::set_generate_mipmaps); + ClassDB::bind_method(D_METHOD("get_generate_mipmaps"), &FontFile::get_generate_mipmaps); + + ClassDB::bind_method(D_METHOD("set_multichannel_signed_distance_field", "msdf"), &FontFile::set_multichannel_signed_distance_field); + ClassDB::bind_method(D_METHOD("is_multichannel_signed_distance_field"), &FontFile::is_multichannel_signed_distance_field); + + ClassDB::bind_method(D_METHOD("set_msdf_pixel_range", "msdf_pixel_range"), &FontFile::set_msdf_pixel_range); + ClassDB::bind_method(D_METHOD("get_msdf_pixel_range"), &FontFile::get_msdf_pixel_range); + + ClassDB::bind_method(D_METHOD("set_msdf_size", "msdf_size"), &FontFile::set_msdf_size); + ClassDB::bind_method(D_METHOD("get_msdf_size"), &FontFile::get_msdf_size); + + ClassDB::bind_method(D_METHOD("set_fixed_size", "fixed_size"), &FontFile::set_fixed_size); + ClassDB::bind_method(D_METHOD("get_fixed_size"), &FontFile::get_fixed_size); + + ClassDB::bind_method(D_METHOD("set_force_autohinter", "force_autohinter"), &FontFile::set_force_autohinter); + ClassDB::bind_method(D_METHOD("is_force_autohinter"), &FontFile::is_force_autohinter); + + ClassDB::bind_method(D_METHOD("set_hinting", "hinting"), &FontFile::set_hinting); + ClassDB::bind_method(D_METHOD("get_hinting"), &FontFile::get_hinting); + + ClassDB::bind_method(D_METHOD("set_subpixel_positioning", "subpixel_positioning"), &FontFile::set_subpixel_positioning); + ClassDB::bind_method(D_METHOD("get_subpixel_positioning"), &FontFile::get_subpixel_positioning); + + ClassDB::bind_method(D_METHOD("set_oversampling", "oversampling"), &FontFile::set_oversampling); + ClassDB::bind_method(D_METHOD("get_oversampling"), &FontFile::get_oversampling); + + ClassDB::bind_method(D_METHOD("get_cache_count"), &FontFile::get_cache_count); + ClassDB::bind_method(D_METHOD("clear_cache"), &FontFile::clear_cache); + ClassDB::bind_method(D_METHOD("remove_cache", "cache_index"), &FontFile::remove_cache); + + ClassDB::bind_method(D_METHOD("get_size_cache_list", "cache_index"), &FontFile::get_size_cache_list); + ClassDB::bind_method(D_METHOD("clear_size_cache", "cache_index"), &FontFile::clear_size_cache); + ClassDB::bind_method(D_METHOD("remove_size_cache", "cache_index", "size"), &FontFile::remove_size_cache); + + ClassDB::bind_method(D_METHOD("set_variation_coordinates", "cache_index", "variation_coordinates"), &FontFile::set_variation_coordinates); + ClassDB::bind_method(D_METHOD("get_variation_coordinates", "cache_index"), &FontFile::get_variation_coordinates); + + ClassDB::bind_method(D_METHOD("set_embolden", "cache_index", "strength"), &FontFile::set_embolden); + ClassDB::bind_method(D_METHOD("get_embolden", "cache_index"), &FontFile::get_embolden); + + ClassDB::bind_method(D_METHOD("set_transform", "cache_index", "transform"), &FontFile::set_transform); + ClassDB::bind_method(D_METHOD("get_transform", "cache_index"), &FontFile::get_transform); + + ClassDB::bind_method(D_METHOD("set_face_index", "cache_index", "face_index"), &FontFile::set_face_index); + ClassDB::bind_method(D_METHOD("get_face_index", "cache_index"), &FontFile::get_face_index); + + ClassDB::bind_method(D_METHOD("set_cache_ascent", "cache_index", "size", "ascent"), &FontFile::set_cache_ascent); + ClassDB::bind_method(D_METHOD("get_cache_ascent", "cache_index", "size"), &FontFile::get_cache_ascent); + + ClassDB::bind_method(D_METHOD("set_cache_descent", "cache_index", "size", "descent"), &FontFile::set_cache_descent); + ClassDB::bind_method(D_METHOD("get_cache_descent", "cache_index", "size"), &FontFile::get_cache_descent); + + ClassDB::bind_method(D_METHOD("set_cache_underline_position", "cache_index", "size", "underline_position"), &FontFile::set_cache_underline_position); + ClassDB::bind_method(D_METHOD("get_cache_underline_position", "cache_index", "size"), &FontFile::get_cache_underline_position); + + ClassDB::bind_method(D_METHOD("set_cache_underline_thickness", "cache_index", "size", "underline_thickness"), &FontFile::set_cache_underline_thickness); + ClassDB::bind_method(D_METHOD("get_cache_underline_thickness", "cache_index", "size"), &FontFile::get_cache_underline_thickness); + + ClassDB::bind_method(D_METHOD("set_cache_scale", "cache_index", "size", "scale"), &FontFile::set_cache_scale); + ClassDB::bind_method(D_METHOD("get_cache_scale", "cache_index", "size"), &FontFile::get_cache_scale); + + ClassDB::bind_method(D_METHOD("get_texture_count", "cache_index", "size"), &FontFile::get_texture_count); + ClassDB::bind_method(D_METHOD("clear_textures", "cache_index", "size"), &FontFile::clear_textures); + ClassDB::bind_method(D_METHOD("remove_texture", "cache_index", "size", "texture_index"), &FontFile::remove_texture); + + ClassDB::bind_method(D_METHOD("set_texture_image", "cache_index", "size", "texture_index", "image"), &FontFile::set_texture_image); + ClassDB::bind_method(D_METHOD("get_texture_image", "cache_index", "size", "texture_index"), &FontFile::get_texture_image); + + ClassDB::bind_method(D_METHOD("set_texture_offsets", "cache_index", "size", "texture_index", "offset"), &FontFile::set_texture_offsets); + ClassDB::bind_method(D_METHOD("get_texture_offsets", "cache_index", "size", "texture_index"), &FontFile::get_texture_offsets); + + ClassDB::bind_method(D_METHOD("get_glyph_list", "cache_index", "size"), &FontFile::get_glyph_list); + ClassDB::bind_method(D_METHOD("clear_glyphs", "cache_index", "size"), &FontFile::clear_glyphs); + ClassDB::bind_method(D_METHOD("remove_glyph", "cache_index", "size", "glyph"), &FontFile::remove_glyph); + + ClassDB::bind_method(D_METHOD("set_glyph_advance", "cache_index", "size", "glyph", "advance"), &FontFile::set_glyph_advance); + ClassDB::bind_method(D_METHOD("get_glyph_advance", "cache_index", "size", "glyph"), &FontFile::get_glyph_advance); + + ClassDB::bind_method(D_METHOD("set_glyph_offset", "cache_index", "size", "glyph", "offset"), &FontFile::set_glyph_offset); + ClassDB::bind_method(D_METHOD("get_glyph_offset", "cache_index", "size", "glyph"), &FontFile::get_glyph_offset); + + ClassDB::bind_method(D_METHOD("set_glyph_size", "cache_index", "size", "glyph", "gl_size"), &FontFile::set_glyph_size); + ClassDB::bind_method(D_METHOD("get_glyph_size", "cache_index", "size", "glyph"), &FontFile::get_glyph_size); + + ClassDB::bind_method(D_METHOD("set_glyph_uv_rect", "cache_index", "size", "glyph", "uv_rect"), &FontFile::set_glyph_uv_rect); + ClassDB::bind_method(D_METHOD("get_glyph_uv_rect", "cache_index", "size", "glyph"), &FontFile::get_glyph_uv_rect); + + ClassDB::bind_method(D_METHOD("set_glyph_texture_idx", "cache_index", "size", "glyph", "texture_idx"), &FontFile::set_glyph_texture_idx); + ClassDB::bind_method(D_METHOD("get_glyph_texture_idx", "cache_index", "size", "glyph"), &FontFile::get_glyph_texture_idx); + + ClassDB::bind_method(D_METHOD("get_kerning_list", "cache_index", "size"), &FontFile::get_kerning_list); + ClassDB::bind_method(D_METHOD("clear_kerning_map", "cache_index", "size"), &FontFile::clear_kerning_map); + ClassDB::bind_method(D_METHOD("remove_kerning", "cache_index", "size", "glyph_pair"), &FontFile::remove_kerning); + + ClassDB::bind_method(D_METHOD("set_kerning", "cache_index", "size", "glyph_pair", "kerning"), &FontFile::set_kerning); + ClassDB::bind_method(D_METHOD("get_kerning", "cache_index", "size", "glyph_pair"), &FontFile::get_kerning); + + ClassDB::bind_method(D_METHOD("render_range", "cache_index", "size", "start", "end"), &FontFile::render_range); + ClassDB::bind_method(D_METHOD("render_glyph", "cache_index", "size", "index"), &FontFile::render_glyph); + + ClassDB::bind_method(D_METHOD("set_language_support_override", "language", "supported"), &FontFile::set_language_support_override); + ClassDB::bind_method(D_METHOD("get_language_support_override", "language"), &FontFile::get_language_support_override); + ClassDB::bind_method(D_METHOD("remove_language_support_override", "language"), &FontFile::remove_language_support_override); + ClassDB::bind_method(D_METHOD("get_language_support_overrides"), &FontFile::get_language_support_overrides); + + ClassDB::bind_method(D_METHOD("set_script_support_override", "script", "supported"), &FontFile::set_script_support_override); + ClassDB::bind_method(D_METHOD("get_script_support_override", "script"), &FontFile::get_script_support_override); + ClassDB::bind_method(D_METHOD("remove_script_support_override", "script"), &FontFile::remove_script_support_override); + ClassDB::bind_method(D_METHOD("get_script_support_overrides"), &FontFile::get_script_support_overrides); + + ClassDB::bind_method(D_METHOD("set_opentype_feature_overrides", "overrides"), &FontFile::set_opentype_feature_overrides); + ClassDB::bind_method(D_METHOD("get_opentype_feature_overrides"), &FontFile::get_opentype_feature_overrides); + + ClassDB::bind_method(D_METHOD("get_glyph_index", "size", "char", "variation_selector"), &FontFile::get_glyph_index); + + ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_data", "get_data"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "generate_mipmaps", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_generate_mipmaps", "get_generate_mipmaps"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "antialiased", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_antialiased", "is_antialiased"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "font_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_name", "get_font_name"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "style_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_style_name", "get_font_style_name"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "font_style", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_style", "get_font_style"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One half of a pixel,One quarter of a pixel", PROPERTY_USAGE_STORAGE), "set_subpixel_positioning", "get_subpixel_positioning"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_multichannel_signed_distance_field", "is_multichannel_signed_distance_field"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_pixel_range", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_msdf_pixel_range", "get_msdf_pixel_range"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_msdf_size", "get_msdf_size"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "force_autohinter", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_force_autohinter", "is_force_autohinter"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_STORAGE), "set_hinting", "get_hinting"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_oversampling", "get_oversampling"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_fixed_size", "get_fixed_size"); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "opentype_feature_overrides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_opentype_feature_overrides", "get_opentype_feature_overrides"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "Font"), PROPERTY_USAGE_STORAGE), "set_fallbacks", "get_fallbacks"); +} + +bool FontFile::_set(const StringName &p_name, const Variant &p_value) { + Vector<String> tokens = p_name.operator String().split("/"); + +#ifndef DISABLE_DEPRECATED + if (tokens.size() == 1 && tokens[0] == "font_path") { + // Compatibility, DynamicFontData. + load_dynamic_font(p_value); + } else if (tokens.size() == 1 && tokens[0] == "override_oversampling") { + set_oversampling(p_value); + } + if (tokens.size() == 1 && tokens[0] == "font_data") { + // Compatibility, DynamicFont. + Ref<Font> f = p_value; + if (f.is_valid()) { + fallbacks.push_back(f); + return true; + } + return false; + } else if (tokens.size() == 2 && tokens[0] == "fallback") { + // Compatibility, DynamicFont. + Ref<FontFile> f = p_value; + if (f.is_valid()) { + fallbacks.push_back(f); + return true; + } + return false; + } else if (tokens.size() == 1 && tokens[0] == "textures") { + // Compatibility, BitmapFont. + set_fixed_size(16); + Array textures = p_value; + for (int i = 0; i < textures.size(); i++) { + Ref<ImageTexture> tex = textures[i]; + ERR_CONTINUE(!tex.is_valid()); + set_texture_image(0, Vector2i(16, 0), i, tex->get_image()); + } + } else if (tokens.size() == 1 && tokens[0] == "chars") { + // Compatibility, BitmapFont. + set_fixed_size(16); + PackedInt32Array arr = p_value; + int len = arr.size(); + ERR_FAIL_COND_V(len % 9, false); + if (!len) { + return false; + } + int chars = len / 9; + for (int i = 0; i < chars; i++) { + const int32_t *data = &arr[i * 9]; + char32_t c = data[0]; + set_glyph_texture_idx(0, Vector2i(16, 0), c, data[1]); + set_glyph_uv_rect(0, Vector2i(16, 0), c, Rect2(data[2], data[3], data[4], data[5])); + set_glyph_offset(0, Vector2i(16, 0), c, Size2(data[6], data[7])); + set_glyph_advance(0, 16, c, Vector2(data[8], 0)); + } + } else if (tokens.size() == 1 && tokens[0] == "kernings") { + // Compatibility, BitmapFont. + set_fixed_size(16); + PackedInt32Array arr = p_value; + int len = arr.size(); + ERR_FAIL_COND_V(len % 3, false); + if (!len) { + return false; + } + for (int i = 0; i < len / 3; i++) { + const int32_t *data = &arr[i * 3]; + set_kerning(0, 16, Vector2i(data[0], data[1]), Vector2(data[2], 0)); + } + } else if (tokens.size() == 1 && tokens[0] == "height") { + // Compatibility, BitmapFont. + bmp_height = p_value; + set_fixed_size(16); + set_cache_descent(0, 16, bmp_height - bmp_ascent); + } else if (tokens.size() == 1 && tokens[0] == "ascent") { + // Compatibility, BitmapFont. + bmp_ascent = p_value; + set_fixed_size(16); + set_cache_ascent(0, 16, bmp_ascent); + set_cache_descent(0, 16, bmp_height - bmp_ascent); + } else if (tokens.size() == 1 && tokens[0] == "fallback") { + // Compatibility, BitmapFont. + Ref<Font> f = p_value; + if (f.is_valid()) { + fallbacks.push_back(f); + return true; + } + return false; + } +#endif // DISABLE_DEPRECATED + + if (tokens.size() == 2 && tokens[0] == "language_support_override") { + String lang = tokens[1]; + set_language_support_override(lang, p_value); + return true; + } else if (tokens.size() == 2 && tokens[0] == "script_support_override") { + String script = tokens[1]; + set_script_support_override(script, p_value); + return true; + } else if (tokens.size() >= 3 && tokens[0] == "cache") { + int cache_index = tokens[1].to_int(); + if (tokens.size() == 3 && tokens[2] == "variation_coordinates") { + set_variation_coordinates(cache_index, p_value); + return true; + } else if (tokens.size() == 3 && tokens[2] == "embolden") { + set_embolden(cache_index, p_value); + return true; + } else if (tokens.size() == 3 && tokens[2] == "face_index") { + set_face_index(cache_index, p_value); + return true; + } else if (tokens.size() == 3 && tokens[2] == "transform") { + set_transform(cache_index, p_value); + return true; + } + if (tokens.size() >= 5) { + Vector2i sz = Vector2i(tokens[2].to_int(), tokens[3].to_int()); + if (tokens[4] == "ascent") { + set_cache_ascent(cache_index, sz.x, p_value); + return true; + } else if (tokens[4] == "descent") { + set_cache_descent(cache_index, sz.x, p_value); + return true; + } else if (tokens[4] == "underline_position") { + set_cache_underline_position(cache_index, sz.x, p_value); + return true; + } else if (tokens[4] == "underline_thickness") { + set_cache_underline_thickness(cache_index, sz.x, p_value); + return true; + } else if (tokens[4] == "scale") { + set_cache_scale(cache_index, sz.x, p_value); + return true; + } else if (tokens.size() == 7 && tokens[4] == "textures") { + int texture_index = tokens[5].to_int(); + if (tokens[6] == "image") { + set_texture_image(cache_index, sz, texture_index, p_value); + return true; + } else if (tokens[6] == "offsets") { + set_texture_offsets(cache_index, sz, texture_index, p_value); + return true; + } + } else if (tokens.size() == 7 && tokens[4] == "glyphs") { + int32_t glyph_index = tokens[5].to_int(); + if (tokens[6] == "advance") { + set_glyph_advance(cache_index, sz.x, glyph_index, p_value); + return true; + } else if (tokens[6] == "offset") { + set_glyph_offset(cache_index, sz, glyph_index, p_value); + return true; + } else if (tokens[6] == "size") { + set_glyph_size(cache_index, sz, glyph_index, p_value); + return true; + } else if (tokens[6] == "uv_rect") { + set_glyph_uv_rect(cache_index, sz, glyph_index, p_value); + return true; + } else if (tokens[6] == "texture_idx") { + set_glyph_texture_idx(cache_index, sz, glyph_index, p_value); + return true; + } + } else if (tokens.size() == 7 && tokens[4] == "kerning_overrides") { + Vector2i gp = Vector2i(tokens[5].to_int(), tokens[6].to_int()); + set_kerning(cache_index, sz.x, gp, p_value); + return true; + } + } + } + return false; +} + +bool FontFile::_get(const StringName &p_name, Variant &r_ret) const { + Vector<String> tokens = p_name.operator String().split("/"); + if (tokens.size() == 2 && tokens[0] == "language_support_override") { + String lang = tokens[1]; + r_ret = get_language_support_override(lang); + return true; + } else if (tokens.size() == 2 && tokens[0] == "script_support_override") { + String script = tokens[1]; + r_ret = get_script_support_override(script); + return true; + } else if (tokens.size() >= 3 && tokens[0] == "cache") { + int cache_index = tokens[1].to_int(); + if (tokens.size() == 3 && tokens[2] == "variation_coordinates") { + r_ret = get_variation_coordinates(cache_index); + return true; + } else if (tokens.size() == 3 && tokens[2] == "embolden") { + r_ret = get_embolden(cache_index); + return true; + } else if (tokens.size() == 3 && tokens[2] == "face_index") { + r_ret = get_face_index(cache_index); + return true; + } else if (tokens.size() == 3 && tokens[2] == "transform") { + r_ret = get_transform(cache_index); + return true; + } + if (tokens.size() >= 5) { + Vector2i sz = Vector2i(tokens[2].to_int(), tokens[3].to_int()); + if (tokens[4] == "ascent") { + r_ret = get_cache_ascent(cache_index, sz.x); + return true; + } else if (tokens[4] == "descent") { + r_ret = get_cache_descent(cache_index, sz.x); + return true; + } else if (tokens[4] == "underline_position") { + r_ret = get_cache_underline_position(cache_index, sz.x); + return true; + } else if (tokens[4] == "underline_thickness") { + r_ret = get_cache_underline_thickness(cache_index, sz.x); + return true; + } else if (tokens[4] == "scale") { + r_ret = get_cache_scale(cache_index, sz.x); + return true; + } else if (tokens.size() == 7 && tokens[4] == "textures") { + int texture_index = tokens[5].to_int(); + if (tokens[6] == "image") { + r_ret = get_texture_image(cache_index, sz, texture_index); + return true; + } else if (tokens[6] == "offsets") { + r_ret = get_texture_offsets(cache_index, sz, texture_index); + return true; + } + } else if (tokens.size() == 7 && tokens[4] == "glyphs") { + int32_t glyph_index = tokens[5].to_int(); + if (tokens[6] == "advance") { + r_ret = get_glyph_advance(cache_index, sz.x, glyph_index); + return true; + } else if (tokens[6] == "offset") { + r_ret = get_glyph_offset(cache_index, sz, glyph_index); + return true; + } else if (tokens[6] == "size") { + r_ret = get_glyph_size(cache_index, sz, glyph_index); + return true; + } else if (tokens[6] == "uv_rect") { + r_ret = get_glyph_uv_rect(cache_index, sz, glyph_index); + return true; + } else if (tokens[6] == "texture_idx") { + r_ret = get_glyph_texture_idx(cache_index, sz, glyph_index); + return true; + } + } else if (tokens.size() == 7 && tokens[4] == "kerning_overrides") { + Vector2i gp = Vector2i(tokens[5].to_int(), tokens[6].to_int()); + r_ret = get_kerning(cache_index, sz.x, gp); + return true; + } + } + } + return false; +} + +void FontFile::_get_property_list(List<PropertyInfo> *p_list) const { + Vector<String> lang_over = get_language_support_overrides(); + for (int i = 0; i < lang_over.size(); i++) { + p_list->push_back(PropertyInfo(Variant::BOOL, "language_support_override/" + lang_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + } + Vector<String> scr_over = get_script_support_overrides(); + for (int i = 0; i < scr_over.size(); i++) { + p_list->push_back(PropertyInfo(Variant::BOOL, "script_support_override/" + scr_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + } + for (int i = 0; i < cache.size(); i++) { + String prefix = "cache/" + itos(i) + "/"; + Array sizes = get_size_cache_list(i); + p_list->push_back(PropertyInfo(Variant::DICTIONARY, prefix + "variation_coordinates", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::INT, "face_index", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::FLOAT, "embolden", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::TRANSFORM2D, "transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + + for (int j = 0; j < sizes.size(); j++) { + Vector2i sz = sizes[j]; + String prefix_sz = prefix + itos(sz.x) + "/" + itos(sz.y) + "/"; + if (sz.y == 0) { + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "ascent", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "descent", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "underline_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "underline_thickness", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::FLOAT, prefix_sz + "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + } + + int tx_cnt = get_texture_count(i, sz); + for (int k = 0; k < tx_cnt; k++) { + p_list->push_back(PropertyInfo(Variant::PACKED_INT32_ARRAY, prefix_sz + "textures/" + itos(k) + "/offsets", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::OBJECT, prefix_sz + "textures/" + itos(k) + "/image", PROPERTY_HINT_RESOURCE_TYPE, "Image", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT)); + } + Array glyphs = get_glyph_list(i, sz); + for (int k = 0; k < glyphs.size(); k++) { + const int32_t &gl = glyphs[k]; + if (sz.y == 0) { + p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "glyphs/" + itos(gl) + "/advance", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + } + p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "glyphs/" + itos(gl) + "/offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "glyphs/" + itos(gl) + "/size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::RECT2, prefix_sz + "glyphs/" + itos(gl) + "/uv_rect", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + p_list->push_back(PropertyInfo(Variant::INT, prefix_sz + "glyphs/" + itos(gl) + "/texture_idx", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + } + if (sz.y == 0) { + Array kerning_map = get_kerning_list(i, sz.x); + for (int k = 0; k < kerning_map.size(); k++) { + const Vector2i &gl_pair = kerning_map[k]; + p_list->push_back(PropertyInfo(Variant::VECTOR2, prefix_sz + "kerning_overrides/" + itos(gl_pair.x) + "/" + itos(gl_pair.y), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE)); + } + } + } + } +} + +void FontFile::reset_state() { + _clear_cache(); + data.clear(); + data_ptr = nullptr; + data_size = 0; + cache.clear(); + + antialiased = true; + mipmaps = false; + msdf = false; + force_autohinter = false; + hinting = TextServer::HINTING_LIGHT; + subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_DISABLED; + msdf_pixel_range = 14; + msdf_size = 128; + fixed_size = 0; + oversampling = 0.f; + + Font::reset_state(); +} + /*************************************************************************/ -Error FontData::load_bitmap_font(const String &p_path) { +Error FontFile::load_bitmap_font(const String &p_path) { reset_state(); antialiased = false; @@ -1209,13 +1778,13 @@ Error FontData::load_bitmap_font(const String &p_path) { set_font_name(font_name); set_font_style(st_flags); - set_ascent(0, base_size, ascent); - set_descent(0, base_size, height - ascent); + set_cache_ascent(0, base_size, ascent); + set_cache_descent(0, base_size, height - ascent); return OK; } -Error FontData::load_dynamic_font(const String &p_path) { +Error FontFile::load_dynamic_font(const String &p_path) { reset_state(); Vector<uint8_t> data = FileAccess::get_file_as_array(p_path); @@ -1224,7 +1793,7 @@ Error FontData::load_dynamic_font(const String &p_path) { return OK; } -void FontData::set_data_ptr(const uint8_t *p_data, size_t p_size) { +void FontFile::set_data_ptr(const uint8_t *p_data, size_t p_size) { data.clear(); data_ptr = p_data; data_size = p_size; @@ -1238,7 +1807,7 @@ void FontData::set_data_ptr(const uint8_t *p_data, size_t p_size) { } } -void FontData::set_data(const PackedByteArray &p_data) { +void FontFile::set_data(const PackedByteArray &p_data) { data = p_data; data_ptr = data.ptr(); data_size = data.size(); @@ -1252,32 +1821,7 @@ void FontData::set_data(const PackedByteArray &p_data) { } } -void FontData::set_face_index(int64_t p_index) { - ERR_FAIL_COND(p_index < 0); - ERR_FAIL_COND(p_index >= 0x7FFF); - - if (face_index != p_index) { - face_index = p_index; - if (data_ptr != nullptr) { - for (int i = 0; i < cache.size(); i++) { - if (cache[i].is_valid()) { - TS->font_set_face_index(cache[i], face_index); - } - } - } - } -} - -int64_t FontData::get_face_index() const { - return face_index; -} - -int64_t FontData::get_face_count() const { - _ensure_rid(0); - return TS->font_get_face_count(cache[0]); -} - -PackedByteArray FontData::get_data() const { +PackedByteArray FontFile::get_data() const { if (unlikely((size_t)data.size() != data_size)) { PackedByteArray *data_w = const_cast<PackedByteArray *>(&data); data_w->resize(data_size); @@ -1286,37 +1830,22 @@ PackedByteArray FontData::get_data() const { return data; } -void FontData::set_font_name(const String &p_name) { +void FontFile::set_font_name(const String &p_name) { _ensure_rid(0); TS->font_set_name(cache[0], p_name); } -String FontData::get_font_name() const { - _ensure_rid(0); - return TS->font_get_name(cache[0]); -} - -void FontData::set_font_style_name(const String &p_name) { +void FontFile::set_font_style_name(const String &p_name) { _ensure_rid(0); TS->font_set_style_name(cache[0], p_name); } -String FontData::get_font_style_name() const { - _ensure_rid(0); - return TS->font_get_style_name(cache[0]); -} - -void FontData::set_font_style(uint32_t p_style) { +void FontFile::set_font_style(uint32_t p_style) { _ensure_rid(0); TS->font_set_style(cache[0], p_style); } -uint32_t FontData::get_font_style() const { - _ensure_rid(0); - return TS->font_get_style(cache[0]); -} - -void FontData::set_antialiased(bool p_antialiased) { +void FontFile::set_antialiased(bool p_antialiased) { if (antialiased != p_antialiased) { antialiased = p_antialiased; for (int i = 0; i < cache.size(); i++) { @@ -1327,11 +1856,11 @@ void FontData::set_antialiased(bool p_antialiased) { } } -bool FontData::is_antialiased() const { +bool FontFile::is_antialiased() const { return antialiased; } -void FontData::set_generate_mipmaps(bool p_generate_mipmaps) { +void FontFile::set_generate_mipmaps(bool p_generate_mipmaps) { if (mipmaps != p_generate_mipmaps) { mipmaps = p_generate_mipmaps; for (int i = 0; i < cache.size(); i++) { @@ -1342,11 +1871,11 @@ void FontData::set_generate_mipmaps(bool p_generate_mipmaps) { } } -bool FontData::get_generate_mipmaps() const { +bool FontFile::get_generate_mipmaps() const { return mipmaps; } -void FontData::set_multichannel_signed_distance_field(bool p_msdf) { +void FontFile::set_multichannel_signed_distance_field(bool p_msdf) { if (msdf != p_msdf) { msdf = p_msdf; for (int i = 0; i < cache.size(); i++) { @@ -1357,11 +1886,11 @@ void FontData::set_multichannel_signed_distance_field(bool p_msdf) { } } -bool FontData::is_multichannel_signed_distance_field() const { +bool FontFile::is_multichannel_signed_distance_field() const { return msdf; } -void FontData::set_msdf_pixel_range(int p_msdf_pixel_range) { +void FontFile::set_msdf_pixel_range(int p_msdf_pixel_range) { if (msdf_pixel_range != p_msdf_pixel_range) { msdf_pixel_range = p_msdf_pixel_range; for (int i = 0; i < cache.size(); i++) { @@ -1372,11 +1901,11 @@ void FontData::set_msdf_pixel_range(int p_msdf_pixel_range) { } } -int FontData::get_msdf_pixel_range() const { +int FontFile::get_msdf_pixel_range() const { return msdf_pixel_range; } -void FontData::set_msdf_size(int p_msdf_size) { +void FontFile::set_msdf_size(int p_msdf_size) { if (msdf_size != p_msdf_size) { msdf_size = p_msdf_size; for (int i = 0; i < cache.size(); i++) { @@ -1387,11 +1916,11 @@ void FontData::set_msdf_size(int p_msdf_size) { } } -int FontData::get_msdf_size() const { +int FontFile::get_msdf_size() const { return msdf_size; } -void FontData::set_fixed_size(int p_fixed_size) { +void FontFile::set_fixed_size(int p_fixed_size) { if (fixed_size != p_fixed_size) { fixed_size = p_fixed_size; for (int i = 0; i < cache.size(); i++) { @@ -1402,11 +1931,11 @@ void FontData::set_fixed_size(int p_fixed_size) { } } -int FontData::get_fixed_size() const { +int FontFile::get_fixed_size() const { return fixed_size; } -void FontData::set_force_autohinter(bool p_force_autohinter) { +void FontFile::set_force_autohinter(bool p_force_autohinter) { if (force_autohinter != p_force_autohinter) { force_autohinter = p_force_autohinter; for (int i = 0; i < cache.size(); i++) { @@ -1417,11 +1946,11 @@ void FontData::set_force_autohinter(bool p_force_autohinter) { } } -bool FontData::is_force_autohinter() const { +bool FontFile::is_force_autohinter() const { return force_autohinter; } -void FontData::set_hinting(TextServer::Hinting p_hinting) { +void FontFile::set_hinting(TextServer::Hinting p_hinting) { if (hinting != p_hinting) { hinting = p_hinting; for (int i = 0; i < cache.size(); i++) { @@ -1432,11 +1961,11 @@ void FontData::set_hinting(TextServer::Hinting p_hinting) { } } -TextServer::Hinting FontData::get_hinting() const { +TextServer::Hinting FontFile::get_hinting() const { return hinting; } -void FontData::set_subpixel_positioning(TextServer::SubpixelPositioning p_subpixel) { +void FontFile::set_subpixel_positioning(TextServer::SubpixelPositioning p_subpixel) { if (subpixel_positioning != p_subpixel) { subpixel_positioning = p_subpixel; for (int i = 0; i < cache.size(); i++) { @@ -1447,41 +1976,11 @@ void FontData::set_subpixel_positioning(TextServer::SubpixelPositioning p_subpix } } -TextServer::SubpixelPositioning FontData::get_subpixel_positioning() const { +TextServer::SubpixelPositioning FontFile::get_subpixel_positioning() const { return subpixel_positioning; } -void FontData::set_embolden(float p_strength) { - if (embolden != p_strength) { - embolden = p_strength; - for (int i = 0; i < cache.size(); i++) { - _ensure_rid(i); - TS->font_set_embolden(cache[i], embolden); - } - emit_changed(); - } -} - -float FontData::get_embolden() const { - return embolden; -} - -void FontData::set_transform(Transform2D p_transform) { - if (transform != p_transform) { - transform = p_transform; - for (int i = 0; i < cache.size(); i++) { - _ensure_rid(i); - TS->font_set_transform(cache[i], transform); - } - emit_changed(); - } -} - -Transform2D FontData::get_transform() const { - return transform; -} - -void FontData::set_oversampling(real_t p_oversampling) { +void FontFile::set_oversampling(real_t p_oversampling) { if (oversampling != p_oversampling) { oversampling = p_oversampling; for (int i = 0; i < cache.size(); i++) { @@ -1492,17 +1991,20 @@ void FontData::set_oversampling(real_t p_oversampling) { } } -real_t FontData::get_oversampling() const { +real_t FontFile::get_oversampling() const { return oversampling; } -RID FontData::find_cache(const Dictionary &p_variation_coordinates) const { +RID FontFile::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform) const { // Find existing variation cache. const Dictionary &supported_coords = get_supported_variation_list(); for (int i = 0; i < cache.size(); i++) { if (cache[i].is_valid()) { const Dictionary &cache_var = TS->font_get_variation_coordinates(cache[i]); bool match = true; + match = match && (TS->font_get_face_index(cache[i]) == p_face_index); + match = match && (TS->font_get_embolden(cache[i]) == p_strength); + match = match && (TS->font_get_transform(cache[i]) == p_transform); for (const Variant *V = supported_coords.next(nullptr); V && match; V = supported_coords.next(V)) { const Vector3 &def = supported_coords[*V]; @@ -1538,19 +2040,28 @@ RID FontData::find_cache(const Dictionary &p_variation_coordinates) const { int idx = cache.size(); _ensure_rid(idx); TS->font_set_variation_coordinates(cache[idx], p_variation_coordinates); + TS->font_set_face_index(cache[idx], p_face_index); + TS->font_set_embolden(cache[idx], p_strength); + TS->font_set_transform(cache[idx], p_transform); return cache[idx]; } -int FontData::get_cache_count() const { +RID FontFile::_get_rid() const { + _ensure_rid(0); + return cache[0]; +} + +int FontFile::get_cache_count() const { return cache.size(); } -void FontData::clear_cache() { +void FontFile::clear_cache() { _clear_cache(); cache.clear(); + emit_changed(); } -void FontData::remove_cache(int p_cache_index) { +void FontFile::remove_cache(int p_cache_index) { ERR_FAIL_INDEX(p_cache_index, cache.size()); if (cache[p_cache_index].is_valid()) { TS->free_rid(cache.write[p_cache_index]); @@ -1559,952 +2070,607 @@ void FontData::remove_cache(int p_cache_index) { emit_changed(); } -Array FontData::get_size_cache_list(int p_cache_index) const { +Array FontFile::get_size_cache_list(int p_cache_index) const { ERR_FAIL_COND_V(p_cache_index < 0, Array()); _ensure_rid(p_cache_index); return TS->font_get_size_cache_list(cache[p_cache_index]); } -void FontData::clear_size_cache(int p_cache_index) { +void FontFile::clear_size_cache(int p_cache_index) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_clear_size_cache(cache[p_cache_index]); } -void FontData::remove_size_cache(int p_cache_index, const Vector2i &p_size) { +void FontFile::remove_size_cache(int p_cache_index, const Vector2i &p_size) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_remove_size_cache(cache[p_cache_index], p_size); } -void FontData::set_variation_coordinates(int p_cache_index, const Dictionary &p_variation_coordinates) { +void FontFile::set_variation_coordinates(int p_cache_index, const Dictionary &p_variation_coordinates) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_variation_coordinates(cache[p_cache_index], p_variation_coordinates); - emit_changed(); } -Dictionary FontData::get_variation_coordinates(int p_cache_index) const { +Dictionary FontFile::get_variation_coordinates(int p_cache_index) const { ERR_FAIL_COND_V(p_cache_index < 0, Dictionary()); _ensure_rid(p_cache_index); return TS->font_get_variation_coordinates(cache[p_cache_index]); } -void FontData::set_ascent(int p_cache_index, int p_size, real_t p_ascent) { +void FontFile::set_embolden(int p_cache_index, float p_strength) { + ERR_FAIL_COND(p_cache_index < 0); + _ensure_rid(p_cache_index); + TS->font_set_embolden(cache[p_cache_index], p_strength); +} + +float FontFile::get_embolden(int p_cache_index) const { + ERR_FAIL_COND_V(p_cache_index < 0, 0.f); + _ensure_rid(p_cache_index); + return TS->font_get_embolden(cache[p_cache_index]); +} + +void FontFile::set_transform(int p_cache_index, Transform2D p_transform) { + ERR_FAIL_COND(p_cache_index < 0); + _ensure_rid(p_cache_index); + TS->font_set_transform(cache[p_cache_index], p_transform); +} + +Transform2D FontFile::get_transform(int p_cache_index) const { + ERR_FAIL_COND_V(p_cache_index < 0, Transform2D()); + _ensure_rid(p_cache_index); + return TS->font_get_transform(cache[p_cache_index]); +} + +void FontFile::set_face_index(int p_cache_index, int64_t p_index) { + ERR_FAIL_COND(p_cache_index < 0); + ERR_FAIL_COND(p_index < 0); + ERR_FAIL_COND(p_index >= 0x7FFF); + + _ensure_rid(p_cache_index); + TS->font_set_face_index(cache[p_cache_index], p_index); +} + +int64_t FontFile::get_face_index(int p_cache_index) const { + ERR_FAIL_COND_V(p_cache_index < 0, 0); + _ensure_rid(p_cache_index); + return TS->font_get_face_index(cache[p_cache_index]); +} + +void FontFile::set_cache_ascent(int p_cache_index, int p_size, real_t p_ascent) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_ascent(cache[p_cache_index], p_size, p_ascent); } -real_t FontData::get_ascent(int p_cache_index, int p_size) const { +real_t FontFile::get_cache_ascent(int p_cache_index, int p_size) const { ERR_FAIL_COND_V(p_cache_index < 0, 0.f); _ensure_rid(p_cache_index); return TS->font_get_ascent(cache[p_cache_index], p_size); } -void FontData::set_descent(int p_cache_index, int p_size, real_t p_descent) { +void FontFile::set_cache_descent(int p_cache_index, int p_size, real_t p_descent) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_descent(cache[p_cache_index], p_size, p_descent); } -real_t FontData::get_descent(int p_cache_index, int p_size) const { +real_t FontFile::get_cache_descent(int p_cache_index, int p_size) const { ERR_FAIL_COND_V(p_cache_index < 0, 0.f); _ensure_rid(p_cache_index); return TS->font_get_descent(cache[p_cache_index], p_size); } -void FontData::set_underline_position(int p_cache_index, int p_size, real_t p_underline_position) { +void FontFile::set_cache_underline_position(int p_cache_index, int p_size, real_t p_underline_position) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_underline_position(cache[p_cache_index], p_size, p_underline_position); } -real_t FontData::get_underline_position(int p_cache_index, int p_size) const { +real_t FontFile::get_cache_underline_position(int p_cache_index, int p_size) const { ERR_FAIL_COND_V(p_cache_index < 0, 0.f); _ensure_rid(p_cache_index); return TS->font_get_underline_position(cache[p_cache_index], p_size); } -void FontData::set_underline_thickness(int p_cache_index, int p_size, real_t p_underline_thickness) { +void FontFile::set_cache_underline_thickness(int p_cache_index, int p_size, real_t p_underline_thickness) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_underline_thickness(cache[p_cache_index], p_size, p_underline_thickness); } -real_t FontData::get_underline_thickness(int p_cache_index, int p_size) const { +real_t FontFile::get_cache_underline_thickness(int p_cache_index, int p_size) const { ERR_FAIL_COND_V(p_cache_index < 0, 0.f); _ensure_rid(p_cache_index); return TS->font_get_underline_thickness(cache[p_cache_index], p_size); } -void FontData::set_scale(int p_cache_index, int p_size, real_t p_scale) { +void FontFile::set_cache_scale(int p_cache_index, int p_size, real_t p_scale) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_scale(cache[p_cache_index], p_size, p_scale); } -real_t FontData::get_scale(int p_cache_index, int p_size) const { +real_t FontFile::get_cache_scale(int p_cache_index, int p_size) const { ERR_FAIL_COND_V(p_cache_index < 0, 0.f); _ensure_rid(p_cache_index); return TS->font_get_scale(cache[p_cache_index], p_size); } -void FontData::set_spacing(int p_cache_index, int p_size, TextServer::SpacingType p_spacing, int p_value) { - ERR_FAIL_COND(p_cache_index < 0); - _ensure_rid(p_cache_index); - TS->font_set_spacing(cache[p_cache_index], p_size, p_spacing, p_value); -} - -int FontData::get_spacing(int p_cache_index, int p_size, TextServer::SpacingType p_spacing) const { - ERR_FAIL_COND_V(p_cache_index < 0, 0); - _ensure_rid(p_cache_index); - return TS->font_get_spacing(cache[p_cache_index], p_size, p_spacing); -} - -int FontData::get_texture_count(int p_cache_index, const Vector2i &p_size) const { +int FontFile::get_texture_count(int p_cache_index, const Vector2i &p_size) const { ERR_FAIL_COND_V(p_cache_index < 0, 0); _ensure_rid(p_cache_index); return TS->font_get_texture_count(cache[p_cache_index], p_size); } -void FontData::clear_textures(int p_cache_index, const Vector2i &p_size) { +void FontFile::clear_textures(int p_cache_index, const Vector2i &p_size) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_clear_textures(cache[p_cache_index], p_size); } -void FontData::remove_texture(int p_cache_index, const Vector2i &p_size, int p_texture_index) { +void FontFile::remove_texture(int p_cache_index, const Vector2i &p_size, int p_texture_index) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_remove_texture(cache[p_cache_index], p_size, p_texture_index); } -void FontData::set_texture_image(int p_cache_index, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) { +void FontFile::set_texture_image(int p_cache_index, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_texture_image(cache[p_cache_index], p_size, p_texture_index, p_image); } -Ref<Image> FontData::get_texture_image(int p_cache_index, const Vector2i &p_size, int p_texture_index) const { +Ref<Image> FontFile::get_texture_image(int p_cache_index, const Vector2i &p_size, int p_texture_index) const { ERR_FAIL_COND_V(p_cache_index < 0, Ref<Image>()); _ensure_rid(p_cache_index); return TS->font_get_texture_image(cache[p_cache_index], p_size, p_texture_index); } -void FontData::set_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) { +void FontFile::set_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_texture_offsets(cache[p_cache_index], p_size, p_texture_index, p_offset); } -PackedInt32Array FontData::get_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index) const { +PackedInt32Array FontFile::get_texture_offsets(int p_cache_index, const Vector2i &p_size, int p_texture_index) const { ERR_FAIL_COND_V(p_cache_index < 0, PackedInt32Array()); _ensure_rid(p_cache_index); return TS->font_get_texture_offsets(cache[p_cache_index], p_size, p_texture_index); } -Array FontData::get_glyph_list(int p_cache_index, const Vector2i &p_size) const { +Array FontFile::get_glyph_list(int p_cache_index, const Vector2i &p_size) const { ERR_FAIL_COND_V(p_cache_index < 0, Array()); _ensure_rid(p_cache_index); return TS->font_get_glyph_list(cache[p_cache_index], p_size); } -void FontData::clear_glyphs(int p_cache_index, const Vector2i &p_size) { +void FontFile::clear_glyphs(int p_cache_index, const Vector2i &p_size) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_clear_glyphs(cache[p_cache_index], p_size); } -void FontData::remove_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) { +void FontFile::remove_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_remove_glyph(cache[p_cache_index], p_size, p_glyph); } -void FontData::set_glyph_advance(int p_cache_index, int p_size, int32_t p_glyph, const Vector2 &p_advance) { +void FontFile::set_glyph_advance(int p_cache_index, int p_size, int32_t p_glyph, const Vector2 &p_advance) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_glyph_advance(cache[p_cache_index], p_size, p_glyph, p_advance); } -Vector2 FontData::get_glyph_advance(int p_cache_index, int p_size, int32_t p_glyph) const { +Vector2 FontFile::get_glyph_advance(int p_cache_index, int p_size, int32_t p_glyph) const { ERR_FAIL_COND_V(p_cache_index < 0, Vector2()); _ensure_rid(p_cache_index); return TS->font_get_glyph_advance(cache[p_cache_index], p_size, p_glyph); } -void FontData::set_glyph_offset(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) { +void FontFile::set_glyph_offset(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_glyph_offset(cache[p_cache_index], p_size, p_glyph, p_offset); } -Vector2 FontData::get_glyph_offset(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const { +Vector2 FontFile::get_glyph_offset(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const { ERR_FAIL_COND_V(p_cache_index < 0, Vector2()); _ensure_rid(p_cache_index); return TS->font_get_glyph_offset(cache[p_cache_index], p_size, p_glyph); } -void FontData::set_glyph_size(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) { +void FontFile::set_glyph_size(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_glyph_size(cache[p_cache_index], p_size, p_glyph, p_gl_size); } -Vector2 FontData::get_glyph_size(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const { +Vector2 FontFile::get_glyph_size(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const { ERR_FAIL_COND_V(p_cache_index < 0, Vector2()); _ensure_rid(p_cache_index); return TS->font_get_glyph_size(cache[p_cache_index], p_size, p_glyph); } -void FontData::set_glyph_uv_rect(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) { +void FontFile::set_glyph_uv_rect(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_glyph_uv_rect(cache[p_cache_index], p_size, p_glyph, p_uv_rect); } -Rect2 FontData::get_glyph_uv_rect(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const { +Rect2 FontFile::get_glyph_uv_rect(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const { ERR_FAIL_COND_V(p_cache_index < 0, Rect2()); _ensure_rid(p_cache_index); return TS->font_get_glyph_uv_rect(cache[p_cache_index], p_size, p_glyph); } -void FontData::set_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) { +void FontFile::set_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_glyph_texture_idx(cache[p_cache_index], p_size, p_glyph, p_texture_idx); } -int FontData::get_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const { +int FontFile::get_glyph_texture_idx(int p_cache_index, const Vector2i &p_size, int32_t p_glyph) const { ERR_FAIL_COND_V(p_cache_index < 0, 0); _ensure_rid(p_cache_index); return TS->font_get_glyph_texture_idx(cache[p_cache_index], p_size, p_glyph); } -Array FontData::get_kerning_list(int p_cache_index, int p_size) const { +Array FontFile::get_kerning_list(int p_cache_index, int p_size) const { ERR_FAIL_COND_V(p_cache_index < 0, Array()); _ensure_rid(p_cache_index); return TS->font_get_kerning_list(cache[p_cache_index], p_size); } -void FontData::clear_kerning_map(int p_cache_index, int p_size) { +void FontFile::clear_kerning_map(int p_cache_index, int p_size) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_clear_kerning_map(cache[p_cache_index], p_size); } -void FontData::remove_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair) { +void FontFile::remove_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_remove_kerning(cache[p_cache_index], p_size, p_glyph_pair); } -void FontData::set_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) { +void FontFile::set_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_set_kerning(cache[p_cache_index], p_size, p_glyph_pair, p_kerning); } -Vector2 FontData::get_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair) const { +Vector2 FontFile::get_kerning(int p_cache_index, int p_size, const Vector2i &p_glyph_pair) const { ERR_FAIL_COND_V(p_cache_index < 0, Vector2()); _ensure_rid(p_cache_index); return TS->font_get_kerning(cache[p_cache_index], p_size, p_glyph_pair); } -void FontData::render_range(int p_cache_index, const Vector2i &p_size, char32_t p_start, char32_t p_end) { +void FontFile::render_range(int p_cache_index, const Vector2i &p_size, char32_t p_start, char32_t p_end) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_render_range(cache[p_cache_index], p_size, p_start, p_end); } -void FontData::render_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_index) { +void FontFile::render_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_index) { ERR_FAIL_COND(p_cache_index < 0); _ensure_rid(p_cache_index); TS->font_render_glyph(cache[p_cache_index], p_size, p_index); } -RID FontData::get_cache_rid(int p_cache_index) const { - ERR_FAIL_COND_V(p_cache_index < 0, RID()); - _ensure_rid(p_cache_index); - return cache[p_cache_index]; -} - -bool FontData::is_language_supported(const String &p_language) const { - _ensure_rid(0); - return TS->font_is_language_supported(cache[0], p_language); -} - -void FontData::set_language_support_override(const String &p_language, bool p_supported) { +void FontFile::set_language_support_override(const String &p_language, bool p_supported) { _ensure_rid(0); TS->font_set_language_support_override(cache[0], p_language, p_supported); } -bool FontData::get_language_support_override(const String &p_language) const { +bool FontFile::get_language_support_override(const String &p_language) const { _ensure_rid(0); return TS->font_get_language_support_override(cache[0], p_language); } -void FontData::remove_language_support_override(const String &p_language) { +void FontFile::remove_language_support_override(const String &p_language) { _ensure_rid(0); TS->font_remove_language_support_override(cache[0], p_language); } -Vector<String> FontData::get_language_support_overrides() const { +Vector<String> FontFile::get_language_support_overrides() const { _ensure_rid(0); return TS->font_get_language_support_overrides(cache[0]); } -bool FontData::is_script_supported(const String &p_script) const { - _ensure_rid(0); - return TS->font_is_script_supported(cache[0], p_script); -} - -void FontData::set_script_support_override(const String &p_script, bool p_supported) { +void FontFile::set_script_support_override(const String &p_script, bool p_supported) { _ensure_rid(0); TS->font_set_script_support_override(cache[0], p_script, p_supported); } -bool FontData::get_script_support_override(const String &p_script) const { +bool FontFile::get_script_support_override(const String &p_script) const { _ensure_rid(0); return TS->font_get_script_support_override(cache[0], p_script); } -void FontData::remove_script_support_override(const String &p_script) { +void FontFile::remove_script_support_override(const String &p_script) { _ensure_rid(0); TS->font_remove_script_support_override(cache[0], p_script); } -Vector<String> FontData::get_script_support_overrides() const { +Vector<String> FontFile::get_script_support_overrides() const { _ensure_rid(0); return TS->font_get_script_support_overrides(cache[0]); } -void FontData::set_opentype_feature_overrides(const Dictionary &p_overrides) { +void FontFile::set_opentype_feature_overrides(const Dictionary &p_overrides) { _ensure_rid(0); TS->font_set_opentype_feature_overrides(cache[0], p_overrides); } -Dictionary FontData::get_opentype_feature_overrides() const { +Dictionary FontFile::get_opentype_feature_overrides() const { _ensure_rid(0); return TS->font_get_opentype_feature_overrides(cache[0]); } -bool FontData::has_char(char32_t p_char) const { - _ensure_rid(0); - return TS->font_has_char(cache[0], p_char); -} - -String FontData::get_supported_chars() const { - _ensure_rid(0); - return TS->font_get_supported_chars(cache[0]); -} - -int32_t FontData::get_glyph_index(int p_size, char32_t p_char, char32_t p_variation_selector) const { +int32_t FontFile::get_glyph_index(int p_size, char32_t p_char, char32_t p_variation_selector) const { _ensure_rid(0); return TS->font_get_glyph_index(cache[0], p_size, p_char, p_variation_selector); } -Dictionary FontData::get_supported_feature_list() const { - _ensure_rid(0); - return TS->font_supported_feature_list(cache[0]); -} - -Dictionary FontData::get_supported_variation_list() const { - _ensure_rid(0); - return TS->font_supported_variation_list(cache[0]); -} - -FontData::FontData() { +FontFile::FontFile() { /* NOP */ } -FontData::~FontData() { - _clear_cache(); +FontFile::~FontFile() { + reset_state(); } /*************************************************************************/ +/* FontVariation */ +/*************************************************************************/ -void Font::_data_changed() { - for (int i = 0; i < rids.size(); i++) { - rids.write[i] = RID(); - } - emit_changed(); -} - -void Font::_ensure_rid(int p_index) const { - // Find or create cache record. - if (!rids[p_index].is_valid() && data[p_index].is_valid()) { - rids.write[p_index] = data[p_index]->find_cache(variation_coordinates); - } -} +void FontVariation::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_base_font", "font"), &FontVariation::set_base_font); + ClassDB::bind_method(D_METHOD("get_base_font"), &FontVariation::get_base_font); -void Font::_bind_methods() { - ClassDB::bind_method(D_METHOD("add_data", "data"), &Font::add_data); - ClassDB::bind_method(D_METHOD("set_data", "idx", "data"), &Font::set_data); - ClassDB::bind_method(D_METHOD("get_data_count"), &Font::get_data_count); - ClassDB::bind_method(D_METHOD("get_data", "idx"), &Font::get_data); - ClassDB::bind_method(D_METHOD("get_data_rid", "idx"), &Font::get_data_rid); - ClassDB::bind_method(D_METHOD("clear_data"), &Font::clear_data); - ClassDB::bind_method(D_METHOD("remove_data", "idx"), &Font::remove_data); - - ClassDB::bind_method(D_METHOD("set_variation_coordinates", "variation_coordinates"), &Font::set_variation_coordinates); - ClassDB::bind_method(D_METHOD("get_variation_coordinates"), &Font::get_variation_coordinates); - ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "variation_coordinates"), "set_variation_coordinates", "get_variation_coordinates"); - - ClassDB::bind_method(D_METHOD("set_spacing", "spacing", "value"), &Font::set_spacing); - ClassDB::bind_method(D_METHOD("get_spacing", "spacing"), &Font::get_spacing); + ClassDB::bind_method(D_METHOD("set_variation_opentype", "coords"), &FontVariation::set_variation_opentype); + ClassDB::bind_method(D_METHOD("get_variation_opentype"), &FontVariation::get_variation_opentype); - ADD_GROUP("Extra Spacing", "spacing"); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_top", PROPERTY_HINT_NONE, "suffix:px"), "set_spacing", "get_spacing", TextServer::SPACING_TOP); - ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_bottom", PROPERTY_HINT_NONE, "suffix:px"), "set_spacing", "get_spacing", TextServer::SPACING_BOTTOM); + ClassDB::bind_method(D_METHOD("set_variation_embolden", "strength"), &FontVariation::set_variation_embolden); + ClassDB::bind_method(D_METHOD("get_variation_embolden"), &FontVariation::get_variation_embolden); - ClassDB::bind_method(D_METHOD("get_height", "size"), &Font::get_height, DEFVAL(DEFAULT_FONT_SIZE)); - ClassDB::bind_method(D_METHOD("get_ascent", "size"), &Font::get_ascent, DEFVAL(DEFAULT_FONT_SIZE)); - ClassDB::bind_method(D_METHOD("get_descent", "size"), &Font::get_descent, DEFVAL(DEFAULT_FONT_SIZE)); - ClassDB::bind_method(D_METHOD("get_underline_position", "size"), &Font::get_underline_position, DEFVAL(DEFAULT_FONT_SIZE)); - ClassDB::bind_method(D_METHOD("get_underline_thickness", "size"), &Font::get_underline_thickness, DEFVAL(DEFAULT_FONT_SIZE)); + ClassDB::bind_method(D_METHOD("set_variation_face_index", "face_index"), &FontVariation::set_variation_face_index); + ClassDB::bind_method(D_METHOD("get_variation_face_index"), &FontVariation::get_variation_face_index); - ClassDB::bind_method(D_METHOD("get_string_size", "text", "size", "alignment", "width", "flags"), &Font::get_string_size, DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND)); - ClassDB::bind_method(D_METHOD("get_multiline_string_size", "text", "width", "size", "flags"), &Font::get_multiline_string_size, DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND)); + ClassDB::bind_method(D_METHOD("set_variation_transform", "transform"), &FontVariation::set_variation_transform); + ClassDB::bind_method(D_METHOD("get_variation_transform"), &FontVariation::get_variation_transform); - ClassDB::bind_method(D_METHOD("draw_string", "canvas_item", "pos", "text", "alignment", "width", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND)); - ClassDB::bind_method(D_METHOD("draw_multiline_string", "canvas_item", "pos", "text", "alignment", "width", "max_lines", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_multiline_string, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND)); + ClassDB::bind_method(D_METHOD("set_opentype_features", "features"), &FontVariation::set_opentype_features); - ClassDB::bind_method(D_METHOD("get_char_size", "char", "next", "size"), &Font::get_char_size, DEFVAL(0), DEFVAL(DEFAULT_FONT_SIZE)); - ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "pos", "char", "next", "size", "modulate", "outline_size", "outline_modulate"), &Font::draw_char, DEFVAL(0), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0))); + ClassDB::bind_method(D_METHOD("set_spacing", "spacing", "value"), &FontVariation::set_spacing); - ClassDB::bind_method(D_METHOD("has_char", "char"), &Font::has_char); - ClassDB::bind_method(D_METHOD("get_supported_chars"), &Font::get_supported_chars); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "base_font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_base_font", "get_base_font"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "fallbacks", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "Font")), "set_fallbacks", "get_fallbacks"); - ClassDB::bind_method(D_METHOD("update_changes"), &Font::update_changes); + ADD_GROUP("Variation", "variation"); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "variation_opentype"), "set_variation_opentype", "get_variation_opentype"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "variation_face_index"), "set_variation_face_index", "get_variation_face_index"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "variation_embolden", PROPERTY_HINT_RANGE, "-2,2,0.01"), "set_variation_embolden", "get_variation_embolden"); + ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "variation_transform", PROPERTY_HINT_NONE, "suffix:px"), "set_variation_transform", "get_variation_transform"); - ClassDB::bind_method(D_METHOD("get_rids"), &Font::get_rids); -} + ADD_GROUP("OpenType Features", "opentype"); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "opentype_features"), "set_opentype_features", "get_opentype_features"); -bool Font::_set(const StringName &p_name, const Variant &p_value) { - Vector<String> tokens = p_name.operator String().split("/"); -#ifndef DISABLE_DEPRECATED - if (tokens.size() == 1 && tokens[0] == "font_data") { - // Compatibility, DynamicFont main data. - Ref<FontData> fd = p_value; - if (fd.is_valid()) { - add_data(fd); - return true; - } - return false; - } else if (tokens.size() == 2 && tokens[0] == "fallback") { - // Compatibility, DynamicFont fallback data. - Ref<FontData> fd = p_value; - if (fd.is_valid()) { - add_data(fd); - return true; - } - return false; - } else if (tokens.size() == 1 && tokens[0] == "fallback") { - // Compatibility, BitmapFont fallback data. - Ref<Font> f = p_value; - if (f.is_valid()) { - for (int i = 0; i < f->get_data_count(); i++) { - add_data(f->get_data(i)); - } - return true; - } - return false; - } -#endif /* DISABLE_DEPRECATED */ - if (tokens.size() == 2 && tokens[0] == "data") { - int idx = tokens[1].to_int(); - Ref<FontData> fd = p_value; - if (fd.is_valid()) { - if (idx == data.size()) { - add_data(fd); - return true; - } else if (idx >= 0 && idx < data.size()) { - set_data(idx, fd); - return true; - } else { - return false; - } - } else if (idx >= 0 && idx < data.size()) { - remove_data(idx); - return true; - } - } - return false; + ADD_GROUP("Extra Spacing", "spacing"); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_glyph", PROPERTY_HINT_NONE, "suffix:px"), "set_spacing", "get_spacing", TextServer::SPACING_GLYPH); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_space", PROPERTY_HINT_NONE, "suffix:px"), "set_spacing", "get_spacing", TextServer::SPACING_SPACE); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_top", PROPERTY_HINT_NONE, "suffix:px"), "set_spacing", "get_spacing", TextServer::SPACING_TOP); + ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_bottom", PROPERTY_HINT_NONE, "suffix:px"), "set_spacing", "get_spacing", TextServer::SPACING_BOTTOM); } -bool Font::_get(const StringName &p_name, Variant &r_ret) const { - Vector<String> tokens = p_name.operator String().split("/"); - if (tokens.size() == 2 && tokens[0] == "data") { - int idx = tokens[1].to_int(); +void FontVariation::_update_rids() const { + Ref<Font> f = _get_base_font_or_default(); - if (idx == data.size()) { - r_ret = Ref<FontData>(); - return true; - } else if (idx >= 0 && idx < data.size()) { - r_ret = get_data(idx); - return true; + rids.clear(); + if (fallbacks.is_empty() && f.is_valid()) { + RID rid = _get_rid(); + if (rid.is_valid()) { + rids.push_back(rid); } - } - return false; -} - -void Font::_get_property_list(List<PropertyInfo> *p_list) const { - for (int i = 0; i < data.size(); i++) { - p_list->push_back(PropertyInfo(Variant::OBJECT, "data/" + itos(i), PROPERTY_HINT_RESOURCE_TYPE, "FontData")); + const TypedArray<Font> &base_fallbacks = f->get_fallbacks(); + for (int i = 0; i < base_fallbacks.size(); i++) { + _update_rids_fb(base_fallbacks[i], 0); + } + } else { + _update_rids_fb(const_cast<FontVariation *>(this), 0); } - p_list->push_back(PropertyInfo(Variant::OBJECT, "data/" + itos(data.size()), PROPERTY_HINT_RESOURCE_TYPE, "FontData")); + dirty_rids = false; } -void Font::reset_state() { - for (int i = 0; i < data.size(); i++) { - if (data[i].is_valid()) { - data.write[i]->connect(SNAME("changed"), callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED); - } +void FontVariation::reset_state() { + if (base_font.is_valid()) { + base_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids)); + base_font.unref(); } - cache.clear(); - cache_wrap.clear(); - data.clear(); - rids.clear(); - - variation_coordinates.clear(); - spacing_bottom = 0; - spacing_top = 0; -} -Dictionary Font::get_feature_list() const { - Dictionary out; - for (int i = 0; i < data.size(); i++) { - Dictionary data_ftrs = data[i]->get_supported_feature_list(); - for (const Variant *ftr = data_ftrs.next(nullptr); ftr != nullptr; ftr = data_ftrs.next(ftr)) { - out[*ftr] = data_ftrs[*ftr]; - } + if (theme_font.is_valid()) { + theme_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids)); + theme_font.unref(); } - return out; -} -void Font::add_data(const Ref<FontData> &p_data) { - ERR_FAIL_COND(p_data.is_null()); - data.push_back(p_data); - rids.push_back(RID()); + variation = Variation(); + opentype_features = Dictionary(); - if (data[data.size() - 1].is_valid()) { - data.write[data.size() - 1]->connect(SNAME("changed"), callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED); - Dictionary data_var_list = p_data->get_supported_variation_list(); - for (int j = 0; j < data_var_list.size(); j++) { - int32_t tag = data_var_list.get_key_at_index(j); - Vector3i value = data_var_list.get_value_at_index(j); - if (!variation_coordinates.has(tag) && !variation_coordinates.has(TS->tag_to_name(tag))) { - variation_coordinates[TS->tag_to_name(tag)] = value.z; - } - } + for (int i = 0; i < TextServer::SPACING_MAX; i++) { + extra_spacing[i] = 0; } - cache.clear(); - cache_wrap.clear(); - - emit_changed(); - notify_property_list_changed(); + Font::reset_state(); } -void Font::set_data(int p_idx, const Ref<FontData> &p_data) { - ERR_FAIL_COND(p_data.is_null()); - ERR_FAIL_INDEX(p_idx, data.size()); - - if (data[p_idx].is_valid()) { - data.write[p_idx]->disconnect(SNAME("changed"), callable_mp(this, &Font::_data_changed)); - } - - data.write[p_idx] = p_data; - rids.write[p_idx] = RID(); - Dictionary data_var_list = p_data->get_supported_variation_list(); - for (int j = 0; j < data_var_list.size(); j++) { - int32_t tag = data_var_list.get_key_at_index(j); - Vector3i value = data_var_list.get_value_at_index(j); - if (!variation_coordinates.has(tag) && !variation_coordinates.has(TS->tag_to_name(tag))) { - variation_coordinates[TS->tag_to_name(tag)] = value.z; +void FontVariation::set_base_font(const Ref<Font> &p_font) { + if (base_font != p_font) { + if (base_font.is_valid()) { + base_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids)); } + base_font = p_font; + if (base_font.is_valid()) { + base_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(this), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED); + } + _invalidate_rids(); + notify_property_list_changed(); } - - if (data[p_idx].is_valid()) { - data.write[p_idx]->connect(SNAME("changed"), callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED); - } - - cache.clear(); - cache_wrap.clear(); - - emit_changed(); - notify_property_list_changed(); -} - -int Font::get_data_count() const { - return data.size(); } -Ref<FontData> Font::get_data(int p_idx) const { - ERR_FAIL_INDEX_V(p_idx, data.size(), Ref<FontData>()); - return data[p_idx]; +Ref<Font> FontVariation::get_base_font() const { + return base_font; } -RID Font::get_data_rid(int p_idx) const { - ERR_FAIL_INDEX_V(p_idx, data.size(), RID()); - _ensure_rid(p_idx); - return rids[p_idx]; -} - -void Font::clear_data() { - for (int i = 0; i < data.size(); i++) { - if (data[i].is_valid()) { - data.write[i]->connect(SNAME("changed"), callable_mp(this, &Font::_data_changed), varray(), CONNECT_REFERENCE_COUNTED); - } +Ref<Font> FontVariation::_get_base_font_or_default() const { + if (theme_font.is_valid()) { + theme_font->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids)); + theme_font.unref(); } - data.clear(); - rids.clear(); -} -void Font::remove_data(int p_idx) { - ERR_FAIL_INDEX(p_idx, data.size()); - - if (data[p_idx].is_valid()) { - data.write[p_idx]->disconnect(SNAME("changed"), callable_mp(this, &Font::_data_changed)); + if (base_font.is_valid()) { + return base_font; } - data.remove_at(p_idx); - rids.remove_at(p_idx); - - cache.clear(); - cache_wrap.clear(); + // Check the project-defined Theme resource. + if (Theme::get_project_default().is_valid()) { + List<StringName> theme_types; + Theme::get_project_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types); - emit_changed(); - notify_property_list_changed(); -} + for (const StringName &E : theme_types) { + if (Theme::get_project_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { + Ref<Font> f = Theme::get_project_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); + if (f.is_valid()) { + theme_font = f; + theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED); + } + return f; + } + } + } -void Font::set_variation_coordinates(const Dictionary &p_variation_coordinates) { - _data_changed(); - variation_coordinates = p_variation_coordinates; -} + // Lastly, fall back on the items defined in the default Theme, if they exist. + if (Theme::get_default().is_valid()) { + List<StringName> theme_types; + Theme::get_default()->get_type_dependencies(get_class_name(), StringName(), &theme_types); -Dictionary Font::get_variation_coordinates() const { - return variation_coordinates; -} + for (const StringName &E : theme_types) { + if (Theme::get_default()->has_theme_item(Theme::DATA_TYPE_FONT, "font", E)) { + Ref<Font> f = Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", E); + if (f.is_valid()) { + theme_font = f; + theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED); + } + return f; + } + } -void Font::set_spacing(TextServer::SpacingType p_spacing, int p_value) { - _data_changed(); - switch (p_spacing) { - case TextServer::SPACING_TOP: { - spacing_top = p_value; - } break; - case TextServer::SPACING_BOTTOM: { - spacing_bottom = p_value; - } break; - default: { - ERR_FAIL_MSG("Invalid spacing type: " + itos(p_spacing)); - } break; + // If they don't exist, use any type to return the default/empty value. + Ref<Font> f = Theme::get_default()->get_theme_item(Theme::DATA_TYPE_FONT, "font", StringName()); + if (f.is_valid()) { + theme_font = f; + theme_font->connect(CoreStringNames::get_singleton()->changed, callable_mp(reinterpret_cast<Font *>(const_cast<FontVariation *>(this)), &Font::_invalidate_rids), varray(), CONNECT_REFERENCE_COUNTED); + } + return f; } -} -int Font::get_spacing(TextServer::SpacingType p_spacing) const { - switch (p_spacing) { - case TextServer::SPACING_TOP: { - return spacing_top; - } break; - case TextServer::SPACING_BOTTOM: { - return spacing_bottom; - } break; - default: { - ERR_FAIL_V_MSG(0, "Invalid spacing type: " + itos(p_spacing)); - } break; - } + return Ref<Font>(); } -real_t Font::get_height(int p_size) const { - real_t ret = 0.f; - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); - ret = MAX(ret, TS->font_get_ascent(rids[i], p_size) + TS->font_get_descent(rids[i], p_size)); +void FontVariation::set_variation_opentype(const Dictionary &p_coords) { + if (variation.opentype != p_coords) { + variation.opentype = p_coords; + _invalidate_rids(); } - return ret + spacing_bottom + spacing_top; } -real_t Font::get_ascent(int p_size) const { - real_t ret = 0.f; - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); - ret = MAX(ret, TS->font_get_ascent(rids[i], p_size)); - } - return ret + spacing_top; +Dictionary FontVariation::get_variation_opentype() const { + return variation.opentype; } -real_t Font::get_descent(int p_size) const { - real_t ret = 0.f; - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); - ret = MAX(ret, TS->font_get_descent(rids[i], p_size)); +void FontVariation::set_variation_embolden(float p_strength) { + if (variation.embolden != p_strength) { + variation.embolden = p_strength; + _invalidate_rids(); } - return ret + spacing_bottom; } -real_t Font::get_underline_position(int p_size) const { - real_t ret = 0.f; - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); - ret = MAX(ret, TS->font_get_underline_position(rids[i], p_size)); - } - return ret + spacing_top; +float FontVariation::get_variation_embolden() const { + return variation.embolden; } -real_t Font::get_underline_thickness(int p_size) const { - real_t ret = 0.f; - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); - ret = MAX(ret, TS->font_get_underline_thickness(rids[i], p_size)); +void FontVariation::set_variation_transform(Transform2D p_transform) { + if (variation.transform != p_transform) { + variation.transform = p_transform; + _invalidate_rids(); } - return ret; } -Size2 Font::get_string_size(const String &p_text, int p_size, HorizontalAlignment p_alignment, float p_width, uint16_t p_flags) const { - ERR_FAIL_COND_V(data.is_empty(), Size2()); - - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); - } - - uint64_t hash = p_text.hash64(); - if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { - hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); - hash = hash_djb2_one_64(p_flags, hash); - } - hash = hash_djb2_one_64(p_size, hash); - - Ref<TextLine> buffer; - if (cache.has(hash)) { - buffer = cache.get(hash); - } else { - buffer.instantiate(); - buffer->add_string(p_text, Ref<Font>(this), p_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); - cache.insert(hash, buffer); - } - return buffer->get_size(); +Transform2D FontVariation::get_variation_transform() const { + return variation.transform; } -Size2 Font::get_multiline_string_size(const String &p_text, float p_width, int p_size, uint16_t p_flags) const { - ERR_FAIL_COND_V(data.is_empty(), Size2()); - - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); +void FontVariation::set_variation_face_index(int p_face_index) { + if (variation.face_index != p_face_index) { + variation.face_index = p_face_index; + _invalidate_rids(); } - - uint64_t hash = p_text.hash64(); - uint64_t wrp_hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); - wrp_hash = hash_djb2_one_64(p_flags, wrp_hash); - wrp_hash = hash_djb2_one_64(p_size, wrp_hash); - - Ref<TextParagraph> lines_buffer; - if (cache_wrap.has(wrp_hash)) { - lines_buffer = cache_wrap.get(wrp_hash); - } else { - lines_buffer.instantiate(); - lines_buffer->add_string(p_text, Ref<Font>(this), p_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); - lines_buffer->set_width(p_width); - lines_buffer->set_flags(p_flags); - cache_wrap.insert(wrp_hash, lines_buffer); - } - - Size2 ret; - for (int i = 0; i < lines_buffer->get_line_count(); i++) { - Size2 line_size = lines_buffer->get_line_size(i); - if (lines_buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) { - ret.x = MAX(ret.x, line_size.x); - ret.y += line_size.y; - } else { - ret.y = MAX(ret.y, line_size.y); - ret.x += line_size.x; - } - } - return ret; } -void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint16_t p_flags) const { - ERR_FAIL_COND(data.is_empty()); - - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); - } - - uint64_t hash = p_text.hash64(); - if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) { - hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); - hash = hash_djb2_one_64(p_flags, hash); - } - hash = hash_djb2_one_64(p_size, hash); - - Ref<TextLine> buffer; - if (cache.has(hash)) { - buffer = cache.get(hash); - } else { - buffer.instantiate(); - buffer->add_string(p_text, Ref<Font>(this), p_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); - cache.insert(hash, buffer); - } - - Vector2 ofs = p_pos; - if (buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) { - ofs.y -= buffer->get_line_ascent(); - } else { - ofs.x -= buffer->get_line_ascent(); - } - - buffer->set_width(p_width); - buffer->set_horizontal_alignment(p_alignment); - buffer->set_flags(p_flags); - - if (p_outline_size > 0 && p_outline_modulate.a != 0.0f) { - buffer->draw_outline(p_canvas_item, ofs, p_outline_size, p_outline_modulate); - } - buffer->draw(p_canvas_item, ofs, p_modulate); +int FontVariation::get_variation_face_index() const { + return variation.face_index; } -void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_max_lines, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint16_t p_flags) const { - ERR_FAIL_COND(data.is_empty()); - - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); - } - - uint64_t hash = p_text.hash64(); - uint64_t wrp_hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash); - wrp_hash = hash_djb2_one_64(p_flags, wrp_hash); - wrp_hash = hash_djb2_one_64(p_size, wrp_hash); - - Ref<TextParagraph> lines_buffer; - if (cache_wrap.has(wrp_hash)) { - lines_buffer = cache_wrap.get(wrp_hash); - } else { - lines_buffer.instantiate(); - lines_buffer->add_string(p_text, Ref<Font>(this), p_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); - lines_buffer->set_width(p_width); - lines_buffer->set_flags(p_flags); - cache_wrap.insert(wrp_hash, lines_buffer); - } - - lines_buffer->set_alignment(p_alignment); - - Vector2 lofs = p_pos; - for (int i = 0; i < lines_buffer->get_line_count(); i++) { - if (lines_buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) { - if (i == 0) { - lofs.y -= lines_buffer->get_line_ascent(0); - } - } else { - if (i == 0) { - lofs.x -= lines_buffer->get_line_ascent(0); - } - } - if (p_width > 0) { - lines_buffer->set_alignment(p_alignment); - } - - if (p_outline_size > 0 && p_outline_modulate.a != 0.0f) { - lines_buffer->draw_line_outline(p_canvas_item, lofs, i, p_outline_size, p_outline_modulate); - } - lines_buffer->draw_line(p_canvas_item, lofs, i, p_modulate); - - Size2 line_size = lines_buffer->get_line_size(i); - if (lines_buffer->get_orientation() == TextServer::ORIENTATION_HORIZONTAL) { - lofs.y += line_size.y; - } else { - lofs.x += line_size.x; - } - - if ((p_max_lines > 0) && (i >= p_max_lines)) { - return; - } +void FontVariation::set_opentype_features(const Dictionary &p_features) { + if (opentype_features != p_features) { + opentype_features = p_features; + _invalidate_rids(); } } -Size2 Font::get_char_size(char32_t p_char, char32_t p_next, int p_size) const { - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); - if (data[i]->has_char(p_char)) { - int32_t glyph_a = TS->font_get_glyph_index(rids[i], p_size, p_char, 0); - Size2 ret = Size2(TS->font_get_glyph_advance(rids[i], p_size, glyph_a).x, TS->font_get_ascent(rids[i], p_size) + TS->font_get_descent(rids[i], p_size)); - if ((p_next != 0) && data[i]->has_char(p_next)) { - int32_t glyph_b = TS->font_get_glyph_index(rids[i], p_size, p_next, 0); - ret.x -= TS->font_get_kerning(rids[i], p_size, Vector2i(glyph_a, glyph_b)).x; - } - return ret; - } - } - return Size2(); +Dictionary FontVariation::get_opentype_features() const { + return opentype_features; } -real_t Font::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate) const { - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); - if (data[i]->has_char(p_char)) { - int32_t glyph_a = TS->font_get_glyph_index(rids[i], p_size, p_char, 0); - real_t ret = TS->font_get_glyph_advance(rids[i], p_size, glyph_a).x; - if ((p_next != 0) && data[i]->has_char(p_next)) { - int32_t glyph_b = TS->font_get_glyph_index(rids[i], p_size, p_next, 0); - ret -= TS->font_get_kerning(rids[i], p_size, Vector2i(glyph_a, glyph_b)).x; - } - - if (p_outline_size > 0 && p_outline_modulate.a != 0.0f) { - TS->font_draw_glyph_outline(rids[i], p_canvas_item, p_size, p_outline_size, p_pos, glyph_a, p_outline_modulate); - } - TS->font_draw_glyph(rids[i], p_canvas_item, p_size, p_pos, glyph_a, p_modulate); - return ret; - } +void FontVariation::set_spacing(TextServer::SpacingType p_spacing, int p_value) { + ERR_FAIL_INDEX((int)p_spacing, TextServer::SPACING_MAX); + if (extra_spacing[p_spacing] != p_value) { + extra_spacing[p_spacing] = p_value; + _invalidate_rids(); } - return 0; } -bool Font::has_char(char32_t p_char) const { - for (int i = 0; i < data.size(); i++) { - if (data[i]->has_char(p_char)) { - return true; - } - } - return false; +int FontVariation::get_spacing(TextServer::SpacingType p_spacing) const { + ERR_FAIL_INDEX_V((int)p_spacing, TextServer::SPACING_MAX, 0); + return extra_spacing[p_spacing]; } -String Font::get_supported_chars() const { - String chars; - for (int i = 0; i < data.size(); i++) { - String data_chars = data[i]->get_supported_chars(); - for (int j = 0; j < data_chars.length(); j++) { - if (chars.find_char(data_chars[j]) == -1) { - chars += data_chars[j]; - } - } +RID FontVariation::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform) const { + Ref<Font> f = _get_base_font_or_default(); + if (f.is_valid()) { + return f->find_variation(p_variation_coordinates, p_face_index, p_strength, p_transform); } - return chars; + return RID(); } -Array Font::get_rids() const { - Array _rids; - for (int i = 0; i < data.size(); i++) { - _ensure_rid(i); - if (rids[i].is_valid()) { - _rids.push_back(rids[i]); - } +RID FontVariation::_get_rid() const { + Ref<Font> f = _get_base_font_or_default(); + if (f.is_valid()) { + return f->find_variation(variation.opentype, variation.face_index, variation.embolden, variation.transform); } - return _rids; -} - -void Font::update_changes() { - emit_changed(); + return RID(); } -Font::Font() { - cache.set_capacity(128); - cache_wrap.set_capacity(32); +FontVariation::FontVariation() { + for (int i = 0; i < TextServer::SPACING_MAX; i++) { + extra_spacing[i] = 0; + } } -Font::~Font() { - clear_data(); - cache.clear(); - cache_wrap.clear(); +FontVariation::~FontVariation() { + reset_state(); } diff --git a/scene/resources/font.h b/scene/resources/font.h index 950959e054..40b223b0f5 100644 --- a/scene/resources/font.h +++ b/scene/resources/font.h @@ -37,16 +37,108 @@ #include "scene/resources/texture.h" #include "servers/text_server.h" +class TextLine; +class TextParagraph; + +/*************************************************************************/ +/* Font */ +/*************************************************************************/ + +class Font : public Resource { + GDCLASS(Font, Resource); + + // Shaped string cache. + mutable LRUCache<uint64_t, Ref<TextLine>> cache; + mutable LRUCache<uint64_t, Ref<TextParagraph>> cache_wrap; + +protected: + // Output. + mutable TypedArray<RID> rids; + mutable bool dirty_rids = true; + + // Fallbacks. + static constexpr int MAX_FALLBACK_DEPTH = 64; + TypedArray<Font> fallbacks; + + static void _bind_methods(); + + virtual void _update_rids_fb(const Ref<Font> &p_f, int p_depth) const; + virtual void _update_rids() const; + virtual bool _is_cyclic(const Ref<Font> &p_f, int p_depth) const; + + virtual void reset_state() override; + +public: + virtual void _invalidate_rids(); + + static constexpr int DEFAULT_FONT_SIZE = 16; + + // Fallbacks. + virtual void set_fallbacks(const TypedArray<Font> &p_fallbacks); + virtual TypedArray<Font> get_fallbacks() const; + + // Output. + virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D()) const { return RID(); }; + virtual RID _get_rid() const { return RID(); }; + virtual TypedArray<RID> get_rids() const; + + // Font metrics. + virtual real_t get_height(int p_font_size) const; + virtual real_t get_ascent(int p_font_size) const; + virtual real_t get_descent(int p_font_size) const; + virtual real_t get_underline_position(int p_font_size) const; + virtual real_t get_underline_thickness(int p_font_size) const; + + virtual String get_font_name() const; + virtual String get_font_style_name() const; + virtual uint32_t get_font_style() const; + + virtual int get_spacing(TextServer::SpacingType p_spacing) const { return 0; }; + virtual Dictionary get_opentype_features() const; + + // Drawing string. + virtual void set_cache_capacity(int p_single_line, int p_multi_line); + + virtual Size2 get_string_size(const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = DEFAULT_FONT_SIZE, uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; + virtual Size2 get_multiline_string_size(const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = DEFAULT_FONT_SIZE, int p_max_lines = -1, uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; + + virtual void draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1.0, 1.0, 1.0), uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; + virtual void draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = DEFAULT_FONT_SIZE, int p_max_lines = -1, const Color &p_modulate = Color(1.0, 1.0, 1.0), uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; + + virtual void draw_string_outline(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = DEFAULT_FONT_SIZE, int p_size = 1, const Color &p_modulate = Color(1.0, 1.0, 1.0), uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; + virtual void draw_multiline_string_outline(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = DEFAULT_FONT_SIZE, int p_max_lines = -1, int p_size = 1, const Color &p_modulate = Color(1.0, 1.0, 1.0), uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const; + + // Drawing char. + virtual Size2 get_char_size(char32_t p_char, int p_font_size = DEFAULT_FONT_SIZE) const; + virtual real_t draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, int p_font_size = DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1.0, 1.0, 1.0)) const; + virtual real_t draw_char_outline(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, int p_font_size = DEFAULT_FONT_SIZE, int p_size = 1, const Color &p_modulate = Color(1.0, 1.0, 1.0)) const; + + // Helper functions. + virtual bool has_char(char32_t p_char) const; + virtual String get_supported_chars() const; + + virtual bool is_language_supported(const String &p_language) const; + virtual bool is_script_supported(const String &p_script) const; + + virtual Dictionary get_supported_feature_list() const; + virtual Dictionary get_supported_variation_list() const; + virtual int64_t get_face_count() const; + + Font(); + ~Font(); +}; + +/*************************************************************************/ +/* FontFile */ /*************************************************************************/ -class FontData : public Resource { - GDCLASS(FontData, Resource); +class FontFile : public Font { + GDCLASS(FontFile, Font); RES_BASE_EXTENSION("fontdata"); // Font source data. const uint8_t *data_ptr = nullptr; size_t data_size = 0; - int face_index = 0; PackedByteArray data; bool antialiased = true; @@ -59,8 +151,11 @@ class FontData : public Resource { TextServer::Hinting hinting = TextServer::HINTING_LIGHT; TextServer::SubpixelPositioning subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO; real_t oversampling = 0.f; - real_t embolden = 0.f; - Transform2D transform; + +#ifndef DISABLE_DEPRECATED + real_t bmp_height = 0.0; + real_t bmp_ascent = 0.0; +#endif // Cache. mutable Vector<RID> cache; @@ -92,20 +187,10 @@ public: virtual void set_data(const PackedByteArray &p_data); virtual PackedByteArray get_data() const; - virtual void set_face_index(int64_t p_index); - virtual int64_t get_face_index() const; - - virtual int64_t get_face_count() const; - // Common properties. virtual void set_font_name(const String &p_name); - virtual String get_font_name() const; - virtual void set_font_style_name(const String &p_name); - virtual String get_font_style_name() const; - virtual void set_font_style(uint32_t p_style); - virtual uint32_t get_font_style() const; virtual void set_antialiased(bool p_antialiased); virtual bool is_antialiased() const; @@ -134,17 +219,12 @@ public: virtual void set_subpixel_positioning(TextServer::SubpixelPositioning p_subpixel); virtual TextServer::SubpixelPositioning get_subpixel_positioning() const; - virtual void set_embolden(float p_strength); - virtual float get_embolden() const; - - virtual void set_transform(Transform2D p_transform); - virtual Transform2D get_transform() const; - virtual void set_oversampling(real_t p_oversampling); virtual real_t get_oversampling() const; // Cache. - virtual RID find_cache(const Dictionary &p_variation_coordinates) const; + virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D()) const override; + virtual RID _get_rid() const override; virtual int get_cache_count() const; virtual void clear_cache(); @@ -157,23 +237,29 @@ public: virtual void set_variation_coordinates(int p_cache_index, const Dictionary &p_variation_coordinates); virtual Dictionary get_variation_coordinates(int p_cache_index) const; - virtual void set_ascent(int p_cache_index, int p_size, real_t p_ascent); - virtual real_t get_ascent(int p_cache_index, int p_size) const; + virtual void set_embolden(int p_cache_index, float p_strength); + virtual float get_embolden(int p_cache_index) const; + + virtual void set_transform(int p_cache_index, Transform2D p_transform); + virtual Transform2D get_transform(int p_cache_index) const; + + virtual void set_face_index(int p_cache_index, int64_t p_index); + virtual int64_t get_face_index(int p_cache_index) const; - virtual void set_descent(int p_cache_index, int p_size, real_t p_descent); - virtual real_t get_descent(int p_cache_index, int p_size) const; + virtual void set_cache_ascent(int p_cache_index, int p_size, real_t p_ascent); + virtual real_t get_cache_ascent(int p_cache_index, int p_size) const; - virtual void set_underline_position(int p_cache_index, int p_size, real_t p_underline_position); - virtual real_t get_underline_position(int p_cache_index, int p_size) const; + virtual void set_cache_descent(int p_cache_index, int p_size, real_t p_descent); + virtual real_t get_cache_descent(int p_cache_index, int p_size) const; - virtual void set_underline_thickness(int p_cache_index, int p_size, real_t p_underline_thickness); - virtual real_t get_underline_thickness(int p_cache_index, int p_size) const; + virtual void set_cache_underline_position(int p_cache_index, int p_size, real_t p_underline_position); + virtual real_t get_cache_underline_position(int p_cache_index, int p_size) const; - virtual void set_scale(int p_cache_index, int p_size, real_t p_scale); // Rendering scale for bitmap fonts (e.g. emoji fonts). - virtual real_t get_scale(int p_cache_index, int p_size) const; + virtual void set_cache_underline_thickness(int p_cache_index, int p_size, real_t p_underline_thickness); + virtual real_t get_cache_underline_thickness(int p_cache_index, int p_size) const; - virtual void set_spacing(int p_cache_index, int p_size, TextServer::SpacingType p_spacing, int p_value); - virtual int get_spacing(int p_cache_index, int p_size, TextServer::SpacingType p_spacing) const; + virtual void set_cache_scale(int p_cache_index, int p_size, real_t p_scale); // Rendering scale for bitmap fonts (e.g. emoji fonts). + virtual real_t get_cache_scale(int p_cache_index, int p_size) const; virtual int get_texture_count(int p_cache_index, const Vector2i &p_size) const; virtual void clear_textures(int p_cache_index, const Vector2i &p_size); @@ -214,16 +300,12 @@ public: virtual void render_range(int p_cache_index, const Vector2i &p_size, char32_t p_start, char32_t p_end); virtual void render_glyph(int p_cache_index, const Vector2i &p_size, int32_t p_index); - virtual RID get_cache_rid(int p_cache_index) const; - // Language/script support override. - virtual bool is_language_supported(const String &p_language) const; virtual void set_language_support_override(const String &p_language, bool p_supported); virtual bool get_language_support_override(const String &p_language) const; virtual void remove_language_support_override(const String &p_language); virtual Vector<String> get_language_support_overrides() const; - virtual bool is_script_supported(const String &p_script) const; virtual void set_script_support_override(const String &p_script, bool p_supported); virtual bool get_script_support_override(const String &p_script) const; virtual void remove_script_support_override(const String &p_script); @@ -233,100 +315,70 @@ public: virtual Dictionary get_opentype_feature_overrides() const; // Base font properties. - virtual bool has_char(char32_t p_char) const; - virtual String get_supported_chars() const; - virtual int32_t get_glyph_index(int p_size, char32_t p_char, char32_t p_variation_selector = 0x0000) const; - virtual Dictionary get_supported_feature_list() const; - virtual Dictionary get_supported_variation_list() const; - - FontData(); - ~FontData(); + FontFile(); + ~FontFile(); }; /*************************************************************************/ +/* FontVariation */ +/*************************************************************************/ -class TextLine; -class TextParagraph; +class FontVariation : public Font { + GDCLASS(FontVariation, Font); -class Font : public Resource { - GDCLASS(Font, Resource); + struct Variation { + Dictionary opentype; + real_t embolden = 0.f; + int face_index = 0; + Transform2D transform; + }; - // Shaped string cache. - mutable LRUCache<uint64_t, Ref<TextLine>> cache; - mutable LRUCache<uint64_t, Ref<TextParagraph>> cache_wrap; - - // Font data cache. - Vector<Ref<FontData>> data; - mutable Vector<RID> rids; + mutable Ref<Font> theme_font; - // Font config. - Dictionary variation_coordinates; - int spacing_bottom = 0; - int spacing_top = 0; + Ref<Font> base_font; - _FORCE_INLINE_ void _data_changed(); - _FORCE_INLINE_ void _ensure_rid(int p_index) const; // Find or create cache record. + Variation variation; + Dictionary opentype_features; + int extra_spacing[TextServer::SPACING_MAX]; protected: static void _bind_methods(); - bool _set(const StringName &p_name, const Variant &p_value); - bool _get(const StringName &p_name, Variant &r_ret) const; - void _get_property_list(List<PropertyInfo> *p_list) const; + virtual void _update_rids() const override; virtual void reset_state() override; public: - static const int DEFAULT_FONT_SIZE = 16; - - Dictionary get_feature_list() const; + virtual void set_base_font(const Ref<Font> &p_font); + virtual Ref<Font> get_base_font() const; + virtual Ref<Font> _get_base_font_or_default() const; - // Font data. - virtual void add_data(const Ref<FontData> &p_data); - virtual void set_data(int p_idx, const Ref<FontData> &p_data); - virtual int get_data_count() const; - virtual Ref<FontData> get_data(int p_idx) const; - virtual RID get_data_rid(int p_idx) const; - virtual void clear_data(); - virtual void remove_data(int p_idx); + virtual void set_variation_opentype(const Dictionary &p_coords); + virtual Dictionary get_variation_opentype() const; - // Font configuration. - virtual void set_variation_coordinates(const Dictionary &p_variation_coordinates); - virtual Dictionary get_variation_coordinates() const; + virtual void set_variation_embolden(float p_strength); + virtual float get_variation_embolden() const; - virtual void set_spacing(TextServer::SpacingType p_spacing, int p_value); - virtual int get_spacing(TextServer::SpacingType p_spacing) const; - - // Font metrics. - virtual real_t get_height(int p_size = DEFAULT_FONT_SIZE) const; - virtual real_t get_ascent(int p_size = DEFAULT_FONT_SIZE) const; - virtual real_t get_descent(int p_size = DEFAULT_FONT_SIZE) const; - virtual real_t get_underline_position(int p_size = DEFAULT_FONT_SIZE) const; - virtual real_t get_underline_thickness(int p_size = DEFAULT_FONT_SIZE) const; + virtual void set_variation_transform(Transform2D p_transform); + virtual Transform2D get_variation_transform() const; - // Drawing string. - virtual Size2 get_string_size(const String &p_text, int p_size = DEFAULT_FONT_SIZE, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; - virtual Size2 get_multiline_string_size(const String &p_text, float p_width = -1, int p_size = DEFAULT_FONT_SIZE, uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND) const; + virtual void set_variation_face_index(int p_face_index); + virtual int get_variation_face_index() const; - virtual void draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_size = DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; - virtual void draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_max_lines = -1, int p_size = DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const; + virtual void set_opentype_features(const Dictionary &p_features); + virtual Dictionary get_opentype_features() const override; - // Helper functions. - virtual bool has_char(char32_t p_char) const; - virtual String get_supported_chars() const; - - // Drawing char. - virtual Size2 get_char_size(char32_t p_char, char32_t p_next = 0, int p_size = DEFAULT_FONT_SIZE) const; - virtual real_t draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, int p_size = DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0)) const; - - Array get_rids() const; + virtual void set_spacing(TextServer::SpacingType p_spacing, int p_value); + virtual int get_spacing(TextServer::SpacingType p_spacing) const override; - void update_changes(); + // Output. + virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D()) const override; + virtual RID _get_rid() const override; - Font(); - ~Font(); + FontVariation(); + ~FontVariation(); }; #endif /* FONT_H */ diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index 9fa483bf36..68441afb1c 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -2376,7 +2376,10 @@ void TextMesh::_create_mesh_array(Array &p_arr) const { TS->shaped_text_set_direction(text_rid, text_direction); String text = (uppercase) ? TS->string_to_upper(xl_text, language) : xl_text; - TS->shaped_text_add_string(text_rid, text, font->get_rids(), font_size, opentype_features, language); + TS->shaped_text_add_string(text_rid, text, font->get_rids(), font_size, font->get_opentype_features(), language); + for (int i = 0; i < TextServer::SPACING_MAX; i++) { + TS->shaped_text_set_spacing(text_rid, TextServer::SpacingType(i), font->get_spacing(TextServer::SpacingType(i))); + } Array stt; if (st_parser == TextServer::STRUCTURED_TEXT_CUSTOM) { @@ -2394,7 +2397,10 @@ void TextMesh::_create_mesh_array(Array &p_arr) const { } else if (dirty_font) { int spans = TS->shaped_get_span_count(text_rid); for (int i = 0; i < spans; i++) { - TS->shaped_set_span_update_font(text_rid, i, font->get_rids(), font_size, opentype_features); + TS->shaped_set_span_update_font(text_rid, i, font->get_rids(), font_size, font->get_opentype_features()); + } + for (int i = 0; i < TextServer::SPACING_MAX; i++) { + TS->shaped_text_set_spacing(text_rid, TextServer::SpacingType(i), font->get_spacing(TextServer::SpacingType(i))); } dirty_font = false; @@ -2679,10 +2685,6 @@ void TextMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &TextMesh::set_text_direction); ClassDB::bind_method(D_METHOD("get_text_direction"), &TextMesh::get_text_direction); - ClassDB::bind_method(D_METHOD("set_opentype_feature", "tag", "value"), &TextMesh::set_opentype_feature); - ClassDB::bind_method(D_METHOD("get_opentype_feature", "tag"), &TextMesh::get_opentype_feature); - ClassDB::bind_method(D_METHOD("clear_opentype_features"), &TextMesh::clear_opentype_features); - ClassDB::bind_method(D_METHOD("set_language", "language"), &TextMesh::set_language); ClassDB::bind_method(D_METHOD("get_language"), &TextMesh::get_language); @@ -2701,11 +2703,9 @@ void TextMesh::_bind_methods() { ADD_GROUP("Text", ""); ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_font", "get_font"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "font_size", PROPERTY_HINT_RANGE, "1,127,1,suffix:px"), "set_font_size", "get_font_size"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "font_size", PROPERTY_HINT_RANGE, "1,256,1,or_greater,suffix:px"), "set_font_size", "get_font_size"); ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uppercase"), "set_uppercase", "is_uppercase"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options"); ADD_GROUP("Mesh", ""); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pixel_size", PROPERTY_HINT_RANGE, "0.0001,128,0.0001,suffix:m"), "set_pixel_size", "get_pixel_size"); @@ -2713,9 +2713,11 @@ void TextMesh::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth", PROPERTY_HINT_RANGE, "0.0,100.0,0.001,or_greater,suffix:m"), "set_depth", "get_depth"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "width", PROPERTY_HINT_NONE, "suffix:m"), "set_width", "get_width"); - ADD_GROUP("Locale", ""); + ADD_GROUP("BiDi", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left"), "set_text_direction", "get_text_direction"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options"); } void TextMesh::_notification(int p_what) { @@ -2732,56 +2734,6 @@ void TextMesh::_notification(int p_what) { } } -bool TextMesh::_set(const StringName &p_name, const Variant &p_value) { - String str = p_name; - if (str.begins_with("opentype_features/")) { - String name = str.get_slicec('/', 1); - int32_t tag = TS->name_to_tag(name); - int value = p_value; - if (value == -1) { - if (opentype_features.has(tag)) { - opentype_features.erase(tag); - dirty_font = true; - _request_update(); - } - } else { - if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) { - opentype_features[tag] = value; - dirty_font = true; - _request_update(); - } - } - notify_property_list_changed(); - return true; - } - - return false; -} - -bool TextMesh::_get(const StringName &p_name, Variant &r_ret) const { - String str = p_name; - if (str.begins_with("opentype_features/")) { - String name = str.get_slicec('/', 1); - int32_t tag = TS->name_to_tag(name); - if (opentype_features.has(tag)) { - r_ret = opentype_features[tag]; - return true; - } else { - r_ret = -1; - return true; - } - } - return false; -} - -void TextMesh::_get_property_list(List<PropertyInfo> *p_list) const { - for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) { - String name = TS->tag_to_name(*ftr); - p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name)); - } - p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); -} - TextMesh::TextMesh() { primitive_type = PRIMITIVE_TRIANGLES; text_rid = TS->create_shaped_text(); @@ -2845,7 +2797,7 @@ Ref<Font> TextMesh::get_font() const { } Ref<Font> TextMesh::_get_font_or_default() const { - if (font_override.is_valid() && font_override->get_data_count() > 0) { + if (font_override.is_valid()) { return font_override; } @@ -2952,29 +2904,6 @@ TextServer::Direction TextMesh::get_text_direction() const { return text_direction; } -void TextMesh::clear_opentype_features() { - opentype_features.clear(); - dirty_font = true; - _request_update(); -} - -void TextMesh::set_opentype_feature(const String &p_name, int p_value) { - int32_t tag = TS->name_to_tag(p_name); - if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) { - opentype_features[tag] = p_value; - dirty_font = true; - _request_update(); - } -} - -int TextMesh::get_opentype_feature(const String &p_name) const { - int32_t tag = TS->name_to_tag(p_name); - if (!opentype_features.has(tag)) { - return -1; - } - return opentype_features[tag]; -} - void TextMesh::set_language(const String &p_language) { if (language != p_language) { language = p_language; diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h index a6d8978c2d..cb93211756 100644 --- a/scene/resources/primitive_meshes.h +++ b/scene/resources/primitive_meshes.h @@ -525,7 +525,6 @@ private: HorizontalAlignment horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER; bool uppercase = false; - Dictionary opentype_features; String language; TextServer::Direction text_direction = TextServer::DIRECTION_AUTO; TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT; @@ -548,10 +547,6 @@ protected: virtual void _create_mesh_array(Array &p_arr) const override; - bool _set(const StringName &p_name, const Variant &p_value); - bool _get(const StringName &p_name, Variant &r_ret) const; - void _get_property_list(List<PropertyInfo> *p_list) const; - public: GDVIRTUAL2RC(Array, _structured_text_parser, Array, String) @@ -574,10 +569,6 @@ public: void set_text_direction(TextServer::Direction p_text_direction); TextServer::Direction get_text_direction() const; - void set_opentype_feature(const String &p_name, int p_value); - int get_opentype_feature(const String &p_name) const; - void clear_opentype_features(); - void set_language(const String &p_language); String get_language() const; diff --git a/scene/resources/text_line.cpp b/scene/resources/text_line.cpp index d6e7ca3478..f32b7feb4b 100644 --- a/scene/resources/text_line.cpp +++ b/scene/resources/text_line.cpp @@ -55,7 +55,7 @@ void TextLine::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextLine::set_bidi_override); - ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language", "meta"), &TextLine::add_string, DEFVAL(Dictionary()), DEFVAL(""), DEFVAL(Variant())); + ClassDB::bind_method(D_METHOD("add_string", "text", "font", "font_size", "language", "meta"), &TextLine::add_string, DEFVAL(""), DEFVAL(Variant())); ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextLine::add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1)); ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextLine::resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER)); @@ -149,8 +149,6 @@ RID TextLine::get_rid() const { void TextLine::clear() { TS->shaped_text_clear(rid); - spacing_top = 0; - spacing_bottom = 0; } void TextLine::set_preserve_invalid(bool p_enabled) { @@ -194,11 +192,12 @@ void TextLine::set_bidi_override(const Array &p_override) { dirty = true; } -bool TextLine::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) { - ERR_FAIL_COND_V(p_fonts.is_null(), false); - bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language, p_meta); - spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP); - spacing_bottom = p_fonts->get_spacing(TextServer::SPACING_BOTTOM); +bool TextLine::add_string(const String &p_text, const Ref<Font> &p_font, int p_font_size, const String &p_language, const Variant &p_meta) { + ERR_FAIL_COND_V(p_font.is_null(), false); + bool res = TS->shaped_text_add_string(rid, p_text, p_font->get_rids(), p_font_size, p_font->get_opentype_features(), p_language, p_meta); + for (int i = 0; i < TextServer::SPACING_MAX; i++) { + TS->shaped_text_set_spacing(rid, TextServer::SpacingType(i), p_font->get_spacing(TextServer::SpacingType(i))); + } dirty = true; return res; } @@ -278,20 +277,20 @@ float TextLine::get_width() const { Size2 TextLine::get_size() const { const_cast<TextLine *>(this)->_shape(); if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { - return Size2(TS->shaped_text_get_size(rid).x, TS->shaped_text_get_size(rid).y + spacing_top + spacing_bottom); + return Size2(TS->shaped_text_get_size(rid).x, TS->shaped_text_get_size(rid).y); } else { - return Size2(TS->shaped_text_get_size(rid).x + spacing_top + spacing_bottom, TS->shaped_text_get_size(rid).y); + return Size2(TS->shaped_text_get_size(rid).x, TS->shaped_text_get_size(rid).y); } } float TextLine::get_line_ascent() const { const_cast<TextLine *>(this)->_shape(); - return TS->shaped_text_get_ascent(rid) + spacing_top; + return TS->shaped_text_get_ascent(rid); } float TextLine::get_line_descent() const { const_cast<TextLine *>(this)->_shape(); - return TS->shaped_text_get_descent(rid) + spacing_bottom; + return TS->shaped_text_get_descent(rid); } float TextLine::get_line_width() const { @@ -347,10 +346,10 @@ void TextLine::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_color) co float clip_l; if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { - ofs.y += TS->shaped_text_get_ascent(rid) + spacing_top; + ofs.y += TS->shaped_text_get_ascent(rid); clip_l = MAX(0, p_pos.x - ofs.x); } else { - ofs.x += TS->shaped_text_get_ascent(rid) + spacing_top; + ofs.x += TS->shaped_text_get_ascent(rid); clip_l = MAX(0, p_pos.y - ofs.y); } return TS->shaped_text_draw(rid, p_canvas, ofs, clip_l, clip_l + width, p_color); @@ -394,10 +393,10 @@ void TextLine::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_si float clip_l; if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { - ofs.y += TS->shaped_text_get_ascent(rid) + spacing_top; + ofs.y += TS->shaped_text_get_ascent(rid); clip_l = MAX(0, p_pos.x - ofs.x); } else { - ofs.x += TS->shaped_text_get_ascent(rid) + spacing_top; + ofs.x += TS->shaped_text_get_ascent(rid); clip_l = MAX(0, p_pos.y - ofs.y); } return TS->shaped_text_draw_outline(rid, p_canvas, ofs, clip_l, clip_l + width, p_outline_size, p_color); @@ -409,11 +408,14 @@ int TextLine::hit_test(float p_coords) const { return TS->shaped_text_hit_test_position(rid, p_coords); } -TextLine::TextLine(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, TextServer::Direction p_direction, TextServer::Orientation p_orientation) { +TextLine::TextLine(const String &p_text, const Ref<Font> &p_font, int p_font_size, const String &p_language, TextServer::Direction p_direction, TextServer::Orientation p_orientation) { rid = TS->create_shaped_text(p_direction, p_orientation); - spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP); - spacing_bottom = p_fonts->get_spacing(TextServer::SPACING_BOTTOM); - TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language); + if (p_font.is_valid()) { + TS->shaped_text_add_string(rid, p_text, p_font->get_rids(), p_font_size, p_font->get_opentype_features(), p_language); + for (int i = 0; i < TextServer::SPACING_MAX; i++) { + TS->shaped_text_set_spacing(rid, TextServer::SpacingType(i), p_font->get_spacing(TextServer::SpacingType(i))); + } + } } TextLine::TextLine() { diff --git a/scene/resources/text_line.h b/scene/resources/text_line.h index 784ee8ef26..2d1548d079 100644 --- a/scene/resources/text_line.h +++ b/scene/resources/text_line.h @@ -41,8 +41,6 @@ class TextLine : public RefCounted { private: RID rid; - int spacing_top = 0; - int spacing_bottom = 0; bool dirty = true; @@ -77,7 +75,7 @@ public: void set_preserve_control(bool p_enabled); bool get_preserve_control() const; - bool add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant()); + bool add_string(const String &p_text, const Ref<Font> &p_font, int p_font_size, const String &p_language = "", const Variant &p_meta = Variant()); bool add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1); bool resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER); @@ -111,7 +109,7 @@ public: int hit_test(float p_coords) const; - TextLine(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL); + TextLine(const String &p_text, const Ref<Font> &p_font, int p_font_size, const String &p_language = "", TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL); TextLine(); ~TextLine(); }; diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp index 874992ea3d..c8b9e895fc 100644 --- a/scene/resources/text_paragraph.cpp +++ b/scene/resources/text_paragraph.cpp @@ -60,10 +60,10 @@ void TextParagraph::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextParagraph::set_bidi_override); - ClassDB::bind_method(D_METHOD("set_dropcap", "text", "fonts", "size", "dropcap_margins", "opentype_features", "language"), &TextParagraph::set_dropcap, DEFVAL(Rect2()), DEFVAL(Dictionary()), DEFVAL("")); + ClassDB::bind_method(D_METHOD("set_dropcap", "text", "font", "font_size", "dropcap_margins", "language"), &TextParagraph::set_dropcap, DEFVAL(Rect2()), DEFVAL("")); ClassDB::bind_method(D_METHOD("clear_dropcap"), &TextParagraph::clear_dropcap); - ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language", "meta"), &TextParagraph::add_string, DEFVAL(Dictionary()), DEFVAL(""), DEFVAL(Variant())); + ClassDB::bind_method(D_METHOD("add_string", "text", "font", "font_size", "language", "meta"), &TextParagraph::add_string, DEFVAL(""), DEFVAL(Variant())); ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextParagraph::add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1)); ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextParagraph::resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER)); @@ -113,9 +113,6 @@ void TextParagraph::_bind_methods() { ClassDB::bind_method(D_METHOD("get_line_underline_position", "line"), &TextParagraph::get_line_underline_position); ClassDB::bind_method(D_METHOD("get_line_underline_thickness", "line"), &TextParagraph::get_line_underline_thickness); - ClassDB::bind_method(D_METHOD("get_spacing_top"), &TextParagraph::get_spacing_top); - ClassDB::bind_method(D_METHOD("get_spacing_bottom"), &TextParagraph::get_spacing_bottom); - ClassDB::bind_method(D_METHOD("get_dropcap_size"), &TextParagraph::get_dropcap_size); ClassDB::bind_method(D_METHOD("get_dropcap_lines"), &TextParagraph::get_dropcap_lines); @@ -266,8 +263,6 @@ RID TextParagraph::get_dropcap_rid() const { void TextParagraph::clear() { _THREAD_SAFE_METHOD_ - spacing_top = 0; - spacing_bottom = 0; for (int i = 0; i < (int)lines_rid.size(); i++) { TS->free_rid(lines_rid[i]); } @@ -347,44 +342,37 @@ TextServer::Orientation TextParagraph::get_orientation() const { return TS->shaped_text_get_orientation(rid); } -bool TextParagraph::set_dropcap(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Rect2 &p_dropcap_margins, const Dictionary &p_opentype_features, const String &p_language) { +bool TextParagraph::set_dropcap(const String &p_text, const Ref<Font> &p_font, int p_font_size, const Rect2 &p_dropcap_margins, const String &p_language) { _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND_V(p_fonts.is_null(), false); + ERR_FAIL_COND_V(p_font.is_null(), false); TS->shaped_text_clear(dropcap_rid); dropcap_margins = p_dropcap_margins; - bool res = TS->shaped_text_add_string(dropcap_rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language); + bool res = TS->shaped_text_add_string(dropcap_rid, p_text, p_font->get_rids(), p_font_size, p_font->get_opentype_features(), p_language); + for (int i = 0; i < TextServer::SPACING_MAX; i++) { + TS->shaped_text_set_spacing(dropcap_rid, TextServer::SpacingType(i), p_font->get_spacing(TextServer::SpacingType(i))); + } lines_dirty = true; return res; } void TextParagraph::clear_dropcap() { _THREAD_SAFE_METHOD_ - dropcap_margins = Rect2(); TS->shaped_text_clear(dropcap_rid); lines_dirty = true; } -bool TextParagraph::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) { +bool TextParagraph::add_string(const String &p_text, const Ref<Font> &p_font, int p_font_size, const String &p_language, const Variant &p_meta) { _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND_V(p_fonts.is_null(), false); - bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language, p_meta); - spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP); - spacing_bottom = p_fonts->get_spacing(TextServer::SPACING_BOTTOM); + ERR_FAIL_COND_V(p_font.is_null(), false); + bool res = TS->shaped_text_add_string(rid, p_text, p_font->get_rids(), p_font_size, p_font->get_opentype_features(), p_language, p_meta); + for (int i = 0; i < TextServer::SPACING_MAX; i++) { + TS->shaped_text_set_spacing(rid, TextServer::SpacingType(i), p_font->get_spacing(TextServer::SpacingType(i))); + } lines_dirty = true; return res; } -int TextParagraph::get_spacing_top() const { - return spacing_top; -} - -int TextParagraph::get_spacing_bottom() const { - return spacing_bottom; -} - void TextParagraph::set_bidi_override(const Array &p_override) { _THREAD_SAFE_METHOD_ @@ -476,9 +464,9 @@ Size2 TextParagraph::get_non_wrapped_size() const { const_cast<TextParagraph *>(this)->_shape_lines(); if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) { - return Size2(TS->shaped_text_get_size(rid).x, TS->shaped_text_get_size(rid).y + spacing_top + spacing_bottom); + return Size2(TS->shaped_text_get_size(rid).x, TS->shaped_text_get_size(rid).y); } else { - return Size2(TS->shaped_text_get_size(rid).x + spacing_top + spacing_bottom, TS->shaped_text_get_size(rid).y); + return Size2(TS->shaped_text_get_size(rid).x, TS->shaped_text_get_size(rid).y); } } @@ -492,9 +480,9 @@ Size2 TextParagraph::get_size() const { Size2 lsize = TS->shaped_text_get_size(lines_rid[i]); if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { size.x = MAX(size.x, lsize.x); - size.y += lsize.y + spacing_top + spacing_bottom; + size.y += lsize.y; } else { - size.x += lsize.x + spacing_top + spacing_bottom; + size.x += lsize.x; size.y = MAX(size.y, lsize.y); } } @@ -538,9 +526,9 @@ Rect2 TextParagraph::get_line_object_rect(int p_line, Variant p_key) const { for (int i = 0; i < p_line; i++) { Size2 lsize = TS->shaped_text_get_size(lines_rid[i]); if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { - xrect.position.y += lsize.y + spacing_top + spacing_bottom; + xrect.position.y += lsize.y; } else { - xrect.position.x += lsize.x + spacing_top + spacing_bottom; + xrect.position.x += lsize.x; } } return xrect; @@ -552,9 +540,9 @@ Size2 TextParagraph::get_line_size(int p_line) const { const_cast<TextParagraph *>(this)->_shape_lines(); ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), Size2()); if (TS->shaped_text_get_orientation(lines_rid[p_line]) == TextServer::ORIENTATION_HORIZONTAL) { - return Size2(TS->shaped_text_get_size(lines_rid[p_line]).x, TS->shaped_text_get_size(lines_rid[p_line]).y + spacing_top + spacing_bottom); + return Size2(TS->shaped_text_get_size(lines_rid[p_line]).x, TS->shaped_text_get_size(lines_rid[p_line]).y); } else { - return Size2(TS->shaped_text_get_size(lines_rid[p_line]).x + spacing_top + spacing_bottom, TS->shaped_text_get_size(lines_rid[p_line]).y); + return Size2(TS->shaped_text_get_size(lines_rid[p_line]).x, TS->shaped_text_get_size(lines_rid[p_line]).y); } } @@ -571,7 +559,7 @@ float TextParagraph::get_line_ascent(int p_line) const { const_cast<TextParagraph *>(this)->_shape_lines(); ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), 0.f); - return TS->shaped_text_get_ascent(lines_rid[p_line]) + spacing_top; + return TS->shaped_text_get_ascent(lines_rid[p_line]); } float TextParagraph::get_line_descent(int p_line) const { @@ -579,7 +567,7 @@ float TextParagraph::get_line_descent(int p_line) const { const_cast<TextParagraph *>(this)->_shape_lines(); ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), 0.f); - return TS->shaped_text_get_descent(lines_rid[p_line]) + spacing_bottom; + return TS->shaped_text_get_descent(lines_rid[p_line]); } float TextParagraph::get_line_width(int p_line) const { @@ -647,7 +635,7 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo float l_width = width; if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { ofs.x = p_pos.x; - ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + spacing_top; + ofs.y += TS->shaped_text_get_ascent(lines_rid[i]); if (i <= dropcap_lines) { if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_LTR) { ofs.x -= h_offset; @@ -656,7 +644,7 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo } } else { ofs.y = p_pos.y; - ofs.x += TS->shaped_text_get_ascent(lines_rid[i]) + spacing_top; + ofs.x += TS->shaped_text_get_ascent(lines_rid[i]); if (i <= dropcap_lines) { if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_LTR) { ofs.x -= h_offset; @@ -711,10 +699,10 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo TS->shaped_text_draw(lines_rid[i], p_canvas, ofs, clip_l, clip_l + l_width, p_color); if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { ofs.x = p_pos.x; - ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + spacing_bottom; + ofs.y += TS->shaped_text_get_descent(lines_rid[i]); } else { ofs.y = p_pos.y; - ofs.x += TS->shaped_text_get_descent(lines_rid[i]) + spacing_bottom; + ofs.x += TS->shaped_text_get_descent(lines_rid[i]); } } } @@ -749,7 +737,7 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli float l_width = width; if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { ofs.x = p_pos.x; - ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + spacing_top; + ofs.y += TS->shaped_text_get_ascent(lines_rid[i]); if (i <= dropcap_lines) { if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_LTR) { ofs.x -= h_offset; @@ -758,7 +746,7 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli } } else { ofs.y = p_pos.y; - ofs.x += TS->shaped_text_get_ascent(lines_rid[i]) + spacing_top; + ofs.x += TS->shaped_text_get_ascent(lines_rid[i]); if (i <= dropcap_lines) { if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_LTR) { ofs.x -= h_offset; @@ -813,10 +801,10 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli TS->shaped_text_draw_outline(lines_rid[i], p_canvas, ofs, clip_l, clip_l + l_width, p_outline_size, p_color); if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) { ofs.x = p_pos.x; - ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + spacing_bottom; + ofs.y += TS->shaped_text_get_descent(lines_rid[i]); } else { ofs.y = p_pos.y; - ofs.x += TS->shaped_text_get_descent(lines_rid[i]) + spacing_bottom; + ofs.x += TS->shaped_text_get_descent(lines_rid[i]); } } } @@ -840,12 +828,12 @@ int TextParagraph::hit_test(const Point2 &p_coords) const { if ((p_coords.y >= ofs.y) && (p_coords.y <= ofs.y + TS->shaped_text_get_size(lines_rid[i]).y)) { return TS->shaped_text_hit_test_position(lines_rid[i], p_coords.x); } - ofs.y += TS->shaped_text_get_size(lines_rid[i]).y + spacing_bottom + spacing_top; + ofs.y += TS->shaped_text_get_size(lines_rid[i]).y; } else { if ((p_coords.x >= ofs.x) && (p_coords.x <= ofs.x + TS->shaped_text_get_size(lines_rid[i]).x)) { return TS->shaped_text_hit_test_position(lines_rid[i], p_coords.y); } - ofs.y += TS->shaped_text_get_size(lines_rid[i]).x + spacing_bottom + spacing_top; + ofs.y += TS->shaped_text_get_size(lines_rid[i]).x; } } return TS->shaped_text_get_range(rid).y; @@ -908,9 +896,9 @@ void TextParagraph::draw_line(RID p_canvas, const Vector2 &p_pos, int p_line, co Vector2 ofs = p_pos; if (TS->shaped_text_get_orientation(lines_rid[p_line]) == TextServer::ORIENTATION_HORIZONTAL) { - ofs.y += TS->shaped_text_get_ascent(lines_rid[p_line]) + spacing_top; + ofs.y += TS->shaped_text_get_ascent(lines_rid[p_line]); } else { - ofs.x += TS->shaped_text_get_ascent(lines_rid[p_line]) + spacing_top; + ofs.x += TS->shaped_text_get_ascent(lines_rid[p_line]); } return TS->shaped_text_draw(lines_rid[p_line], p_canvas, ofs, -1, -1, p_color); } @@ -923,18 +911,21 @@ void TextParagraph::draw_line_outline(RID p_canvas, const Vector2 &p_pos, int p_ Vector2 ofs = p_pos; if (TS->shaped_text_get_orientation(lines_rid[p_line]) == TextServer::ORIENTATION_HORIZONTAL) { - ofs.y += TS->shaped_text_get_ascent(lines_rid[p_line]) + spacing_top; + ofs.y += TS->shaped_text_get_ascent(lines_rid[p_line]); } else { - ofs.x += TS->shaped_text_get_ascent(lines_rid[p_line]) + spacing_top; + ofs.x += TS->shaped_text_get_ascent(lines_rid[p_line]); } return TS->shaped_text_draw_outline(lines_rid[p_line], p_canvas, ofs, -1, -1, p_outline_size, p_color); } -TextParagraph::TextParagraph(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, float p_width, TextServer::Direction p_direction, TextServer::Orientation p_orientation) { +TextParagraph::TextParagraph(const String &p_text, const Ref<Font> &p_font, int p_font_size, const String &p_language, float p_width, TextServer::Direction p_direction, TextServer::Orientation p_orientation) { rid = TS->create_shaped_text(p_direction, p_orientation); - TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language); - spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP); - spacing_bottom = p_fonts->get_spacing(TextServer::SPACING_BOTTOM); + if (p_font.is_valid()) { + TS->shaped_text_add_string(rid, p_text, p_font->get_rids(), p_font_size, p_font->get_opentype_features(), p_language); + for (int i = 0; i < TextServer::SPACING_MAX; i++) { + TS->shaped_text_set_spacing(rid, TextServer::SpacingType(i), p_font->get_spacing(TextServer::SpacingType(i))); + } + } width = p_width; } diff --git a/scene/resources/text_paragraph.h b/scene/resources/text_paragraph.h index bdcc2b5701..f161cb5b8c 100644 --- a/scene/resources/text_paragraph.h +++ b/scene/resources/text_paragraph.h @@ -48,8 +48,6 @@ private: RID rid; LocalVector<RID> lines_rid; - int spacing_top = 0; - int spacing_bottom = 0; bool lines_dirty = true; @@ -92,10 +90,10 @@ public: void set_custom_punctuation(const String &p_punct); String get_custom_punctuation() const; - bool set_dropcap(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Rect2 &p_dropcap_margins = Rect2(), const Dictionary &p_opentype_features = Dictionary(), const String &p_language = ""); + bool set_dropcap(const String &p_text, const Ref<Font> &p_font, int p_font_size, const Rect2 &p_dropcap_margins = Rect2(), const String &p_language = ""); void clear_dropcap(); - bool add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant()); + bool add_string(const String &p_text, const Ref<Font> &p_font, int p_font_size, const String &p_language = "", const Variant &p_meta = Variant()); bool add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1); bool resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER); @@ -151,7 +149,7 @@ public: Mutex &get_mutex() const { return _thread_safe_; }; - TextParagraph(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", float p_width = -1.f, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL); + TextParagraph(const String &p_text, const Ref<Font> &p_font, int p_font_size, const String &p_language = "", float p_width = -1.f, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL); TextParagraph(); ~TextParagraph(); }; |