diff options
Diffstat (limited to 'scene')
56 files changed, 2757 insertions, 2749 deletions
diff --git a/scene/2d/animated_sprite_2d.cpp b/scene/2d/animated_sprite_2d.cpp index 221d52bc20..d56c7b8811 100644 --- a/scene/2d/animated_sprite_2d.cpp +++ b/scene/2d/animated_sprite_2d.cpp @@ -138,8 +138,11 @@ void AnimatedSprite2D::_validate_property(PropertyInfo &property) const { if (property.name == "frame") { property.hint = PROPERTY_HINT_RANGE; - if (frames->has_animation(animation) && frames->get_frame_count(animation) > 1) { + if (frames->has_animation(animation) && frames->get_frame_count(animation) > 0) { property.hint_string = "0," + itos(frames->get_frame_count(animation) - 1) + ",1"; + } else { + // Avoid an error, `hint_string` is required for `PROPERTY_HINT_RANGE`. + property.hint_string = "0,0,1"; } property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS; } diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp index 913003c7e6..4155d0797f 100644 --- a/scene/2d/cpu_particles_2d.cpp +++ b/scene/2d/cpu_particles_2d.cpp @@ -243,7 +243,7 @@ bool CPUParticles2D::get_fractional_delta() const { } TypedArray<String> CPUParticles2D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); + TypedArray<String> warnings = Node2D::get_configuration_warnings(); CanvasItemMaterial *mat = Object::cast_to<CanvasItemMaterial>(get_material().ptr()); @@ -314,6 +314,8 @@ void CPUParticles2D::set_param_max(Parameter p_param, real_t p_value) { if (parameters_min[p_param] > parameters_max[p_param]) { set_param_min(p_param, p_value); } + + update_configuration_warnings(); } real_t CPUParticles2D::get_param_max(Parameter p_param) const { @@ -374,6 +376,8 @@ void CPUParticles2D::set_param_curve(Parameter p_param, const Ref<Curve> &p_curv default: { } } + + update_configuration_warnings(); } Ref<Curve> CPUParticles2D::get_param_curve(Parameter p_param) const { diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp index 6edd75e62a..a869cf2525 100644 --- a/scene/2d/gpu_particles_2d.cpp +++ b/scene/2d/gpu_particles_2d.cpp @@ -141,7 +141,6 @@ void GPUParticles2D::set_process_material(const Ref<Material> &p_material) { void GPUParticles2D::set_trail_enabled(bool p_enabled) { trail_enabled = p_enabled; RS::get_singleton()->particles_set_trails(particles, trail_enabled, trail_length); - update_configuration_warnings(); update(); RS::get_singleton()->particles_set_transform_align(particles, p_enabled ? RS::PARTICLES_TRANSFORM_ALIGN_Y_TO_VELOCITY : RS::PARTICLES_TRANSFORM_ALIGN_DISABLED); @@ -297,7 +296,7 @@ bool GPUParticles2D::get_interpolate() const { } TypedArray<String> GPUParticles2D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); + TypedArray<String> warnings = Node2D::get_configuration_warnings(); if (RenderingServer::get_singleton()->is_low_end()) { warnings.push_back(RTR("GPU-based particles are not supported by the OpenGL video driver.\nUse the CPUParticles2D node instead. You can use the \"Convert to CPUParticles2D\" option for this purpose.")); diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp index 8585f3bdfc..4df0c37ad6 100644 --- a/scene/3d/cpu_particles_3d.cpp +++ b/scene/3d/cpu_particles_3d.cpp @@ -164,6 +164,8 @@ void CPUParticles3D::set_mesh(const Ref<Mesh> &p_mesh) { } else { RS::get_singleton()->multimesh_set_mesh(multimesh, RID()); } + + update_configuration_warnings(); } Ref<Mesh> CPUParticles3D::get_mesh() const { @@ -187,7 +189,7 @@ bool CPUParticles3D::get_fractional_delta() const { } TypedArray<String> CPUParticles3D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); + TypedArray<String> warnings = GeometryInstance3D::get_configuration_warnings(); bool mesh_found = false; bool anim_material_found = false; @@ -266,6 +268,8 @@ void CPUParticles3D::set_param_min(Parameter p_param, real_t p_value) { if (parameters_min[p_param] > parameters_max[p_param]) { set_param_max(p_param, p_value); } + + update_configuration_warnings(); } real_t CPUParticles3D::get_param_min(Parameter p_param) const { @@ -276,10 +280,13 @@ real_t CPUParticles3D::get_param_min(Parameter p_param) const { void CPUParticles3D::set_param_max(Parameter p_param, real_t p_value) { ERR_FAIL_INDEX(p_param, PARAM_MAX); + parameters_max[p_param] = p_value; if (parameters_min[p_param] > parameters_max[p_param]) { set_param_min(p_param, p_value); } + + update_configuration_warnings(); } real_t CPUParticles3D::get_param_max(Parameter p_param) const { @@ -340,6 +347,8 @@ void CPUParticles3D::set_param_curve(Parameter p_param, const Ref<Curve> &p_curv default: { } } + + update_configuration_warnings(); } Ref<Curve> CPUParticles3D::get_param_curve(Parameter p_param) const { diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index d18c452ca7..b352114c7f 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -270,7 +270,7 @@ bool GPUParticles3D::get_interpolate() const { } TypedArray<String> GPUParticles3D::get_configuration_warnings() const { - TypedArray<String> warnings = Node::get_configuration_warnings(); + TypedArray<String> warnings = GeometryInstance3D::get_configuration_warnings(); if (RenderingServer::get_singleton()->is_low_end()) { warnings.push_back(RTR("GPU-based particles are not supported by the OpenGL video driver.\nUse the CPUParticles3D node instead. You can use the \"Convert to CPUParticles3D\" option for this purpose.")); diff --git a/scene/3d/label_3d.cpp b/scene/3d/label_3d.cpp index 0849b2c631..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])); } @@ -370,15 +316,8 @@ void Label3D::_generate_glyph_surfaces(const Glyph &p_glyph, Vector2 &r_offset, bool msdf = TS->font_is_multichannel_signed_distance_field(p_glyph.font_rid); - uint64_t mat_hash; - if (tex != RID()) { - mat_hash = hash_one_uint64(tex.get_id()); - } else { - mat_hash = hash_one_uint64(0); - } - mat_hash = hash_fmix32(hash_murmur3_one_64(p_priority | (p_outline_size << 31), mat_hash)); - - if (!surfaces.has(mat_hash)) { + SurfaceKey key = SurfaceKey(tex.get_id(), p_priority, p_outline_size); + if (!surfaces.has(key)) { SurfaceData surf; surf.material = RenderingServer::get_singleton()->material_create(); // Set defaults for material, names need to match up those in StandardMaterial3D @@ -407,9 +346,9 @@ void Label3D::_generate_glyph_surfaces(const Glyph &p_glyph, Vector2 &r_offset, surf.z_shift = p_priority * pixel_size; } - surfaces[mat_hash] = surf; + surfaces[key] = surf; } - SurfaceData &s = surfaces[mat_hash]; + SurfaceData &s = surfaces[key]; s.mesh_vertices.resize((s.offset + 1) * 4); s.mesh_normals.resize((s.offset + 1) * 4); @@ -464,7 +403,7 @@ void Label3D::_shape() { aabb = AABB(); // Clear materials. - for (const KeyValue<uint64_t, SurfaceData> &E : surfaces) { + for (const KeyValue<SurfaceKey, SurfaceData> &E : surfaces) { RenderingServer::get_singleton()->free(E.value.material); } surfaces.clear(); @@ -478,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) { @@ -494,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; @@ -541,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; @@ -577,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. @@ -591,10 +536,10 @@ 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<uint64_t, SurfaceData> &E : surfaces) { + for (const KeyValue<SurfaceKey, SurfaceData> &E : surfaces) { Array mesh_array; mesh_array.resize(RS::ARRAY_MAX); mesh_array[RS::ARRAY_VERTEX] = E.value.mesh_vertices; @@ -664,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; @@ -788,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; } @@ -1018,7 +940,7 @@ Label3D::~Label3D() { TS->free_rid(text_rid); RenderingServer::get_singleton()->free(mesh); - for (KeyValue<uint64_t, SurfaceData> E : surfaces) { + for (KeyValue<SurfaceKey, SurfaceData> E : surfaces) { RenderingServer::get_singleton()->free(E.value.material); } surfaces.clear(); diff --git a/scene/3d/label_3d.h b/scene/3d/label_3d.h index 7766bca068..4498e89517 100644 --- a/scene/3d/label_3d.h +++ b/scene/3d/label_3d.h @@ -76,7 +76,29 @@ private: RID material; }; - HashMap<uint64_t, SurfaceData> surfaces; + struct SurfaceKey { + uint64_t texture_id; + int32_t priority; + int32_t outline_size; + + bool operator==(const SurfaceKey &p_b) const { + return (texture_id == p_b.texture_id) && (priority == p_b.priority) && (outline_size == p_b.outline_size); + } + + SurfaceKey(uint64_t p_texture_id, int p_priority, int p_outline_size) { + texture_id = p_texture_id; + priority = p_priority; + outline_size = p_outline_size; + } + }; + + struct SurfaceKeyHasher { + _FORCE_INLINE_ static uint32_t hash(const SurfaceKey &p_a) { + return hash_murmur3_buffer(&p_a, sizeof(SurfaceKey)); + } + }; + + HashMap<SurfaceKey, SurfaceData, SurfaceKeyHasher> surfaces; HorizontalAlignment horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER; VerticalAlignment vertical_alignment = VERTICAL_ALIGNMENT_CENTER; @@ -100,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; @@ -128,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(); @@ -158,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/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index 55b55d924c..cb6354f7a8 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -1034,8 +1034,11 @@ void AnimatedSprite3D::_validate_property(PropertyInfo &property) const { if (property.name == "frame") { property.hint = PROPERTY_HINT_RANGE; - if (frames->has_animation(animation) && frames->get_frame_count(animation) > 1) { + if (frames->has_animation(animation) && frames->get_frame_count(animation) > 0) { property.hint_string = "0," + itos(frames->get_frame_count(animation) - 1) + ",1"; + } else { + // Avoid an error, `hint_string` is required for `PROPERTY_HINT_RANGE`. + property.hint_string = "0,0,1"; } property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS; } diff --git a/scene/3d/voxel_gi.cpp b/scene/3d/voxel_gi.cpp index d81b59b3fc..ae231026a7 100644 --- a/scene/3d/voxel_gi.cpp +++ b/scene/3d/voxel_gi.cpp @@ -272,7 +272,8 @@ VoxelGI::Subdiv VoxelGI::get_subdiv() const { } void VoxelGI::set_extents(const Vector3 &p_extents) { - extents = p_extents; + // Prevent very small extents as these break baking if other extents are set very high. + extents = Vector3(MAX(1.0, p_extents.x), MAX(1.0, p_extents.y), MAX(1.0, p_extents.z)); update_gizmos(); } diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index 1371c9cd57..d8de22d27c 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -347,7 +347,6 @@ void Button::_notification(int p_what) { if (outline_size > 0 && font_outline_color.a > 0) { text_buf->draw_outline(ci, text_ofs, outline_size, font_outline_color); } - text_buf->draw(ci, text_ofs, color); } break; } @@ -363,7 +362,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 +408,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 +488,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 +495,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 +511,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 +518,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 22e9763929..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")); @@ -68,6 +68,7 @@ void CodeEdit::_notification(int p_what) { code_completion_max_lines = get_theme_constant(SNAME("completion_lines")); code_completion_scroll_width = get_theme_constant(SNAME("completion_scroll_width")); code_completion_scroll_color = get_theme_color(SNAME("completion_scroll_color")); + code_completion_scroll_hovered_color = get_theme_color(SNAME("completion_scroll_hovered_color")); code_completion_background_color = get_theme_color(SNAME("completion_background_color")); code_completion_selected_color = get_theme_color(SNAME("completion_selected_color")); code_completion_existing_color = get_theme_color(SNAME("completion_existing_color")); @@ -85,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(); @@ -122,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 { @@ -134,6 +135,9 @@ void CodeEdit::_notification(int p_what) { RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(code_completion_rect.position, code_completion_rect.size + Size2(scroll_width, 0)), code_completion_background_color); } + code_completion_scroll_rect.position = code_completion_rect.position + Vector2(code_completion_rect.size.width, 0); + code_completion_scroll_rect.size = Vector2(scroll_width, code_completion_rect.size.height); + code_completion_line_ofs = CLAMP(code_completion_current_selected - lines / 2, 0, code_completion_options_count - lines); RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(code_completion_rect.position.x, code_completion_rect.position.y + (code_completion_current_selected - code_completion_line_ofs) * row_height), Size2(code_completion_rect.size.width, row_height)), code_completion_selected_color); @@ -174,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); } @@ -185,9 +189,11 @@ void CodeEdit::_notification(int p_what) { /* Draw a small scroll rectangle to show a position in the options. */ if (scroll_width) { + Color scroll_color = is_code_completion_scroll_hovered || is_code_completion_scroll_pressed ? code_completion_scroll_hovered_color : code_completion_scroll_color; + float r = (float)code_completion_max_lines / code_completion_options_count; float o = (float)code_completion_line_ofs / code_completion_options_count; - draw_rect(Rect2(code_completion_rect.position.x + code_completion_rect.size.width, code_completion_rect.position.y + o * code_completion_rect.size.y, scroll_width, code_completion_rect.size.y * r), code_completion_scroll_color); + draw_rect(Rect2(code_completion_rect.position.x + code_completion_rect.size.width, code_completion_rect.position.y + o * code_completion_rect.size.y, scroll_width, code_completion_rect.size.y * r), scroll_color); } } @@ -202,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; } @@ -226,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); @@ -260,6 +266,12 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { return; } + if (is_code_completion_scroll_pressed && mb->get_button_index() == MouseButton::LEFT) { + is_code_completion_scroll_pressed = false; + update(); + return; + } + if (code_completion_active && code_completion_rect.has_point(mb->get_position())) { if (!mb->is_pressed()) { return; @@ -289,7 +301,21 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { break; } return; + } else if (code_completion_active && code_completion_scroll_rect.has_point(mb->get_position())) { + if (mb->get_button_index() != MouseButton::LEFT) { + return; + } + + if (mb->is_pressed()) { + is_code_completion_scroll_pressed = true; + + _update_scroll_selected_line(mb->get_position().y); + update(); + } + + return; } + cancel_code_completion(); set_code_hint(""); @@ -354,6 +380,18 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { set_symbol_lookup_word_as_valid(false); } } + + bool scroll_hovered = code_completion_scroll_rect.has_point(mpos); + if (is_code_completion_scroll_hovered != scroll_hovered) { + is_code_completion_scroll_hovered = scroll_hovered; + update(); + } + + if (is_code_completion_scroll_pressed) { + _update_scroll_selected_line(mpos.y); + update(); + return; + } } Ref<InputEventKey> k = p_gui_input; @@ -546,6 +584,10 @@ Control::CursorShape CodeEdit::get_cursor_shape(const Point2 &p_pos) const { return CURSOR_ARROW; } + if (code_completion_active && code_completion_scroll_rect.has_point(p_pos)) { + return CURSOR_ARROW; + } + Point2i pos = get_line_column_at_pos(p_pos, false); int line = pos.y; int col = pos.x; @@ -2697,6 +2739,13 @@ TypedArray<String> CodeEdit::_get_delimiters(DelimiterType p_type) const { } /* Code Completion */ +void CodeEdit::_update_scroll_selected_line(float p_mouse_y) { + float percent = (float)(p_mouse_y - code_completion_scroll_rect.position.y) / code_completion_scroll_rect.size.height; + percent = CLAMP(percent, 0.0f, 1.0f); + + code_completion_current_selected = (int)(percent * (code_completion_options.size() - 1)); +} + void CodeEdit::_filter_code_completion_candidates_impl() { int line_height = get_line_height(); @@ -2746,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); } @@ -2857,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; } @@ -2963,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(); @@ -2980,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); } } @@ -3036,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/code_edit.h b/scene/gui/code_edit.h index ccf046c612..a431d8a5b2 100644 --- a/scene/gui/code_edit.h +++ b/scene/gui/code_edit.h @@ -203,22 +203,27 @@ private: int code_completion_max_lines = 7; int code_completion_scroll_width = 0; Color code_completion_scroll_color = Color(0, 0, 0, 0); + Color code_completion_scroll_hovered_color = Color(0, 0, 0, 0); Color code_completion_background_color = Color(0, 0, 0, 0); Color code_completion_selected_color = Color(0, 0, 0, 0); Color code_completion_existing_color = Color(0, 0, 0, 0); bool code_completion_active = false; + bool is_code_completion_scroll_hovered = false; + bool is_code_completion_scroll_pressed = false; Vector<ScriptLanguage::CodeCompletionOption> code_completion_options; int code_completion_line_ofs = 0; int code_completion_current_selected = 0; int code_completion_longest_line = 0; Rect2i code_completion_rect; + Rect2i code_completion_scroll_rect; HashSet<char32_t> code_completion_prefixes; List<ScriptLanguage::CodeCompletionOption> code_completion_option_submitted; List<ScriptLanguage::CodeCompletionOption> code_completion_option_sources; String code_completion_base; + void _update_scroll_selected_line(float p_mouse_y); void _filter_code_completion_candidates_impl(); /* Line length guidelines */ diff --git a/scene/gui/color_mode.cpp b/scene/gui/color_mode.cpp new file mode 100644 index 0000000000..af78d67e5a --- /dev/null +++ b/scene/gui/color_mode.cpp @@ -0,0 +1,330 @@ +/*************************************************************************/ +/* color_mode.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "color_mode.h" + +#include "core/math/color.h" +#include "scene/gui/slider.h" +#include "thirdparty/misc/ok_color.h" + +ColorMode::ColorMode(ColorPicker *p_color_picker) { + color_picker = p_color_picker; +} + +String ColorModeRGB::get_slider_label(int idx) const { + ERR_FAIL_INDEX_V_MSG(idx, 3, String(), "Couldn't get slider label."); + return labels[idx]; +} + +float ColorModeRGB::get_slider_max(int idx) const { + ERR_FAIL_INDEX_V_MSG(idx, 4, 0, "Couldn't get slider max value."); + Color color = color_picker->get_pick_color(); + return next_power_of_2(MAX(255, color.components[idx] * 255.0)) - 1; +} + +float ColorModeRGB::get_slider_value(int idx) const { + ERR_FAIL_INDEX_V_MSG(idx, 4, 0, "Couldn't get slider value."); + return color_picker->get_pick_color().components[idx] * 255; +} + +Color ColorModeRGB::get_color() const { + Vector<float> values = color_picker->get_active_slider_values(); + Color color; + for (int i = 0; i < 4; i++) { + color.components[i] = values[i] / 255.0; + } + return color; +} + +void ColorModeRGB::slider_draw(int p_which) { + Vector<Vector2> pos; + pos.resize(4); + Vector<Color> col; + col.resize(4); + HSlider *slider = color_picker->get_slider(p_which); + Size2 size = slider->get_size(); + Color left_color; + Color right_color; + Color color = color_picker->get_pick_color(); + const real_t margin = 4 * color_picker->get_theme_default_base_scale(); + + if (p_which == ColorPicker::SLIDER_COUNT) { + slider->draw_texture_rect(color_picker->get_theme_icon(SNAME("sample_bg"), SNAME("ColorPicker")), Rect2(Point2(0, margin), Size2(size.x, margin)), true); + + left_color = color; + left_color.a = 0; + right_color = color; + right_color.a = 1; + } else { + left_color = Color( + p_which == 0 ? 0 : color.r, + p_which == 1 ? 0 : color.g, + p_which == 2 ? 0 : color.b); + right_color = Color( + p_which == 0 ? 1 : color.r, + p_which == 1 ? 1 : color.g, + p_which == 2 ? 1 : color.b); + } + + col.set(0, left_color); + col.set(1, right_color); + col.set(2, right_color); + col.set(3, left_color); + pos.set(0, Vector2(0, margin)); + pos.set(1, Vector2(size.x, margin)); + pos.set(2, Vector2(size.x, margin * 2)); + pos.set(3, Vector2(0, margin * 2)); + + slider->draw_polygon(pos, col); +} + +String ColorModeHSV::get_slider_label(int idx) const { + ERR_FAIL_INDEX_V_MSG(idx, 3, String(), "Couldn't get slider label."); + return labels[idx]; +} + +float ColorModeHSV::get_slider_max(int idx) const { + ERR_FAIL_INDEX_V_MSG(idx, 4, 0, "Couldn't get slider max value."); + return slider_max[idx]; +} + +float ColorModeHSV::get_slider_value(int idx) const { + switch (idx) { + case 0: + return color_picker->get_pick_color().get_h() * 360.0; + case 1: + return color_picker->get_pick_color().get_s() * 100.0; + case 2: + return color_picker->get_pick_color().get_v() * 100.0; + case 3: + return Math::round(color_picker->get_pick_color().components[3] * 255.0); + default: + ERR_FAIL_V_MSG(0, "Couldn't get slider value."); + } +} + +Color ColorModeHSV::get_color() const { + Vector<float> values = color_picker->get_active_slider_values(); + Color color; + color.set_hsv(values[0] / 360.0, values[1] / 100.0, values[2] / 100.0, values[3] / 255.0); + return color; +} + +void ColorModeHSV::slider_draw(int p_which) { + Vector<Vector2> pos; + pos.resize(4); + Vector<Color> col; + col.resize(4); + HSlider *slider = color_picker->get_slider(p_which); + Size2 size = slider->get_size(); + Color left_color; + Color right_color; + Color color = color_picker->get_pick_color(); + const real_t margin = 4 * color_picker->get_theme_default_base_scale(); + + if (p_which == ColorPicker::SLIDER_COUNT) { + slider->draw_texture_rect(color_picker->get_theme_icon(SNAME("sample_bg"), SNAME("ColorPicker")), Rect2(Point2(0, margin), Size2(size.x, margin)), true); + + left_color = color; + left_color.a = 0; + right_color = color; + right_color.a = 1; + } else if (p_which == 0) { + Ref<Texture2D> hue = color_picker->get_theme_icon(SNAME("color_hue"), SNAME("ColorPicker")); + slider->draw_set_transform(Point2(), -Math_PI / 2, Size2(1.0, 1.0)); + slider->draw_texture_rect(hue, Rect2(Vector2(margin * -2, 0), Vector2(slider->get_size().x, margin)), false, Color(1, 1, 1), true); + return; + } else { + Color s_col; + Color v_col; + s_col.set_hsv(color.get_h(), 0, color.get_v()); + left_color = (p_which == 1) ? s_col : Color(0, 0, 0); + s_col.set_hsv(color.get_h(), 1, color.get_v()); + v_col.set_hsv(color.get_h(), color.get_s(), 1); + right_color = (p_which == 1) ? s_col : v_col; + } + col.set(0, left_color); + col.set(1, right_color); + col.set(2, right_color); + col.set(3, left_color); + pos.set(0, Vector2(0, margin)); + pos.set(1, Vector2(size.x, margin)); + pos.set(2, Vector2(size.x, margin * 2)); + pos.set(3, Vector2(0, margin * 2)); + + slider->draw_polygon(pos, col); +} + +String ColorModeRAW::get_slider_label(int idx) const { + ERR_FAIL_INDEX_V_MSG(idx, 3, String(), "Couldn't get slider label."); + return labels[idx]; +} + +float ColorModeRAW::get_slider_max(int idx) const { + ERR_FAIL_INDEX_V_MSG(idx, 4, 0, "Couldn't get slider max value."); + return slider_max[idx]; +} + +float ColorModeRAW::get_slider_value(int idx) const { + ERR_FAIL_INDEX_V_MSG(idx, 4, 0, "Couldn't get slider value."); + return color_picker->get_pick_color().components[idx]; +} + +Color ColorModeRAW::get_color() const { + Vector<float> values = color_picker->get_active_slider_values(); + Color color; + for (int i = 0; i < 4; i++) { + color.components[i] = values[i]; + } + return color; +} + +void ColorModeRAW::slider_draw(int p_which) { + Vector<Vector2> pos; + pos.resize(4); + Vector<Color> col; + col.resize(4); + HSlider *slider = color_picker->get_slider(p_which); + Size2 size = slider->get_size(); + Color left_color; + Color right_color; + Color color = color_picker->get_pick_color(); + const real_t margin = 4 * color_picker->get_theme_default_base_scale(); + + if (p_which == ColorPicker::SLIDER_COUNT) { + slider->draw_texture_rect(color_picker->get_theme_icon(SNAME("sample_bg"), SNAME("ColorPicker")), Rect2(Point2(0, margin), Size2(size.x, margin)), true); + + left_color = color; + left_color.a = 0; + right_color = color; + right_color.a = 1; + + col.set(0, left_color); + col.set(1, right_color); + col.set(2, right_color); + col.set(3, left_color); + pos.set(0, Vector2(0, margin)); + pos.set(1, Vector2(size.x, margin)); + pos.set(2, Vector2(size.x, margin * 2)); + pos.set(3, Vector2(0, margin * 2)); + + slider->draw_polygon(pos, col); + } +} + +bool ColorModeRAW::apply_theme() const { + for (int i = 0; i < 4; i++) { + HSlider *slider = color_picker->get_slider(i); + slider->remove_theme_icon_override("grabber"); + slider->remove_theme_icon_override("grabber_highlight"); + slider->remove_theme_style_override("slider"); + slider->remove_theme_style_override("grabber_area"); + slider->remove_theme_style_override("grabber_area_highlight"); + } + + return true; +} + +String ColorModeOKHSL::get_slider_label(int idx) const { + ERR_FAIL_INDEX_V_MSG(idx, 3, String(), "Couldn't get slider label."); + return labels[idx]; +} + +float ColorModeOKHSL::get_slider_max(int idx) const { + ERR_FAIL_INDEX_V_MSG(idx, 4, 0, "Couldn't get slider max value."); + return slider_max[idx]; +} + +float ColorModeOKHSL::get_slider_value(int idx) const { + switch (idx) { + case 0: + return color_picker->get_pick_color().get_ok_hsl_h() * 360.0; + case 1: + return color_picker->get_pick_color().get_ok_hsl_s() * 100.0; + case 2: + return color_picker->get_pick_color().get_ok_hsl_l() * 100.0; + case 3: + return Math::round(color_picker->get_pick_color().components[3] * 255.0); + default: + ERR_FAIL_V_MSG(0, "Couldn't get slider value."); + } +} + +Color ColorModeOKHSL::get_color() const { + Vector<float> values = color_picker->get_active_slider_values(); + Color color; + color.set_ok_hsl(values[0] / 360.0, values[1] / 100.0, values[2] / 100.0, values[3] / 255.0); + return color; +} + +void ColorModeOKHSL::slider_draw(int p_which) { + Vector<Vector2> pos; + pos.resize(4); + Vector<Color> col; + col.resize(4); + HSlider *slider = color_picker->get_slider(p_which); + Size2 size = slider->get_size(); + Color left_color; + Color right_color; + Color color = color_picker->get_pick_color(); + const real_t margin = 4 * color_picker->get_theme_default_base_scale(); + + if (p_which == ColorPicker::SLIDER_COUNT) { + slider->draw_texture_rect(color_picker->get_theme_icon(SNAME("sample_bg"), SNAME("ColorPicker")), Rect2(Point2(0, margin), Size2(size.x, margin)), true); + + left_color = color; + left_color.a = 0; + right_color = color; + right_color.a = 1; + } else if (p_which == 0) { + Ref<Texture2D> hue = color_picker->get_theme_icon(SNAME("color_hue"), SNAME("ColorPicker")); + slider->draw_set_transform(Point2(), -Math_PI / 2, Size2(1.0, 1.0)); + slider->draw_texture_rect(hue, Rect2(Vector2(margin * -2, 0), Vector2(slider->get_size().x, margin)), false, Color(1, 1, 1), true); + return; + } else { + Color s_col; + Color v_col; + s_col.set_ok_hsl(color.get_h(), 0, color.get_v()); + left_color = (p_which == 1) ? s_col : Color(0, 0, 0); + s_col.set_ok_hsl(color.get_h(), 1, color.get_v()); + v_col.set_ok_hsl(color.get_h(), color.get_s(), 1); + right_color = (p_which == 1) ? s_col : v_col; + } + col.set(0, left_color); + col.set(1, right_color); + col.set(2, right_color); + col.set(3, left_color); + pos.set(0, Vector2(0, margin)); + pos.set(1, Vector2(size.x, margin)); + pos.set(2, Vector2(size.x, margin * 2)); + pos.set(3, Vector2(0, margin * 2)); + + slider->draw_polygon(pos, col); +} diff --git a/scene/gui/color_mode.h b/scene/gui/color_mode.h new file mode 100644 index 0000000000..8a19699c40 --- /dev/null +++ b/scene/gui/color_mode.h @@ -0,0 +1,143 @@ +/*************************************************************************/ +/* color_mode.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef COLOR_MODE_H +#define COLOR_MODE_H + +#include "scene/gui/color_picker.h" + +struct Color; + +class ColorMode { +public: + ColorPicker *color_picker = nullptr; + + virtual String get_name() const = 0; + + virtual int get_slider_count() const { return 3; }; + virtual float get_slider_step() const = 0; + virtual String get_slider_label(int idx) const = 0; + virtual float get_slider_max(int idx) const = 0; + virtual float get_slider_value(int idx) const = 0; + + virtual Color get_color() const = 0; + + virtual void slider_draw(int p_which) = 0; + virtual bool apply_theme() const { return false; } + virtual ColorPicker::PickerShapeType get_shape_override() const { return ColorPicker::SHAPE_MAX; } + + ColorMode(ColorPicker *p_color_picker); + virtual ~ColorMode(){}; +}; + +class ColorModeHSV : public ColorMode { +public: + String labels[3] = { "H", "S", "V" }; + float slider_max[4] = { 359, 100, 100, 255 }; + + virtual String get_name() const override { return "HSV"; } + + virtual float get_slider_step() const override { return 1.0; } + virtual String get_slider_label(int idx) const override; + virtual float get_slider_max(int idx) const override; + virtual float get_slider_value(int idx) const override; + + virtual Color get_color() const override; + + virtual void slider_draw(int p_which) override; + + ColorModeHSV(ColorPicker *p_color_picker) : + ColorMode(p_color_picker){}; +}; + +class ColorModeRGB : public ColorMode { +public: + String labels[3] = { "R", "G", "B" }; + + virtual String get_name() const override { return "RGB"; } + + virtual float get_slider_step() const override { return 1; } + virtual String get_slider_label(int idx) const override; + virtual float get_slider_max(int idx) const override; + virtual float get_slider_value(int idx) const override; + + virtual Color get_color() const override; + + virtual void slider_draw(int p_which) override; + + ColorModeRGB(ColorPicker *p_color_picker) : + ColorMode(p_color_picker){}; +}; + +class ColorModeRAW : public ColorMode { +public: + String labels[3] = { "R", "G", "B" }; + float slider_max[4] = { 100, 100, 100, 1 }; + + virtual String get_name() const override { return "RAW"; } + + virtual float get_slider_step() const override { return 0.01; } + virtual String get_slider_label(int idx) const override; + virtual float get_slider_max(int idx) const override; + virtual float get_slider_value(int idx) const override; + + virtual Color get_color() const override; + + virtual void slider_draw(int p_which) override; + virtual bool apply_theme() const override; + + ColorModeRAW(ColorPicker *p_color_picker) : + ColorMode(p_color_picker){}; +}; + +class ColorModeOKHSL : public ColorMode { +public: + String labels[3] = { "H", "S", "L" }; + float slider_max[4] = { 359, 100, 100, 255 }; + + virtual String get_name() const override { return "OKHSL"; } + + virtual float get_slider_step() const override { return 1.0; } + virtual String get_slider_label(int idx) const override; + virtual float get_slider_max(int idx) const override; + virtual float get_slider_value(int idx) const override; + + virtual Color get_color() const override; + + virtual void slider_draw(int p_which) override; + virtual ColorPicker::PickerShapeType get_shape_override() const override { return ColorPicker::SHAPE_OKHSL_CIRCLE; } + + ColorModeOKHSL(ColorPicker *p_color_picker) : + ColorMode(p_color_picker){}; + + ~ColorModeOKHSL(){}; +}; + +#endif // COLOR_MODE_H diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 28d645e8f6..492a81f933 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -34,7 +34,7 @@ #include "core/math/color.h" #include "core/os/keyboard.h" #include "core/os/os.h" -#include "scene/main/window.h" +#include "scene/gui/color_mode.h" #ifdef TOOLS_ENABLED #include "editor/editor_settings.h" @@ -75,10 +75,14 @@ void ColorPicker::_notification(int p_what) { wheel_edit->set_custom_minimum_size(Size2(get_theme_constant(SNAME("sv_width")), get_theme_constant(SNAME("sv_height")))); wheel_margin->add_theme_constant_override("margin_bottom", 8 * get_theme_default_base_scale()); - for (int i = 0; i < 4; i++) { + for (int i = 0; i < SLIDER_COUNT; i++) { labels[i]->set_custom_minimum_size(Size2(get_theme_constant(SNAME("label_width")), 0)); set_offset((Side)i, get_offset((Side)i) + get_theme_constant(SNAME("margin"))); } + alpha_label->set_custom_minimum_size(Size2(get_theme_constant(SNAME("label_width")), 0)); + set_offset((Side)0, get_offset((Side)0) + get_theme_constant(SNAME("margin"))); + + _reset_theme(); if (Engine::get_singleton()->is_editor_hint()) { // Adjust for the width of the "Script" icon. @@ -194,75 +198,38 @@ void ColorPicker::set_focus_on_line_edit() { } void ColorPicker::_update_controls() { - const char *rgb[3] = { "R", "G", "B" }; - const char *hsv[3] = { "H", "S", "V" }; - const char *hsl[3] = { "H", "S", "L" }; - if (hsv_mode_enabled && picker_type == SHAPE_OKHSL_CIRCLE) { - for (int i = 0; i < 3; i++) { - labels[i]->set_text(hsl[i]); - } - } else if (hsv_mode_enabled && picker_type != SHAPE_OKHSL_CIRCLE) { - for (int i = 0; i < 3; i++) { - labels[i]->set_text(hsv[i]); - } - } else { - for (int i = 0; i < 3; i++) { - labels[i]->set_text(rgb[i]); - } + int mode_sliders_count = modes[current_mode]->get_slider_count(); + + for (int i = current_slider_count; i < mode_sliders_count; i++) { + sliders[i]->show(); + labels[i]->show(); + values[i]->show(); } - if (picker_type == SHAPE_OKHSL_CIRCLE) { - btn_hsv->set_text(RTR("OKHSL")); - } else { - btn_hsv->set_text(RTR("HSV")); - } - if (hsv_mode_enabled) { - set_raw_mode(false); - set_hsv_mode(true); - btn_raw->set_disabled(true); - } else if (raw_mode_enabled) { - set_raw_mode(true); - set_hsv_mode(false); - btn_raw->set_disabled(false); - btn_hsv->set_disabled(true); - } else { - set_raw_mode(false); - set_hsv_mode(false); - btn_raw->set_disabled(false); - btn_hsv->set_disabled(false); - } - - if (raw_mode_enabled) { - for (int i = 0; i < 3; i++) { - scroll[i]->remove_theme_icon_override("grabber"); - scroll[i]->remove_theme_icon_override("grabber_highlight"); - scroll[i]->remove_theme_style_override("slider"); - scroll[i]->remove_theme_style_override("grabber_area"); - scroll[i]->remove_theme_style_override("grabber_area_highlight"); - } - } else { - Ref<StyleBoxEmpty> style_box_empty(memnew(StyleBoxEmpty)); - Ref<Texture2D> bar_arrow = get_theme_icon(SNAME("bar_arrow")); - - for (int i = 0; i < 4; i++) { - scroll[i]->add_theme_icon_override("grabber", bar_arrow); - scroll[i]->add_theme_icon_override("grabber_highlight", bar_arrow); - scroll[i]->add_theme_style_override("slider", style_box_empty); - scroll[i]->add_theme_style_override("grabber_area", style_box_empty); - scroll[i]->add_theme_style_override("grabber_area_highlight", style_box_empty); - } + for (int i = mode_sliders_count; i < current_slider_count; i++) { + sliders[i]->hide(); + labels[i]->hide(); + values[i]->hide(); } + current_slider_count = mode_sliders_count; + + for (int i = 0; i < current_slider_count; i++) { + labels[i]->set_text(modes[current_mode]->get_slider_label(i)); + } + alpha_label->set_text("A"); + + slider_theme_modified = modes[current_mode]->apply_theme(); if (edit_alpha) { - values[3]->show(); - scroll[3]->show(); - labels[3]->show(); + alpha_value->show(); + alpha_slider->show(); + alpha_label->show(); } else { - values[3]->hide(); - scroll[3]->hide(); - labels[3]->hide(); + alpha_value->hide(); + alpha_slider->hide(); + alpha_label->hide(); } - switch (picker_type) { + switch (_get_actual_shape()) { case SHAPE_HSV_RECTANGLE: wheel_edit->hide(); w_edit->show(); @@ -297,7 +264,7 @@ void ColorPicker::_update_controls() { void ColorPicker::_set_pick_color(const Color &p_color, bool p_update_sliders) { color = p_color; if (color != last_color) { - if (picker_type == SHAPE_OKHSL_CIRCLE) { + if (_get_actual_shape() == SHAPE_OKHSL_CIRCLE) { h = color.get_ok_hsl_h(); s = color.get_ok_hsl_s(); v = color.get_ok_hsl_l(); @@ -353,27 +320,93 @@ void ColorPicker::_value_changed(double) { return; } - if (hsv_mode_enabled) { - h = scroll[0]->get_value() / 360.0; - s = scroll[1]->get_value() / 100.0; - v = scroll[2]->get_value() / 100.0; - if (picker_type == SHAPE_OKHSL_CIRCLE) { - color.set_ok_hsl(h, s, v, Math::round(scroll[3]->get_value() / 255.0)); - } else { - color.set_hsv(h, s, v, Math::round(scroll[3]->get_value() / 255.0)); - } + color = modes[current_mode]->get_color(); + if (current_mode == MODE_HSV || current_mode == MODE_OKHSL) { + h = sliders[0]->get_value() / 360.0; + s = sliders[1]->get_value() / 100.0; + v = sliders[2]->get_value() / 100.0; last_color = color; - } else { - for (int i = 0; i < 4; i++) { - color.components[i] = scroll[i]->get_value() / (raw_mode_enabled ? 1.0 : 255.0); - } } _set_pick_color(color, false); emit_signal(SNAME("color_changed"), color); } +void ColorPicker::add_mode(ColorMode *p_mode) { + modes.push_back(p_mode); + mode_option_button->add_item(RTR(p_mode->get_name())); +} + +void ColorPicker::create_slider(GridContainer *gc, int idx) { + Label *l = memnew(Label()); + l->set_v_size_flags(SIZE_SHRINK_CENTER); + gc->add_child(l); + + HSlider *s = memnew(HSlider); + s->set_v_size_flags(SIZE_SHRINK_CENTER); + s->set_focus_mode(FOCUS_NONE); + gc->add_child(s); + + SpinBox *v = memnew(SpinBox); + s->share(v); + gc->add_child(v); + v->get_line_edit()->connect("focus_entered", callable_mp(this, &ColorPicker::_focus_enter)); + v->get_line_edit()->connect("focus_exited", callable_mp(this, &ColorPicker::_focus_exit)); + + s->set_h_size_flags(SIZE_EXPAND_FILL); + + s->connect("value_changed", callable_mp(this, &ColorPicker::_value_changed)); + s->connect("draw", callable_mp(this, &ColorPicker::_slider_draw), make_binds(idx)); + + if (idx < SLIDER_COUNT) { + sliders[idx] = s; + values[idx] = v; + labels[idx] = l; + } else { + alpha_slider = s; + alpha_value = v; + alpha_label = l; + } +} + +HSlider *ColorPicker::get_slider(int idx) { + if (idx < SLIDER_COUNT) { + return sliders[idx]; + } + return alpha_slider; +} + +Vector<float> ColorPicker::get_active_slider_values() { + Vector<float> values; + for (int i = 0; i < current_slider_count; i++) { + values.push_back(sliders[i]->get_value()); + } + values.push_back(alpha_slider->get_value()); + return values; +} + +ColorPicker::PickerShapeType ColorPicker::_get_actual_shape() const { + return modes[current_mode]->get_shape_override() != SHAPE_MAX ? modes[current_mode]->get_shape_override() : current_shape; +} + +void ColorPicker::_reset_theme() { + Ref<StyleBoxEmpty> style_box_empty(memnew(StyleBoxEmpty)); + + for (int i = 0; i < SLIDER_COUNT; i++) { + sliders[i]->add_theme_icon_override("grabber", get_theme_icon(SNAME("bar_arrow"), SNAME("ColorPicker"))); + sliders[i]->add_theme_icon_override("grabber_highlight", get_theme_icon(SNAME("bar_arrow"), SNAME("ColorPicker"))); + sliders[i]->add_theme_style_override("slider", style_box_empty); + sliders[i]->add_theme_style_override("grabber_area", style_box_empty); + sliders[i]->add_theme_style_override("grabber_area_highlight", style_box_empty); + } + alpha_slider->add_theme_icon_override("grabber", get_theme_icon(SNAME("bar_arrow"), SNAME("ColorPicker"))); + alpha_slider->add_theme_icon_override("grabber_highlight", get_theme_icon(SNAME("bar_arrow"), SNAME("ColorPicker"))); + alpha_slider->add_theme_style_override("slider", style_box_empty); + alpha_slider->add_theme_style_override("grabber_area", style_box_empty); + alpha_slider->add_theme_style_override("grabber_area_highlight", style_box_empty); +} + void ColorPicker::_html_submitted(const String &p_html) { if (updating || text_is_constructor || !c_text->is_visible()) { return; @@ -397,35 +430,15 @@ void ColorPicker::_update_color(bool p_update_sliders) { updating = true; if (p_update_sliders) { - if (hsv_mode_enabled) { - for (int i = 0; i < 4; i++) { - scroll[i]->set_step(1.0); - } - scroll[0]->set_max(359); - scroll[0]->set_value(h * 360.0); - scroll[1]->set_max(100); - scroll[1]->set_value(s * 100.0); - scroll[2]->set_max(100); - scroll[2]->set_value(v * 100.0); - scroll[3]->set_max(255); - scroll[3]->set_value(Math::round(color.components[3] * 255.0)); - } else { - for (int i = 0; i < 4; i++) { - if (raw_mode_enabled) { - scroll[i]->set_step(0.01); - scroll[i]->set_max(100); - if (i == 3) { - scroll[i]->set_max(1); - } - scroll[i]->set_value(color.components[i]); - } else { - scroll[i]->set_step(1); - const float byte_value = Math::round(color.components[i] * 255.0); - scroll[i]->set_max(next_power_of_2(MAX(255, byte_value)) - 1); - scroll[i]->set_value(byte_value); - } - } + float step = modes[current_mode]->get_slider_step(); + for (int i = 0; i < current_slider_count; i++) { + sliders[i]->set_max(modes[current_mode]->get_slider_max(i)); + sliders[i]->set_step(step); + sliders[i]->set_value(modes[current_mode]->get_slider_value(i)); } + alpha_slider->set_max(modes[current_mode]->get_slider_max(current_slider_count)); + alpha_slider->set_step(step); + alpha_slider->set_value(modes[current_mode]->get_slider_value(current_slider_count)); } _update_text_value(); @@ -433,9 +446,10 @@ void ColorPicker::_update_color(bool p_update_sliders) { sample->update(); uv_edit->update(); w_edit->update(); - for (int i = 0; i < 4; i++) { - scroll[i]->update(); + for (int i = 0; i < current_slider_count; i++) { + sliders[i]->update(); } + alpha_slider->update(); wheel->update(); wheel_uv->update(); updating = false; @@ -481,15 +495,16 @@ Color ColorPicker::get_pick_color() const { return color; } -void ColorPicker::set_picker_shape(PickerShapeType p_picker_type) { - ERR_FAIL_INDEX(p_picker_type, SHAPE_MAX); - picker_type = p_picker_type; +void ColorPicker::set_picker_shape(PickerShapeType p_shape) { + ERR_FAIL_INDEX(p_shape, SHAPE_MAX); + current_shape = p_shape; + _update_controls(); _update_color(); } ColorPicker::PickerShapeType ColorPicker::get_picker_shape() const { - return picker_type; + return current_shape; } inline int ColorPicker::_get_preset_size() { @@ -505,6 +520,21 @@ void ColorPicker::_add_preset_button(int p_size, const Color &p_color) { preset_container->add_child(btn_preset); } +void ColorPicker::_set_color_mode(ColorModeType p_mode) { + if (slider_theme_modified) { + _reset_theme(); + } + + current_mode = p_mode; + + if (!is_inside_tree()) { + return; + } + + _update_controls(); + _update_color(); +} + void ColorPicker::add_preset(const Color &p_color) { if (presets.find(p_color)) { presets.move_to_back(presets.find(p_color)); @@ -564,46 +594,14 @@ PackedColorArray ColorPicker::get_presets() const { return arr; } -void ColorPicker::set_hsv_mode(bool p_enabled) { - if (hsv_mode_enabled == p_enabled || raw_mode_enabled) { - return; - } - hsv_mode_enabled = p_enabled; - if (btn_hsv->is_pressed() != p_enabled) { - btn_hsv->set_pressed(p_enabled); - } - - if (!is_inside_tree()) { - return; - } - - _update_controls(); - _update_color(); -} - -bool ColorPicker::is_hsv_mode() const { - return hsv_mode_enabled; -} - -void ColorPicker::set_raw_mode(bool p_enabled) { - if (raw_mode_enabled == p_enabled || hsv_mode_enabled) { - return; - } - raw_mode_enabled = p_enabled; - if (btn_raw->is_pressed() != p_enabled) { - btn_raw->set_pressed(p_enabled); - } - - if (!is_inside_tree()) { - return; - } - - _update_controls(); - _update_color(); +void ColorPicker::set_color_mode(ColorModeType p_mode) { + ERR_FAIL_INDEX(p_mode, MODE_MAX); + mode_option_button->select(p_mode); + _set_color_mode(p_mode); } -bool ColorPicker::is_raw_mode() const { - return raw_mode_enabled; +ColorPicker::ColorModeType ColorPicker::get_color_mode() const { + return current_mode; } void ColorPicker::set_deferred_mode(bool p_enabled) { @@ -690,6 +688,8 @@ void ColorPicker::_hsv_draw(int p_which, Control *c) { if (!c) { return; } + + PickerShapeType actual_shape = _get_actual_shape(); if (p_which == 0) { Vector<Point2> points; Vector<Color> colors; @@ -697,7 +697,7 @@ void ColorPicker::_hsv_draw(int p_which, Control *c) { Color col = color; Vector2 center = c->get_size() / 2.0; - switch (picker_type) { + switch (actual_shape) { case SHAPE_HSV_WHEEL: { points.resize(4); colors.resize(4); @@ -759,7 +759,7 @@ void ColorPicker::_hsv_draw(int p_which, Control *c) { Ref<Texture2D> cursor = get_theme_icon(SNAME("picker_cursor"), SNAME("ColorPicker")); int x; int y; - if (picker_type == SHAPE_VHS_CIRCLE || picker_type == SHAPE_OKHSL_CIRCLE) { + if (actual_shape == SHAPE_VHS_CIRCLE || actual_shape == SHAPE_OKHSL_CIRCLE) { x = center.x + (center.x * Math::cos(h * Math_TAU) * s) - (cursor->get_width() / 2); y = center.y + (center.y * Math::sin(h * Math_TAU) * s) - (cursor->get_height() / 2); } else { @@ -773,7 +773,7 @@ void ColorPicker::_hsv_draw(int p_which, Control *c) { c->draw_texture(cursor, Point2(x, y)); col.set_hsv(h, 1, 1); - if (picker_type == SHAPE_HSV_WHEEL) { + if (actual_shape == SHAPE_HSV_WHEEL) { points.resize(4); double h1 = h - (0.5 / 360); double h2 = h + (0.5 / 360); @@ -785,14 +785,14 @@ void ColorPicker::_hsv_draw(int p_which, Control *c) { } } else if (p_which == 1) { - if (picker_type == SHAPE_HSV_RECTANGLE) { + if (actual_shape == SHAPE_HSV_RECTANGLE) { Ref<Texture2D> hue = get_theme_icon(SNAME("color_hue"), SNAME("ColorPicker")); c->draw_texture_rect(hue, Rect2(Point2(), c->get_size())); int y = c->get_size().y - c->get_size().y * (1.0 - h); Color col; col.set_hsv(h, 1, 1); c->draw_line(Point2(0, y), Point2(c->get_size().x, y), col.inverted()); - } else if (picker_type == SHAPE_OKHSL_CIRCLE) { + } else if (actual_shape == SHAPE_OKHSL_CIRCLE) { Vector<Point2> points; Vector<Color> colors; Color col; @@ -811,7 +811,7 @@ void ColorPicker::_hsv_draw(int p_which, Control *c) { int y = c->get_size().y - c->get_size().y * CLAMP(v, 0, 1); col.set_ok_hsl(h, 1, v); c->draw_line(Point2(0, y), Point2(c->get_size().x, y), col.inverted()); - } else if (picker_type == SHAPE_VHS_CIRCLE) { + } else if (actual_shape == SHAPE_VHS_CIRCLE) { Vector<Point2> points; Vector<Color> colors; Color col; @@ -833,87 +833,24 @@ void ColorPicker::_hsv_draw(int p_which, Control *c) { } } else if (p_which == 2) { c->draw_rect(Rect2(Point2(), c->get_size()), Color(1, 1, 1)); - if (picker_type == SHAPE_VHS_CIRCLE || picker_type == SHAPE_OKHSL_CIRCLE) { + if (actual_shape == SHAPE_VHS_CIRCLE || actual_shape == SHAPE_OKHSL_CIRCLE) { circle_mat->set_shader_param("v", v); } } } void ColorPicker::_slider_draw(int p_which) { - Vector<Vector2> pos; - pos.resize(4); - Vector<Color> col; - col.resize(4); - Size2 size = scroll[p_which]->get_size(); - Color left_color; - Color right_color; - const real_t margin = 4 * get_theme_default_base_scale(); - - if (p_which == 3) { - scroll[p_which]->draw_texture_rect(get_theme_icon(SNAME("sample_bg"), SNAME("ColorPicker")), Rect2(Point2(0, margin), Size2(size.x, margin)), true); - - left_color = color; - left_color.a = 0; - right_color = color; - right_color.a = 1; - } else { - if (raw_mode_enabled) { - return; - } - if (hsv_mode_enabled) { - if (p_which == 0) { - Ref<Texture2D> hue = get_theme_icon(SNAME("color_hue"), SNAME("ColorPicker")); - scroll[p_which]->draw_set_transform(Point2(), -Math_PI / 2, Size2(1.0, 1.0)); - scroll[p_which]->draw_texture_rect(hue, Rect2(Vector2(margin * -2, 0), Vector2(scroll[p_which]->get_size().x, margin)), false, Color(1, 1, 1), true); - return; - } - Color s_col; - Color v_col; - if (picker_type == SHAPE_OKHSL_CIRCLE) { - s_col.set_ok_hsl(h, 0, v); - } else { - s_col.set_hsv(h, 0, v); - } - left_color = (p_which == 1) ? s_col : Color(0, 0, 0); - if (picker_type == SHAPE_OKHSL_CIRCLE) { - s_col.set_ok_hsl(h, 1, v); - v_col.set_ok_hsl(h, s, 1); - } else { - s_col.set_hsv(h, 1, v); - v_col.set_hsv(h, s, 1); - } - right_color = (p_which == 1) ? s_col : v_col; - } else { - left_color = Color( - p_which == 0 ? 0 : color.r, - p_which == 1 ? 0 : color.g, - p_which == 2 ? 0 : color.b); - right_color = Color( - p_which == 0 ? 1 : color.r, - p_which == 1 ? 1 : color.g, - p_which == 2 ? 1 : color.b); - } - } - - col.set(0, left_color); - col.set(1, right_color); - col.set(2, right_color); - col.set(3, left_color); - pos.set(0, Vector2(0, margin)); - pos.set(1, Vector2(size.x, margin)); - pos.set(2, Vector2(size.x, margin * 2)); - pos.set(3, Vector2(0, margin * 2)); - - scroll[p_which]->draw_polygon(pos, col); + modes[current_mode]->slider_draw(p_which); } void ColorPicker::_uv_input(const Ref<InputEvent> &p_event, Control *c) { Ref<InputEventMouseButton> bev = p_event; + PickerShapeType current_picker = _get_actual_shape(); if (bev.is_valid()) { if (bev->is_pressed() && bev->get_button_index() == MouseButton::LEFT) { Vector2 center = c->get_size() / 2.0; - if (picker_type == SHAPE_VHS_CIRCLE || picker_type == SHAPE_OKHSL_CIRCLE) { + if (current_picker == SHAPE_VHS_CIRCLE || current_picker == SHAPE_OKHSL_CIRCLE) { real_t dist = center.distance_to(bev->get_position()); if (dist <= center.x) { real_t rad = center.angle_to_point(bev->get_position()); @@ -951,11 +888,12 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event, Control *c) { } } changing_color = true; - if (picker_type == SHAPE_OKHSL_CIRCLE) { + if (current_picker == SHAPE_OKHSL_CIRCLE) { color.set_ok_hsl(h, s, v, color.a); - } else if (picker_type != SHAPE_OKHSL_CIRCLE) { + } else { color.set_hsv(h, s, v, color.a); } + last_color = color; set_pick_color(color); @@ -981,7 +919,7 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event, Control *c) { } Vector2 center = c->get_size() / 2.0; - if (picker_type == SHAPE_VHS_CIRCLE || picker_type == SHAPE_OKHSL_CIRCLE) { + if (current_picker == SHAPE_VHS_CIRCLE || current_picker == SHAPE_OKHSL_CIRCLE) { real_t dist = center.distance_to(mev->get_position()); real_t rad = center.angle_to_point(mev->get_position()); h = ((rad >= 0) ? rad : (Math_TAU + rad)) / Math_TAU; @@ -1002,9 +940,9 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event, Control *c) { v = 1.0 - (y - corner_y) / real_size.y; } } - if (picker_type != SHAPE_OKHSL_CIRCLE) { + if (current_picker != SHAPE_OKHSL_CIRCLE) { color.set_hsv(h, s, v, color.a); - } else if (picker_type == SHAPE_OKHSL_CIRCLE) { + } else { color.set_ok_hsl(h, s, v, color.a); } last_color = color; @@ -1018,12 +956,13 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event, Control *c) { void ColorPicker::_w_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> bev = p_event; + PickerShapeType actual_shape = _get_actual_shape(); if (bev.is_valid()) { if (bev->is_pressed() && bev->get_button_index() == MouseButton::LEFT) { changing_color = true; float y = CLAMP((float)bev->get_position().y, 0, w_edit->get_size().height); - if (picker_type == SHAPE_VHS_CIRCLE || picker_type == SHAPE_OKHSL_CIRCLE) { + if (actual_shape == SHAPE_VHS_CIRCLE || actual_shape == SHAPE_OKHSL_CIRCLE) { v = 1.0 - (y / w_edit->get_size().height); } else { h = y / w_edit->get_size().height; @@ -1031,9 +970,9 @@ void ColorPicker::_w_input(const Ref<InputEvent> &p_event) { } else { changing_color = false; } - if (picker_type != SHAPE_OKHSL_CIRCLE) { + if (actual_shape != SHAPE_OKHSL_CIRCLE) { color.set_hsv(h, s, v, color.a); - } else if (picker_type == SHAPE_OKHSL_CIRCLE) { + } else { color.set_ok_hsl(h, s, v, color.a); } last_color = color; @@ -1053,16 +992,18 @@ void ColorPicker::_w_input(const Ref<InputEvent> &p_event) { return; } float y = CLAMP((float)mev->get_position().y, 0, w_edit->get_size().height); - if (picker_type == SHAPE_VHS_CIRCLE || picker_type == SHAPE_OKHSL_CIRCLE) { + if (actual_shape == SHAPE_VHS_CIRCLE || actual_shape == SHAPE_OKHSL_CIRCLE) { v = 1.0 - (y / w_edit->get_size().height); } else { h = y / w_edit->get_size().height; } - if (hsv_mode_enabled && picker_type != SHAPE_OKHSL_CIRCLE) { + + if (current_mode == MODE_HSV) { color.set_hsv(h, s, v, color.a); - } else if (hsv_mode_enabled && picker_type == SHAPE_OKHSL_CIRCLE) { + } else if (current_mode == MODE_OKHSL) { color.set_ok_hsl(h, s, v, color.a); } + last_color = color; set_pick_color(color); _update_color(); @@ -1153,21 +1094,30 @@ void ColorPicker::_focus_enter() { c_text->select(0, 0); } - for (int i = 0; i < 4; i++) { + for (int i = 0; i < current_slider_count; i++) { if (values[i]->get_line_edit()->has_focus() && !has_ctext_focus) { values[i]->get_line_edit()->select_all(); } else { values[i]->get_line_edit()->select(0, 0); } } + if (alpha_value->get_line_edit()->has_focus() && !has_ctext_focus) { + alpha_value->get_line_edit()->select_all(); + } else { + alpha_value->get_line_edit()->select(0, 0); + } } void ColorPicker::_focus_exit() { - for (int i = 0; i < 4; i++) { + for (int i = 0; i < current_slider_count; i++) { if (!values[i]->get_line_edit()->get_menu()->is_visible()) { values[i]->get_line_edit()->select(0, 0); } } + if (!alpha_value->get_line_edit()->get_menu()->is_visible()) { + alpha_value->get_line_edit()->select(0, 0); + } + c_text->select(0, 0); } @@ -1207,12 +1157,10 @@ bool ColorPicker::are_presets_visible() const { void ColorPicker::_bind_methods() { ClassDB::bind_method(D_METHOD("set_pick_color", "color"), &ColorPicker::set_pick_color); ClassDB::bind_method(D_METHOD("get_pick_color"), &ColorPicker::get_pick_color); - ClassDB::bind_method(D_METHOD("set_hsv_mode", "enabled"), &ColorPicker::set_hsv_mode); - ClassDB::bind_method(D_METHOD("is_hsv_mode"), &ColorPicker::is_hsv_mode); - ClassDB::bind_method(D_METHOD("set_raw_mode", "enabled"), &ColorPicker::set_raw_mode); - ClassDB::bind_method(D_METHOD("is_raw_mode"), &ColorPicker::is_raw_mode); ClassDB::bind_method(D_METHOD("set_deferred_mode", "mode"), &ColorPicker::set_deferred_mode); ClassDB::bind_method(D_METHOD("is_deferred_mode"), &ColorPicker::is_deferred_mode); + ClassDB::bind_method(D_METHOD("set_color_mode", "color_mode"), &ColorPicker::set_color_mode); + ClassDB::bind_method(D_METHOD("get_color_mode"), &ColorPicker::get_color_mode); ClassDB::bind_method(D_METHOD("set_edit_alpha", "show"), &ColorPicker::set_edit_alpha); ClassDB::bind_method(D_METHOD("is_editing_alpha"), &ColorPicker::is_editing_alpha); ClassDB::bind_method(D_METHOD("set_presets_enabled", "enabled"), &ColorPicker::set_presets_enabled); @@ -1222,13 +1170,12 @@ void ColorPicker::_bind_methods() { ClassDB::bind_method(D_METHOD("add_preset", "color"), &ColorPicker::add_preset); ClassDB::bind_method(D_METHOD("erase_preset", "color"), &ColorPicker::erase_preset); ClassDB::bind_method(D_METHOD("get_presets"), &ColorPicker::get_presets); - ClassDB::bind_method(D_METHOD("set_picker_shape", "picker"), &ColorPicker::set_picker_shape); + ClassDB::bind_method(D_METHOD("set_picker_shape", "shape"), &ColorPicker::set_picker_shape); ClassDB::bind_method(D_METHOD("get_picker_shape"), &ColorPicker::get_picker_shape); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_pick_color", "get_pick_color"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "edit_alpha"), "set_edit_alpha", "is_editing_alpha"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hsv_mode"), "set_hsv_mode", "is_hsv_mode"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "raw_mode"), "set_raw_mode", "is_raw_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "color_mode", PROPERTY_HINT_ENUM, "RGB,HSV,RAW,OKHSL"), "set_color_mode", "get_color_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deferred_mode"), "set_deferred_mode", "is_deferred_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "picker_shape", PROPERTY_HINT_ENUM, "HSV Rectangle,HSV Rectangle Wheel,VHS Circle,OKHSL Circle"), "set_picker_shape", "get_picker_shape"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "presets_enabled"), "set_presets_enabled", "are_presets_enabled"); @@ -1238,6 +1185,11 @@ void ColorPicker::_bind_methods() { ADD_SIGNAL(MethodInfo("preset_added", PropertyInfo(Variant::COLOR, "color"))); ADD_SIGNAL(MethodInfo("preset_removed", PropertyInfo(Variant::COLOR, "color"))); + BIND_ENUM_CONSTANT(MODE_RGB); + BIND_ENUM_CONSTANT(MODE_HSV); + BIND_ENUM_CONSTANT(MODE_RAW); + BIND_ENUM_CONSTANT(MODE_OKHSL); + BIND_ENUM_CONSTANT(SHAPE_HSV_RECTANGLE); BIND_ENUM_CONSTANT(SHAPE_HSV_WHEEL); BIND_ENUM_CONSTANT(SHAPE_VHS_CIRCLE); @@ -1250,6 +1202,7 @@ ColorPicker::ColorPicker() : add_child(hb_edit, false, INTERNAL_MODE_FRONT); hb_edit->set_v_size_flags(SIZE_EXPAND_FILL); + uv_edit = memnew(Control); hb_edit->add_child(uv_edit); uv_edit->connect("gui_input", callable_mp(this, &ColorPicker::_uv_input), make_binds(uv_edit)); uv_edit->set_mouse_filter(MOUSE_FILTER_PASS); @@ -1260,11 +1213,13 @@ ColorPicker::ColorPicker() : HBoxContainer *hb_smpl = memnew(HBoxContainer); add_child(hb_smpl, false, INTERNAL_MODE_FRONT); + sample = memnew(TextureRect); hb_smpl->add_child(sample); sample->set_h_size_flags(SIZE_EXPAND_FILL); sample->connect("gui_input", callable_mp(this, &ColorPicker::_sample_input)); sample->connect("draw", callable_mp(this, &ColorPicker::_sample_draw)); + btn_pick = memnew(Button); btn_pick->set_flat(true); hb_smpl->add_child(btn_pick); btn_pick->set_toggle_mode(true); @@ -1277,51 +1232,35 @@ ColorPicker::ColorPicker() : add_child(memnew(HSeparator), false, INTERNAL_MODE_FRONT); VBoxContainer *vbr = memnew(VBoxContainer); + add_child(vbr, false, INTERNAL_MODE_FRONT); vbr->set_h_size_flags(SIZE_EXPAND_FILL); - for (int i = 0; i < 4; i++) { - HBoxContainer *hbc = memnew(HBoxContainer); + GridContainer *gc = memnew(GridContainer); - labels[i] = memnew(Label()); - labels[i]->set_custom_minimum_size(Size2(get_theme_constant(SNAME("label_width")), 0)); - labels[i]->set_v_size_flags(SIZE_SHRINK_CENTER); - hbc->add_child(labels[i]); + vbr->add_child(gc); + gc->set_h_size_flags(SIZE_EXPAND_FILL); + gc->set_columns(3); - scroll[i] = memnew(HSlider); - scroll[i]->set_v_size_flags(SIZE_SHRINK_CENTER); - scroll[i]->set_focus_mode(FOCUS_NONE); - hbc->add_child(scroll[i]); - - values[i] = memnew(SpinBox); - scroll[i]->share(values[i]); - hbc->add_child(values[i]); - values[i]->get_line_edit()->connect("focus_entered", callable_mp(this, &ColorPicker::_focus_enter)); - values[i]->get_line_edit()->connect("focus_exited", callable_mp(this, &ColorPicker::_focus_exit)); - - scroll[i]->set_min(0); - scroll[i]->set_page(0); - scroll[i]->set_h_size_flags(SIZE_EXPAND_FILL); - - scroll[i]->connect("value_changed", callable_mp(this, &ColorPicker::_value_changed)); - scroll[i]->connect("draw", callable_mp(this, &ColorPicker::_slider_draw), make_binds(i)); - - vbr->add_child(hbc); + for (int i = 0; i < SLIDER_COUNT + 1; i++) { + create_slider(gc, i); } - labels[3]->set_text("A"); + alpha_label->set_text("A"); HBoxContainer *hhb = memnew(HBoxContainer); vbr->add_child(hhb); - hhb->add_child(btn_hsv); - btn_hsv->set_text(RTR("HSV")); - btn_hsv->connect("toggled", callable_mp(this, &ColorPicker::set_hsv_mode)); + mode_option_button = memnew(OptionButton); - hhb->add_child(btn_raw); - btn_raw->set_text(RTR("Raw")); - btn_raw->connect("toggled", callable_mp(this, &ColorPicker::set_raw_mode)); + hhb->add_child(mode_option_button); + add_mode(new ColorModeRGB(this)); + add_mode(new ColorModeHSV(this)); + add_mode(new ColorModeRAW(this)); + add_mode(new ColorModeOKHSL(this)); + mode_option_button->connect("item_selected", callable_mp(this, &ColorPicker::_set_color_mode)); + text_type = memnew(Button); hhb->add_child(text_type); text_type->set_text("#"); text_type->set_tooltip(RTR("Switch between hexadecimal and code values.")); @@ -1332,12 +1271,14 @@ ColorPicker::ColorPicker() : text_type->set_mouse_filter(MOUSE_FILTER_IGNORE); } + c_text = memnew(LineEdit); hhb->add_child(c_text); c_text->set_h_size_flags(SIZE_EXPAND_FILL); c_text->connect("text_submitted", callable_mp(this, &ColorPicker::_html_submitted)); c_text->connect("focus_entered", callable_mp(this, &ColorPicker::_focus_enter)); c_text->connect("focus_exited", callable_mp(this, &ColorPicker::_html_focus_exit)); + wheel_edit = memnew(AspectRatioContainer); wheel_edit->set_h_size_flags(SIZE_EXPAND_FILL); wheel_edit->set_v_size_flags(SIZE_EXPAND_FILL); hb_edit->add_child(wheel_edit); @@ -1347,41 +1288,53 @@ ColorPicker::ColorPicker() : circle_mat.instantiate(); circle_mat->set_shader(circle_shader); + wheel_margin = memnew(MarginContainer); wheel_margin->add_theme_constant_override("margin_bottom", 8); wheel_edit->add_child(wheel_margin); + wheel = memnew(Control); wheel_margin->add_child(wheel); wheel->set_mouse_filter(MOUSE_FILTER_PASS); wheel->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(2, wheel)); + wheel_uv = memnew(Control); wheel_margin->add_child(wheel_uv); wheel_uv->connect("gui_input", callable_mp(this, &ColorPicker::_uv_input), make_binds(wheel_uv)); wheel_uv->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(0, wheel_uv)); + w_edit = memnew(Control); hb_edit->add_child(w_edit); w_edit->set_h_size_flags(SIZE_FILL); w_edit->set_v_size_flags(SIZE_EXPAND_FILL); w_edit->connect("gui_input", callable_mp(this, &ColorPicker::_w_input)); w_edit->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(1, w_edit)); - picker_type = SHAPE_HSV_RECTANGLE; _update_controls(); updating = false; set_pick_color(Color(1, 1, 1)); + preset_separator = memnew(HSeparator); add_child(preset_separator, false, INTERNAL_MODE_FRONT); + preset_container = memnew(GridContainer); preset_container->set_h_size_flags(SIZE_EXPAND_FILL); preset_container->set_columns(preset_column_count); add_child(preset_container, false, INTERNAL_MODE_FRONT); + btn_add_preset = memnew(Button); btn_add_preset->set_icon_alignment(HORIZONTAL_ALIGNMENT_CENTER); btn_add_preset->set_tooltip(RTR("Add current color as a preset.")); btn_add_preset->connect("pressed", callable_mp(this, &ColorPicker::_add_preset_pressed)); preset_container->add_child(btn_add_preset); } +ColorPicker::~ColorPicker() { + for (int i = 0; i < modes.size(); i++) { + delete modes[i]; + } +} + ///////////////// void ColorPickerButton::_about_to_popup() { diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h index 953be032ec..e219c78319 100644 --- a/scene/gui/color_picker.h +++ b/scene/gui/color_picker.h @@ -34,16 +34,23 @@ #include "scene/gui/aspect_ratio_container.h" #include "scene/gui/box_container.h" #include "scene/gui/button.h" -#include "scene/gui/check_button.h" +#include "scene/gui/control.h" #include "scene/gui/grid_container.h" #include "scene/gui/label.h" #include "scene/gui/line_edit.h" +#include "scene/gui/option_button.h" #include "scene/gui/popup.h" #include "scene/gui/separator.h" #include "scene/gui/slider.h" #include "scene/gui/spin_box.h" #include "scene/gui/texture_rect.h" +class ColorMode; +class ColorModeRGB; +class ColorModeHSV; +class ColorModeRAW; +class ColorModeOKHSL; + class ColorPresetButton : public BaseButton { GDCLASS(ColorPresetButton, BaseButton); @@ -64,6 +71,15 @@ class ColorPicker : public BoxContainer { GDCLASS(ColorPicker, BoxContainer); public: + enum ColorModeType { + MODE_RGB, + MODE_HSV, + MODE_RAW, + MODE_OKHSL, + + MODE_MAX + }; + enum PickerShapeType { SHAPE_HSV_RECTANGLE, SHAPE_HSV_WHEEL, @@ -73,38 +89,52 @@ public: SHAPE_MAX }; + static const int SLIDER_COUNT = 4; + private: static Ref<Shader> wheel_shader; static Ref<Shader> circle_shader; static Ref<Shader> circle_ok_color_shader; static List<Color> preset_cache; + int current_slider_count = SLIDER_COUNT; + + bool slider_theme_modified = true; + + Vector<ColorMode *> modes; + Control *screen = nullptr; - Control *uv_edit = memnew(Control); - Control *w_edit = memnew(Control); - AspectRatioContainer *wheel_edit = memnew(AspectRatioContainer); - MarginContainer *wheel_margin = memnew(MarginContainer); + Control *uv_edit = nullptr; + Control *w_edit = nullptr; + AspectRatioContainer *wheel_edit = nullptr; + MarginContainer *wheel_margin = nullptr; Ref<ShaderMaterial> wheel_mat; Ref<ShaderMaterial> circle_mat; - Control *wheel = memnew(Control); - Control *wheel_uv = memnew(Control); - TextureRect *sample = memnew(TextureRect); - GridContainer *preset_container = memnew(GridContainer); - HSeparator *preset_separator = memnew(HSeparator); - Button *btn_add_preset = memnew(Button); - Button *btn_pick = memnew(Button); - CheckButton *btn_hsv = memnew(CheckButton); - CheckButton *btn_raw = memnew(CheckButton); - HSlider *scroll[4]; - SpinBox *values[4]; - Label *labels[4]; - Button *text_type = memnew(Button); - LineEdit *c_text = memnew(LineEdit); + Control *wheel = nullptr; + Control *wheel_uv = nullptr; + TextureRect *sample = nullptr; + GridContainer *preset_container = nullptr; + HSeparator *preset_separator = nullptr; + Button *btn_add_preset = nullptr; + Button *btn_pick = nullptr; + + OptionButton *mode_option_button = nullptr; + + HSlider *sliders[SLIDER_COUNT]; + SpinBox *values[SLIDER_COUNT]; + Label *labels[SLIDER_COUNT]; + Button *text_type = nullptr; + LineEdit *c_text = nullptr; + + HSlider *alpha_slider = nullptr; + SpinBox *alpha_value = nullptr; + Label *alpha_label = nullptr; bool edit_alpha = true; Size2i ms; bool text_is_constructor = false; - PickerShapeType picker_type = SHAPE_HSV_WHEEL; + PickerShapeType current_shape = SHAPE_HSV_RECTANGLE; + ColorModeType current_mode = MODE_RGB; const int preset_column_count = 9; int prev_preset_size = 0; @@ -114,8 +144,6 @@ private: Color old_color; bool display_old_color = false; - bool raw_mode_enabled = false; - bool hsv_mode_enabled = false; bool deferred_mode_enabled = false; bool updating = true; bool changing_color = false; @@ -128,6 +156,9 @@ private: float v = 0.0; Color last_color; + PickerShapeType _get_actual_shape() const; + void create_slider(GridContainer *gc, int idx); + void _reset_theme(); void _html_submitted(const String &p_html); void _value_changed(double); void _update_controls(); @@ -152,14 +183,21 @@ private: inline int _get_preset_size(); void _add_preset_button(int p_size, const Color &p_color); + void _set_color_mode(ColorModeType p_mode); + protected: void _notification(int); static void _bind_methods(); public: + HSlider *get_slider(int idx); + Vector<float> get_active_slider_values(); + static void init_shaders(); static void finish_shaders(); + void add_mode(ColorMode *p_mode); + void set_edit_alpha(bool p_show); bool is_editing_alpha() const; @@ -173,7 +211,7 @@ public: void set_display_old_color(bool p_enabled); bool is_displaying_old_color() const; - void set_picker_shape(PickerShapeType p_picker_type); + void set_picker_shape(PickerShapeType p_shape); PickerShapeType get_picker_shape() const; void add_preset(const Color &p_color); @@ -181,11 +219,8 @@ public: PackedColorArray get_presets() const; void _update_presets(); - void set_hsv_mode(bool p_enabled); - bool is_hsv_mode() const; - - void set_raw_mode(bool p_enabled); - bool is_raw_mode() const; + void set_color_mode(ColorModeType p_mode); + ColorModeType get_color_mode() const; void set_deferred_mode(bool p_enabled); bool is_deferred_mode() const; @@ -199,6 +234,7 @@ public: void set_focus_on_line_edit(); ColorPicker(); + ~ColorPicker(); }; class ColorPickerButton : public Button { @@ -239,4 +275,5 @@ public: }; VARIANT_ENUM_CAST(ColorPicker::PickerShapeType); +VARIANT_ENUM_CAST(ColorPicker::ColorModeType); #endif // COLOR_PICKER_H 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/file_dialog.cpp b/scene/gui/file_dialog.cpp index 73effa6c4b..6bb4ac9c6f 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -480,7 +480,12 @@ void FileDialog::update_file_name() { String filter_str = filters[idx]; String file_str = file->get_text(); String base_name = file_str.get_basename(); - file_str = base_name + "." + filter_str.strip_edges().to_lower(); + Vector<String> filter_substr = filter_str.split(";"); + if (filter_substr.size() >= 2) { + file_str = base_name + "." + filter_substr[0].strip_edges().get_extension().to_lower(); + } else { + file_str = base_name + "." + filter_str.strip_edges().get_extension().to_lower(); + } file->set_text(file_str); } } 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 540250c8e9..39f8f23cd8 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -744,6 +744,17 @@ void LineEdit::_notification(int p_what) { update(); } break; + case NOTIFICATION_INTERNAL_PROCESS: { + if (caret_blinking) { + caret_blink_timer += get_process_delta_time(); + + if (caret_blink_timer >= caret_blink_speed) { + caret_blink_timer = 0.0; + _toggle_draw_caret(); + } + } + } break; + case NOTIFICATION_DRAW: { if ((!has_focus() && !(menu && menu->has_focus()) && !caret_force_displayed) || !window_has_focus) { draw_caret = false; @@ -776,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: @@ -991,8 +1002,9 @@ void LineEdit::_notification(int p_what) { case NOTIFICATION_FOCUS_ENTER: { if (!caret_force_displayed) { if (caret_blink_enabled) { - if (caret_blink_timer->is_stopped()) { - caret_blink_timer->start(); + if (!caret_blinking) { + caret_blinking = true; + caret_blink_timer = 0.0; } } else { draw_caret = true; @@ -1010,7 +1022,7 @@ void LineEdit::_notification(int p_what) { case NOTIFICATION_FOCUS_EXIT: { if (caret_blink_enabled && !caret_force_displayed) { - caret_blink_timer->stop(); + caret_blinking = false; } if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) { @@ -1318,14 +1330,16 @@ bool LineEdit::is_caret_blink_enabled() const { void LineEdit::set_caret_blink_enabled(const bool p_enabled) { caret_blink_enabled = p_enabled; + set_process_internal(p_enabled); if (has_focus() || caret_force_displayed) { if (p_enabled) { - if (caret_blink_timer->is_stopped()) { - caret_blink_timer->start(); + if (!caret_blinking) { + caret_blinking = true; + caret_blink_timer = 0.0; } } else { - caret_blink_timer->stop(); + caret_blinking = false; } } @@ -1345,20 +1359,19 @@ void LineEdit::set_caret_force_displayed(const bool p_enabled) { } float LineEdit::get_caret_blink_speed() const { - return caret_blink_timer->get_wait_time(); + return caret_blink_speed; } void LineEdit::set_caret_blink_speed(const float p_speed) { ERR_FAIL_COND(p_speed <= 0); - caret_blink_timer->set_wait_time(p_speed); + caret_blink_speed = p_speed; } void LineEdit::_reset_caret_blink_timer() { if (caret_blink_enabled) { draw_caret = true; if (has_focus()) { - caret_blink_timer->stop(); - caret_blink_timer->start(); + caret_blink_timer = 0.0; update(); } } @@ -1438,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; @@ -1675,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) { @@ -1683,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; @@ -2142,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; @@ -2223,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; @@ -2298,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); @@ -2406,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() { @@ -2508,10 +2450,6 @@ LineEdit::LineEdit(const String &p_placeholder) { set_mouse_filter(MOUSE_FILTER_STOP); set_process_unhandled_key_input(true); - caret_blink_timer = memnew(Timer); - add_child(caret_blink_timer, false, INTERNAL_MODE_FRONT); - caret_blink_timer->set_wait_time(0.65); - caret_blink_timer->connect("timeout", callable_mp(this, &LineEdit::_toggle_draw_caret)); set_caret_blink_enabled(false); set_placeholder(p_placeholder); diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index 0fb178fca4..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; @@ -160,7 +159,9 @@ private: bool caret_blink_enabled = false; bool caret_force_displayed = false; bool draw_caret = true; - Timer *caret_blink_timer = nullptr; + float caret_blink_speed = 0.65; + double caret_blink_timer = 0.0; + bool caret_blinking = false; bool _is_over_clear_button(const Point2 &p_pos) const; @@ -208,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: @@ -246,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..05824d54f1 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. @@ -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, "Font"); + 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 5506616b7a..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); @@ -2668,7 +2662,11 @@ void TextEdit::_update_caches() { /* General overrides. */ Size2 TextEdit::get_minimum_size() const { - return style_normal->get_minimum_size(); + Size2 size = style_normal->get_minimum_size(); + if (fit_content_height) { + size.y += content_height_cache; + } + return size; } bool TextEdit::is_text_field() const { @@ -2854,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; @@ -4499,6 +4470,18 @@ float TextEdit::get_v_scroll_speed() const { return v_scroll_speed; } +void TextEdit::set_fit_content_height_enabled(const bool p_enabled) { + if (fit_content_height == p_enabled) { + return; + } + fit_content_height = p_enabled; + update_minimum_size(); +} + +bool TextEdit::is_fit_content_height_enabled() const { + return fit_content_height; +} + double TextEdit::get_scroll_pos_for_line(int p_line, int p_wrap_index) const { ERR_FAIL_INDEX_V(p_line, text.size(), 0); ERR_FAIL_COND_V(p_wrap_index < 0, 0); @@ -5055,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); @@ -5091,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); @@ -5297,7 +5277,7 @@ void TextEdit::_bind_methods() { /* Viewport. */ // Scrolling. - ClassDB::bind_method(D_METHOD("set_smooth_scroll_enable", "enable"), &TextEdit::set_smooth_scroll_enabled); + ClassDB::bind_method(D_METHOD("set_smooth_scroll_enabled", "enable"), &TextEdit::set_smooth_scroll_enabled); ClassDB::bind_method(D_METHOD("is_smooth_scroll_enabled"), &TextEdit::is_smooth_scroll_enabled); ClassDB::bind_method(D_METHOD("set_v_scroll", "value"), &TextEdit::set_v_scroll); @@ -5312,6 +5292,9 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_v_scroll_speed", "speed"), &TextEdit::set_v_scroll_speed); ClassDB::bind_method(D_METHOD("get_v_scroll_speed"), &TextEdit::get_v_scroll_speed); + ClassDB::bind_method(D_METHOD("set_fit_content_height_enabled"), &TextEdit::set_fit_content_height_enabled); + ClassDB::bind_method(D_METHOD("is_fit_content_height_enabled"), &TextEdit::is_fit_content_height_enabled); + ClassDB::bind_method(D_METHOD("get_scroll_pos_for_line", "line", "wrap_index"), &TextEdit::get_scroll_pos_for_line, DEFVAL(0)); // Visible lines. @@ -5408,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"); @@ -5431,11 +5412,12 @@ void TextEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "syntax_highlighter", PROPERTY_HINT_RESOURCE_TYPE, "SyntaxHighlighter", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "set_syntax_highlighter", "get_syntax_highlighter"); ADD_GROUP("Scroll", "scroll_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_smooth"), "set_smooth_scroll_enable", "is_smooth_scroll_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_smooth"), "set_smooth_scroll_enabled", "is_smooth_scroll_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scroll_v_scroll_speed", PROPERTY_HINT_NONE, "suffix:px/s"), "set_v_scroll_speed", "get_v_scroll_speed"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_past_end_of_file"), "set_scroll_past_end_of_file_enabled", "is_scroll_past_end_of_file_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scroll_vertical", PROPERTY_HINT_NONE, "suffix:px"), "set_v_scroll", "get_v_scroll"); ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_horizontal", PROPERTY_HINT_NONE, "suffix:px"), "set_h_scroll", "get_h_scroll"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_fit_content_height"), "set_fit_content_height_enabled", "is_fit_content_height_enabled"); ADD_GROUP("Minimap", "minimap_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "minimap_draw"), "set_draw_minimap", "is_drawing_minimap"); @@ -5448,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"); @@ -5473,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) { @@ -6197,6 +6127,11 @@ void TextEdit::_update_scrollbars() { total_width += minimap_width; } + content_height_cache = MAX(total_rows, 1) * get_line_height(); + if (fit_content_height) { + update_minimum_size(); + } + updating_scrolls = true; if (total_rows > visible_rows) { diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 9de2982d0a..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; @@ -456,6 +453,8 @@ private: HScrollBar *h_scroll = nullptr; VScrollBar *v_scroll = nullptr; + float content_height_cache = 0.0; + bool fit_content_height = false; bool scroll_past_end_of_file_enabled = false; // Smooth scrolling. @@ -579,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; @@ -648,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; @@ -684,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); @@ -851,6 +843,9 @@ public: void set_v_scroll_speed(float p_speed); float get_v_scroll_speed() const; + void set_fit_content_height_enabled(const bool p_enabled); + bool is_fit_content_height_enabled() const; + double get_scroll_pos_for_line(int p_line, int p_wrap_index = 0) const; // Visible lines. 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/gui/video_stream_player.h b/scene/gui/video_stream_player.h index d2822a989b..913e7905b6 100644 --- a/scene/gui/video_stream_player.h +++ b/scene/gui/video_stream_player.h @@ -64,7 +64,7 @@ class VideoStreamPlayer : public Control { bool autoplay = false; float volume = 1.0; double last_audio_time = 0.0; - bool expand = true; + bool expand = false; bool loops = false; int buffering_ms = 500; int audio_track = 0; 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 401aeb4889..9d13ac3a38 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -467,6 +467,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_color("completion_selected_color", "CodeEdit", Color(0.26, 0.26, 0.27)); theme->set_color("completion_existing_color", "CodeEdit", Color(0.87, 0.87, 0.87, 0.13)); theme->set_color("completion_scroll_color", "CodeEdit", control_font_pressed_color * Color(1, 1, 1, 0.29)); + theme->set_color("completion_scroll_hovered_color", "CodeEdit", control_font_pressed_color * Color(1, 1, 1, 0.4)); theme->set_color("completion_font_color", "CodeEdit", Color(0.67, 0.67, 0.67)); theme->set_color("font_color", "CodeEdit", control_font_color); theme->set_color("font_selected_color", "CodeEdit", Color(0, 0, 0)); @@ -917,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)); @@ -938,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); @@ -1033,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()) { @@ -1045,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 f4fd81b25f..68441afb1c 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -2190,12 +2190,12 @@ RibbonTrailMesh::RibbonTrailMesh() { /* TextMesh */ /*************************************************************************/ -void TextMesh::_generate_glyph_mesh_data(uint32_t p_hash, const Glyph &p_gl) const { - if (cache.has(p_hash)) { +void TextMesh::_generate_glyph_mesh_data(const GlyphMeshKey &p_key, const Glyph &p_gl) const { + if (cache.has(p_key)) { return; } - GlyphMeshData &gl_data = cache[p_hash]; + GlyphMeshData &gl_data = cache[p_key]; Dictionary d = TS->font_get_glyph_contours(p_gl.font_rid, p_gl.font_size, p_gl.index); Vector2 origin = Vector2(p_gl.x_off, p_gl.y_off) * pixel_size; @@ -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; @@ -2437,11 +2443,9 @@ void TextMesh::_create_mesh_array(Array &p_arr) const { continue; } if (glyphs[i].font_rid != RID()) { - uint32_t hash = hash_one_uint64(glyphs[i].font_rid.get_id()); - hash = hash_murmur3_one_32(glyphs[i].index, hash); - - _generate_glyph_mesh_data(hash, glyphs[i]); - GlyphMeshData &gl_data = cache[hash]; + GlyphMeshKey key = GlyphMeshKey(glyphs[i].font_rid.get_id(), glyphs[i].index); + _generate_glyph_mesh_data(key, glyphs[i]); + GlyphMeshData &gl_data = cache[key]; p_size += glyphs[i].repeat * gl_data.triangles.size() * ((has_depth) ? 2 : 1); i_size += glyphs[i].repeat * gl_data.triangles.size() * ((has_depth) ? 2 : 1); @@ -2496,10 +2500,9 @@ void TextMesh::_create_mesh_array(Array &p_arr) const { continue; } if (glyphs[i].font_rid != RID()) { - uint32_t hash = hash_one_uint64(glyphs[i].font_rid.get_id()); - hash = hash_murmur3_one_32(glyphs[i].index, hash); - - const GlyphMeshData &gl_data = cache[hash]; + GlyphMeshKey key = GlyphMeshKey(glyphs[i].font_rid.get_id(), glyphs[i].index); + _generate_glyph_mesh_data(key, glyphs[i]); + const GlyphMeshData &gl_data = cache[key]; int64_t ts = gl_data.triangles.size(); const Vector2 *ts_ptr = gl_data.triangles.ptr(); @@ -2682,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); @@ -2704,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"); @@ -2716,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) { @@ -2735,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(); @@ -2848,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; } @@ -2955,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 38cc7db5fe..cb93211756 100644 --- a/scene/resources/primitive_meshes.h +++ b/scene/resources/primitive_meshes.h @@ -475,6 +475,7 @@ private: sharp = p_sharp; }; }; + struct ContourInfo { real_t length = 0.0; bool ccw = true; @@ -484,6 +485,27 @@ private: ccw = p_ccw; } }; + + struct GlyphMeshKey { + uint64_t font_id; + uint32_t gl_id; + + bool operator==(const GlyphMeshKey &p_b) const { + return (font_id == p_b.font_id) && (gl_id == p_b.gl_id); + } + + GlyphMeshKey(uint64_t p_font_id, uint32_t p_gl_id) { + font_id = p_font_id; + gl_id = p_gl_id; + } + }; + + struct GlyphMeshKeyHasher { + _FORCE_INLINE_ static uint32_t hash(const GlyphMeshKey &p_a) { + return hash_murmur3_buffer(&p_a, sizeof(GlyphMeshKey)); + } + }; + struct GlyphMeshData { Vector<Vector2> triangles; Vector<Vector<ContourPoint>> contours; @@ -491,7 +513,7 @@ private: Vector2 min_p = Vector2(INFINITY, INFINITY); Vector2 max_p = Vector2(-INFINITY, -INFINITY); }; - mutable HashMap<uint32_t, GlyphMeshData> cache; + mutable HashMap<GlyphMeshKey, GlyphMeshData, GlyphMeshKeyHasher> cache; RID text_rid; String text; @@ -503,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; @@ -517,7 +538,7 @@ private: mutable bool dirty_font = true; mutable bool dirty_cache = true; - void _generate_glyph_mesh_data(uint32_t p_hash, const Glyph &p_glyph) const; + void _generate_glyph_mesh_data(const GlyphMeshKey &p_key, const Glyph &p_glyph) const; void _font_changed(); protected: @@ -526,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) @@ -552,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_file.cpp b/scene/resources/text_file.cpp index 96a47c37c4..0404e1f79b 100644 --- a/scene/resources/text_file.cpp +++ b/scene/resources/text_file.cpp @@ -64,7 +64,7 @@ Error TextFile::load_text(const String &p_path) { w[len] = 0; String s; - ERR_FAIL_COND_V_MSG(s.parse_utf8((const char *)w), ERR_INVALID_DATA, "Script '" + p_path + "' contains invalid unicode (UTF-8), so it was not loaded. Please ensure that scripts are saved in valid UTF-8 unicode."); + ERR_FAIL_COND_V_MSG(s.parse_utf8((const char *)w) != OK, ERR_INVALID_DATA, "Script '" + p_path + "' contains invalid unicode (UTF-8), so it was not loaded. Please ensure that scripts are saved in valid UTF-8 unicode."); text = s; path = p_path; return OK; 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(); }; diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index f31a71eada..e58ef68388 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -3016,12 +3016,12 @@ Error ImageTextureLayered::create_from_images(Vector<Ref<Image>> p_images) { } void ImageTextureLayered::update_layer(const Ref<Image> &p_image, int p_layer) { - ERR_FAIL_COND(texture.is_valid()); - ERR_FAIL_COND(p_image.is_null()); - ERR_FAIL_COND(p_image->get_format() != format); - ERR_FAIL_COND(p_image->get_width() != width || p_image->get_height() != height); - ERR_FAIL_INDEX(p_layer, layers); - ERR_FAIL_COND(p_image->has_mipmaps() != mipmaps); + ERR_FAIL_COND_MSG(texture.is_null(), "Texture is not initialized."); + ERR_FAIL_COND_MSG(p_image.is_null(), "Invalid image."); + ERR_FAIL_COND_MSG(p_image->get_format() != format, "Image format must match texture's image format."); + ERR_FAIL_COND_MSG(p_image->get_width() != width || p_image->get_height() != height, "Image size must match texture's image size."); + ERR_FAIL_COND_MSG(p_image->has_mipmaps() != mipmaps, "Image mipmap configuration must match texture's image mipmap configuration."); + ERR_FAIL_INDEX_MSG(p_layer, layers, "Layer index is out of bounds."); RS::get_singleton()->texture_2d_update(texture, p_image, p_layer); } diff --git a/scene/resources/world_3d.cpp b/scene/resources/world_3d.cpp index a84ee773b4..fb6dcd3d57 100644 --- a/scene/resources/world_3d.cpp +++ b/scene/resources/world_3d.cpp @@ -31,7 +31,6 @@ #include "world_3d.h" #include "core/config/project_settings.h" -#include "core/math/octree.h" #include "scene/3d/camera_3d.h" #include "scene/3d/visible_on_screen_notifier_3d.h" #include "scene/scene_string_names.h" |