diff options
Diffstat (limited to 'editor/plugins/font_config_plugin.cpp')
-rw-r--r-- | editor/plugins/font_config_plugin.cpp | 979 |
1 files changed, 979 insertions, 0 deletions
diff --git a/editor/plugins/font_config_plugin.cpp b/editor/plugins/font_config_plugin.cpp new file mode 100644 index 0000000000..848fb5887d --- /dev/null +++ b/editor/plugins/font_config_plugin.cpp @@ -0,0 +1,979 @@ +/*************************************************************************/ +/* font_config_plugin.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 "font_config_plugin.h" + +#include "editor/editor_scale.h" +#include "editor/import/dynamic_font_import_settings.h" + +/*************************************************************************/ +/* EditorPropertyFontMetaObject */ +/*************************************************************************/ + +bool EditorPropertyFontMetaObject::_set(const StringName &p_name, const Variant &p_value) { + String name = p_name; + + if (name.begins_with("keys")) { + String key = name.get_slicec('/', 1); + dict[key] = p_value; + return true; + } + + return false; +} + +bool EditorPropertyFontMetaObject::_get(const StringName &p_name, Variant &r_ret) const { + String name = p_name; + + if (name.begins_with("keys")) { + String key = name.get_slicec('/', 1); + r_ret = dict[key]; + return true; + } + + return false; +} + +void EditorPropertyFontMetaObject::_bind_methods() { +} + +void EditorPropertyFontMetaObject::set_dict(const Dictionary &p_dict) { + dict = p_dict; +} + +Dictionary EditorPropertyFontMetaObject::get_dict() { + return dict; +} + +/*************************************************************************/ +/* EditorPropertyFontOTObject */ +/*************************************************************************/ + +bool EditorPropertyFontOTObject::_set(const StringName &p_name, const Variant &p_value) { + String name = p_name; + + if (name.begins_with("keys")) { + int key = name.get_slicec('/', 1).to_int(); + dict[key] = p_value; + return true; + } + + return false; +} + +bool EditorPropertyFontOTObject::_get(const StringName &p_name, Variant &r_ret) const { + String name = p_name; + + if (name.begins_with("keys")) { + int key = name.get_slicec('/', 1).to_int(); + r_ret = dict[key]; + return true; + } + + return false; +} + +void EditorPropertyFontOTObject::_bind_methods() { + ClassDB::bind_method(D_METHOD("property_can_revert", "name"), &EditorPropertyFontOTObject::property_can_revert); + ClassDB::bind_method(D_METHOD("property_get_revert", "name"), &EditorPropertyFontOTObject::property_get_revert); +} + +void EditorPropertyFontOTObject::set_dict(const Dictionary &p_dict) { + dict = p_dict; +} + +Dictionary EditorPropertyFontOTObject::get_dict() { + return dict; +} + +void EditorPropertyFontOTObject::set_defaults(const Dictionary &p_dict) { + defaults_dict = p_dict; +} + +Dictionary EditorPropertyFontOTObject::get_defaults() { + return defaults_dict; +} + +bool EditorPropertyFontOTObject::property_can_revert(const String &p_name) { + String name = p_name; + + if (name.begins_with("keys")) { + int key = name.get_slicec('/', 1).to_int(); + if (defaults_dict.has(key) && dict.has(key)) { + int value = dict[key]; + Vector3i range = defaults_dict[key]; + return range.z != value; + } + } + + return false; +} + +Variant EditorPropertyFontOTObject::property_get_revert(const String &p_name) { + String name = p_name; + + if (name.begins_with("keys")) { + int key = name.get_slicec('/', 1).to_int(); + if (defaults_dict.has(key)) { + Vector3i range = defaults_dict[key]; + return range.z; + } + } + + return Variant(); +} + +/*************************************************************************/ +/* EditorPropertyFontMetaOverride */ +/*************************************************************************/ + +void EditorPropertyFontMetaOverride::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: { + if (Object::cast_to<Button>(button_add)) { + button_add->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); + } + } break; + } +} + +void EditorPropertyFontMetaOverride::_property_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing) { + if (p_property.begins_with("keys")) { + Dictionary dict = object->get_dict(); + String key = p_property.get_slice("/", 1); + dict[key] = (bool)p_value; + + emit_changed(get_edited_property(), dict, "", true); + + dict = dict.duplicate(); // Duplicate, so undo/redo works better. + object->set_dict(dict); + } +} + +void EditorPropertyFontMetaOverride::_remove(Object *p_button, const String &p_key) { + Dictionary dict = object->get_dict(); + + dict.erase(p_key); + + emit_changed(get_edited_property(), dict, "", false); + + dict = dict.duplicate(); // Duplicate, so undo/redo works better. + object->set_dict(dict); + update_property(); +} + +void EditorPropertyFontMetaOverride::_add_menu() { + if (script_editor) { + Size2 size = get_size(); + menu->set_position(get_screen_position() + Size2(0, size.height * get_global_transform().get_scale().y)); + menu->reset_size(); + menu->popup(); + } else { + locale_select->popup_locale_dialog(); + } +} + +void EditorPropertyFontMetaOverride::_add_script(int p_option) { + Dictionary dict = object->get_dict(); + + dict[script_codes[p_option]] = true; + + emit_changed(get_edited_property(), dict, "", false); + + dict = dict.duplicate(); // Duplicate, so undo/redo works better. + object->set_dict(dict); + update_property(); +} + +void EditorPropertyFontMetaOverride::_add_lang(const String &p_locale) { + Dictionary dict = object->get_dict(); + + dict[p_locale] = true; + + emit_changed(get_edited_property(), dict, "", false); + + dict = dict.duplicate(); // Duplicate, so undo/redo works better. + object->set_dict(dict); + update_property(); +} + +void EditorPropertyFontMetaOverride::_object_id_selected(const StringName &p_property, ObjectID p_id) { + emit_signal(SNAME("object_id_selected"), p_property, p_id); +} + +void EditorPropertyFontMetaOverride::update_property() { + Variant updated_val = get_edited_object()->get(get_edited_property()); + + Dictionary dict = updated_val; + + edit->set_text(vformat(TTR("Overrides (%d)"), dict.size())); + + bool unfolded = get_edited_object()->editor_is_section_unfolded(get_edited_property()); + if (edit->is_pressed() != unfolded) { + edit->set_pressed(unfolded); + } + + if (unfolded) { + updating = true; + + if (!container) { + container = memnew(MarginContainer); + container->set_theme_type_variation("MarginContainer4px"); + add_child(container); + set_bottom_editor(container); + + VBoxContainer *vbox = memnew(VBoxContainer); + vbox->set_v_size_flags(SIZE_EXPAND_FILL); + container->add_child(vbox); + + property_vbox = memnew(VBoxContainer); + property_vbox->set_h_size_flags(SIZE_EXPAND_FILL); + vbox->add_child(property_vbox); + + paginator = memnew(EditorPaginator); + paginator->connect("page_changed", callable_mp(this, &EditorPropertyFontMetaOverride::_page_changed)); + vbox->add_child(paginator); + } else { + // Queue children for deletion, deleting immediately might cause errors. + for (int i = property_vbox->get_child_count() - 1; i >= 0; i--) { + property_vbox->get_child(i)->queue_delete(); + } + button_add = nullptr; + } + + int size = dict.size(); + + int max_page = MAX(0, size - 1) / page_length; + page_index = MIN(page_index, max_page); + + paginator->update(page_index, max_page); + paginator->set_visible(max_page > 0); + + int offset = page_index * page_length; + + int amount = MIN(size - offset, page_length); + + dict = dict.duplicate(); + object->set_dict(dict); + + for (int i = 0; i < amount; i++) { + String name = dict.get_key_at_index(i); + EditorProperty *prop = memnew(EditorPropertyCheck); + prop->set_object_and_property(object.ptr(), "keys/" + name); + + if (script_editor) { + prop->set_label(TranslationServer::get_singleton()->get_script_name(name)); + } else { + prop->set_label(TranslationServer::get_singleton()->get_locale_name(name)); + } + prop->set_tooltip(name); + prop->set_selectable(false); + + prop->connect("property_changed", callable_mp(this, &EditorPropertyFontMetaOverride::_property_changed)); + prop->connect("object_id_selected", callable_mp(this, &EditorPropertyFontMetaOverride::_object_id_selected)); + + HBoxContainer *hbox = memnew(HBoxContainer); + property_vbox->add_child(hbox); + hbox->add_child(prop); + prop->set_h_size_flags(SIZE_EXPAND_FILL); + Button *remove = memnew(Button); + remove->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"))); + hbox->add_child(remove); + remove->connect("pressed", callable_mp(this, &EditorPropertyFontMetaOverride::_remove), varray(remove, name)); + + prop->update_property(); + } + + if (script_editor) { + button_add = EditorInspector::create_inspector_action_button(TTR("Add Script")); + } else { + button_add = EditorInspector::create_inspector_action_button(TTR("Add Locale")); + } + button_add->connect("pressed", callable_mp(this, &EditorPropertyFontMetaOverride::_add_menu)); + property_vbox->add_child(button_add); + + updating = false; + } else { + if (container) { + set_bottom_editor(nullptr); + memdelete(container); + button_add = nullptr; + container = nullptr; + } + } +} + +void EditorPropertyFontMetaOverride::_edit_pressed() { + Variant prop_val = get_edited_object()->get(get_edited_property()); + if (prop_val.get_type() == Variant::NIL) { + Callable::CallError ce; + Variant::construct(Variant::DICTIONARY, prop_val, nullptr, 0, ce); + get_edited_object()->set(get_edited_property(), prop_val); + } + + get_edited_object()->editor_set_section_unfold(get_edited_property(), edit->is_pressed()); + update_property(); +} + +void EditorPropertyFontMetaOverride::_page_changed(int p_page) { + if (updating) { + return; + } + page_index = p_page; + update_property(); +} + +EditorPropertyFontMetaOverride::EditorPropertyFontMetaOverride(bool p_script) { + script_editor = p_script; + + object.instantiate(); + page_length = int(EDITOR_GET("interface/inspector/max_array_dictionary_items_per_page")); + + edit = memnew(Button); + edit->set_h_size_flags(SIZE_EXPAND_FILL); + edit->set_clip_text(true); + edit->connect("pressed", callable_mp(this, &EditorPropertyFontMetaOverride::_edit_pressed)); + edit->set_toggle_mode(true); + add_child(edit); + add_focusable(edit); + + menu = memnew(PopupMenu); + if (script_editor) { + script_codes = TranslationServer::get_singleton()->get_all_scripts(); + for (int i = 0; i < script_codes.size(); i++) { + menu->add_item(TranslationServer::get_singleton()->get_script_name(script_codes[i]) + " (" + script_codes[i] + ")", i); + } + } + add_child(menu); + menu->connect("id_pressed", callable_mp(this, &EditorPropertyFontMetaOverride::_add_script)); + + locale_select = memnew(EditorLocaleDialog); + locale_select->connect("locale_selected", callable_mp(this, &EditorPropertyFontMetaOverride::_add_lang)); + add_child(locale_select); +} + +/*************************************************************************/ +/* EditorPropertyOTVariation */ +/*************************************************************************/ + +void EditorPropertyOTVariation::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: { + } break; + } +} + +void EditorPropertyOTVariation::_property_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing) { + if (p_property.begins_with("keys")) { + Dictionary dict = object->get_dict(); + Dictionary defaults_dict = object->get_defaults(); + int key = p_property.get_slice("/", 1).to_int(); + dict[key] = (int)p_value; + if (defaults_dict.has(key)) { + Vector3i range = defaults_dict[key]; + if (range.z == (int)p_value) { + dict.erase(key); + } + } + + emit_changed(get_edited_property(), dict, "", true); + + dict = dict.duplicate(); // Duplicate, so undo/redo works better. + object->set_dict(dict); + } +} + +void EditorPropertyOTVariation::_object_id_selected(const StringName &p_property, ObjectID p_id) { + emit_signal(SNAME("object_id_selected"), p_property, p_id); +} + +void EditorPropertyOTVariation::update_property() { + Variant updated_val = get_edited_object()->get(get_edited_property()); + + Dictionary dict = updated_val; + + Ref<Font> fd; + if (Object::cast_to<Font>(get_edited_object()) != nullptr) { + fd = get_edited_object(); + } else if (Object::cast_to<DynamicFontImportSettingsData>(get_edited_object()) != nullptr) { + Ref<DynamicFontImportSettingsData> imp = Object::cast_to<DynamicFontImportSettingsData>(get_edited_object()); + fd = imp->get_font(); + } + + Dictionary supported = (fd.is_valid()) ? fd->get_supported_variation_list() : Dictionary(); + + edit->set_text(vformat(TTR("Variation Coordinates (%d)"), supported.size())); + + bool unfolded = get_edited_object()->editor_is_section_unfolded(get_edited_property()); + if (edit->is_pressed() != unfolded) { + edit->set_pressed(unfolded); + } + + if (unfolded) { + updating = true; + + if (!container) { + container = memnew(MarginContainer); + container->set_theme_type_variation("MarginContainer4px"); + add_child(container); + set_bottom_editor(container); + + VBoxContainer *vbox = memnew(VBoxContainer); + vbox->set_v_size_flags(SIZE_EXPAND_FILL); + container->add_child(vbox); + + property_vbox = memnew(VBoxContainer); + property_vbox->set_h_size_flags(SIZE_EXPAND_FILL); + vbox->add_child(property_vbox); + + paginator = memnew(EditorPaginator); + paginator->connect("page_changed", callable_mp(this, &EditorPropertyOTVariation::_page_changed)); + vbox->add_child(paginator); + } else { + // Queue children for deletion, deleting immediately might cause errors. + for (int i = property_vbox->get_child_count() - 1; i >= 0; i--) { + property_vbox->get_child(i)->queue_delete(); + } + } + + int size = supported.size(); + + int max_page = MAX(0, size - 1) / page_length; + page_index = MIN(page_index, max_page); + + paginator->update(page_index, max_page); + paginator->set_visible(max_page > 0); + + int offset = page_index * page_length; + + int amount = MIN(size - offset, page_length); + + dict = dict.duplicate(); + object->set_dict(dict); + object->set_defaults(supported); + + for (int i = 0; i < amount; i++) { + int name_tag = supported.get_key_at_index(i); + Vector3i range = supported.get_value_at_index(i); + + EditorPropertyInteger *prop = memnew(EditorPropertyInteger); + prop->setup(range.x, range.y, 1, false, false); + prop->set_object_and_property(object.ptr(), "keys/" + itos(name_tag)); + + String name = TS->tag_to_name(name_tag); + prop->set_label(name.capitalize()); + prop->set_tooltip(name); + prop->set_selectable(false); + + prop->connect("property_changed", callable_mp(this, &EditorPropertyOTVariation::_property_changed)); + prop->connect("object_id_selected", callable_mp(this, &EditorPropertyOTVariation::_object_id_selected)); + + property_vbox->add_child(prop); + + prop->update_property(); + } + + updating = false; + } else { + if (container) { + set_bottom_editor(nullptr); + memdelete(container); + container = nullptr; + } + } +} + +void EditorPropertyOTVariation::_edit_pressed() { + Variant prop_val = get_edited_object()->get(get_edited_property()); + if (prop_val.get_type() == Variant::NIL) { + Callable::CallError ce; + Variant::construct(Variant::DICTIONARY, prop_val, nullptr, 0, ce); + get_edited_object()->set(get_edited_property(), prop_val); + } + + get_edited_object()->editor_set_section_unfold(get_edited_property(), edit->is_pressed()); + update_property(); +} + +void EditorPropertyOTVariation::_page_changed(int p_page) { + if (updating) { + return; + } + page_index = p_page; + update_property(); +} + +EditorPropertyOTVariation::EditorPropertyOTVariation() { + object.instantiate(); + page_length = int(EDITOR_GET("interface/inspector/max_array_dictionary_items_per_page")); + + edit = memnew(Button); + edit->set_h_size_flags(SIZE_EXPAND_FILL); + edit->set_clip_text(true); + edit->connect("pressed", callable_mp(this, &EditorPropertyOTVariation::_edit_pressed)); + edit->set_toggle_mode(true); + add_child(edit); + add_focusable(edit); +} + +/*************************************************************************/ +/* EditorPropertyOTFeatures */ +/*************************************************************************/ + +void EditorPropertyOTFeatures::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: { + if (Object::cast_to<Button>(button_add)) { + button_add->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); + } + } break; + } +} + +void EditorPropertyOTFeatures::_property_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing) { + if (p_property.begins_with("keys")) { + Dictionary dict = object->get_dict(); + int key = p_property.get_slice("/", 1).to_int(); + dict[key] = (int)p_value; + + emit_changed(get_edited_property(), dict, "", true); + + dict = dict.duplicate(); // Duplicate, so undo/redo works better. + object->set_dict(dict); + } +} + +void EditorPropertyOTFeatures::_remove(Object *p_button, int p_key) { + Dictionary dict = object->get_dict(); + + dict.erase(p_key); + + emit_changed(get_edited_property(), dict, "", false); + + dict = dict.duplicate(); // Duplicate, so undo/redo works better. + object->set_dict(dict); + update_property(); +} + +void EditorPropertyOTFeatures::_add_menu() { + Size2 size = get_size(); + menu->set_position(get_screen_position() + Size2(0, size.height * get_global_transform().get_scale().y)); + menu->reset_size(); + menu->popup(); +} + +void EditorPropertyOTFeatures::_add_feature(int p_option) { + Dictionary dict = object->get_dict(); + + dict[p_option] = 1; + + emit_changed(get_edited_property(), dict, "", false); + + dict = dict.duplicate(); // Duplicate, so undo/redo works better. + object->set_dict(dict); + update_property(); +} + +void EditorPropertyOTFeatures::_object_id_selected(const StringName &p_property, ObjectID p_id) { + emit_signal(SNAME("object_id_selected"), p_property, p_id); +} + +void EditorPropertyOTFeatures::update_property() { + Variant updated_val = get_edited_object()->get(get_edited_property()); + + Dictionary dict = updated_val; + + Ref<Font> fd; + if (Object::cast_to<FontVariation>(get_edited_object()) != nullptr) { + fd = get_edited_object(); + } else if (Object::cast_to<DynamicFontImportSettingsData>(get_edited_object()) != nullptr) { + Ref<DynamicFontImportSettingsData> imp = Object::cast_to<DynamicFontImportSettingsData>(get_edited_object()); + fd = imp->get_font(); + } + + Dictionary supported; + if (fd.is_valid()) { + supported = fd->get_supported_feature_list(); + } + + edit->set_text(vformat(TTR("Features (%d of %d set)"), dict.size(), supported.size())); + + bool unfolded = get_edited_object()->editor_is_section_unfolded(get_edited_property()); + if (edit->is_pressed() != unfolded) { + edit->set_pressed(unfolded); + } + + if (unfolded) { + updating = true; + + if (!container) { + container = memnew(MarginContainer); + container->set_theme_type_variation("MarginContainer4px"); + add_child(container); + set_bottom_editor(container); + + VBoxContainer *vbox = memnew(VBoxContainer); + vbox->set_v_size_flags(SIZE_EXPAND_FILL); + container->add_child(vbox); + + property_vbox = memnew(VBoxContainer); + property_vbox->set_h_size_flags(SIZE_EXPAND_FILL); + vbox->add_child(property_vbox); + + paginator = memnew(EditorPaginator); + paginator->connect("page_changed", callable_mp(this, &EditorPropertyOTFeatures::_page_changed)); + vbox->add_child(paginator); + } else { + // Queue children for deletion, deleting immediately might cause errors. + for (int i = property_vbox->get_child_count() - 1; i >= 0; i--) { + property_vbox->get_child(i)->queue_delete(); + } + button_add = nullptr; + } + + // Update add menu items. + menu->clear(); + bool have_sub[FGRP_MAX]; + for (int i = 0; i < FGRP_MAX; i++) { + menu_sub[i]->clear(); + have_sub[i] = false; + } + + bool show_hidden = EDITOR_GET("interface/inspector/show_low_level_opentype_features"); + + for (int i = 0; i < supported.size(); i++) { + int name_tag = supported.get_key_at_index(i); + Dictionary info = supported.get_value_at_index(i); + bool hidden = info["hidden"].operator bool(); + String name = TS->tag_to_name(name_tag); + FeatureGroups grp = FGRP_MAX; + + if (hidden && !show_hidden) { + continue; + } + + if (name.begins_with("stylistic_set_")) { + grp = FGRP_STYLISTIC_SET; + } else if (name.begins_with("character_variant_")) { + grp = FGRP_CHARACTER_VARIANT; + } else if (name.ends_with("_capitals")) { + grp = FGRP_CAPITLS; + } else if (name.ends_with("_ligatures")) { + grp = FGRP_LIGATURES; + } else if (name.ends_with("_alternates")) { + grp = FGRP_ALTERNATES; + } else if (name.ends_with("_kanji_forms") || name.begins_with("jis") || name == "simplified_forms" || name == "traditional_name_forms" || name == "traditional_forms") { + grp = FGRP_EAL; + } else if (name.ends_with("_widths")) { + grp = FGRP_EAW; + } else if (name == "tabular_figures" || name == "proportional_figures") { + grp = FGRP_NUMAL; + } else if (name.begins_with("custom_")) { + grp = FGRP_CUSTOM; + } + String disp_name = name.capitalize(); + if (info.has("label")) { + disp_name = vformat("%s (%s)", disp_name, info["label"].operator String()); + } + + if (grp == FGRP_MAX) { + menu->add_item(disp_name, name_tag); + } else { + menu_sub[grp]->add_item(disp_name, name_tag); + have_sub[grp] = true; + } + } + for (int i = 0; i < FGRP_MAX; i++) { + if (have_sub[i]) { + menu->add_submenu_item(RTR(group_names[i]), "FTRMenu_" + itos(i)); + } + } + + int size = dict.size(); + + int max_page = MAX(0, size - 1) / page_length; + page_index = MIN(page_index, max_page); + + paginator->update(page_index, max_page); + paginator->set_visible(max_page > 0); + + int offset = page_index * page_length; + + int amount = MIN(size - offset, page_length); + + dict = dict.duplicate(); + object->set_dict(dict); + + for (int i = 0; i < amount; i++) { + int name_tag = dict.get_key_at_index(i); + + if (supported.has(name_tag)) { + Dictionary info = supported[name_tag]; + Variant::Type vtype = Variant::Type(info["type"].operator int()); + bool hidden = info["hidden"].operator bool(); + if (hidden && !show_hidden) { + continue; + } + + EditorProperty *prop = nullptr; + switch (vtype) { + case Variant::NIL: { + prop = memnew(EditorPropertyNil); + } break; + case Variant::BOOL: { + prop = memnew(EditorPropertyCheck); + } break; + case Variant::INT: { + EditorPropertyInteger *editor = memnew(EditorPropertyInteger); + editor->setup(0, 255, 1, false, false); + prop = editor; + } break; + default: { + ERR_CONTINUE_MSG(true, vformat("Unsupported OT feature data type %s", Variant::get_type_name(vtype))); + } + } + prop->set_object_and_property(object.ptr(), "keys/" + itos(name_tag)); + + String name = TS->tag_to_name(name_tag); + String disp_name = name.capitalize(); + if (info.has("label")) { + disp_name = vformat("%s (%s)", disp_name, info["label"].operator String()); + } + prop->set_label(disp_name); + prop->set_tooltip(name); + prop->set_selectable(false); + + prop->connect("property_changed", callable_mp(this, &EditorPropertyOTFeatures::_property_changed)); + prop->connect("object_id_selected", callable_mp(this, &EditorPropertyOTFeatures::_object_id_selected)); + + HBoxContainer *hbox = memnew(HBoxContainer); + property_vbox->add_child(hbox); + hbox->add_child(prop); + prop->set_h_size_flags(SIZE_EXPAND_FILL); + Button *remove = memnew(Button); + remove->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"))); + hbox->add_child(remove); + remove->connect("pressed", callable_mp(this, &EditorPropertyOTFeatures::_remove), varray(remove, name_tag)); + + prop->update_property(); + } + } + + button_add = EditorInspector::create_inspector_action_button(TTR("Add Feature")); + button_add->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); + button_add->connect("pressed", callable_mp(this, &EditorPropertyOTFeatures::_add_menu)); + property_vbox->add_child(button_add); + + updating = false; + } else { + if (container) { + set_bottom_editor(nullptr); + memdelete(container); + button_add = nullptr; + container = nullptr; + } + } +} + +void EditorPropertyOTFeatures::_edit_pressed() { + Variant prop_val = get_edited_object()->get(get_edited_property()); + if (prop_val.get_type() == Variant::NIL) { + Callable::CallError ce; + Variant::construct(Variant::DICTIONARY, prop_val, nullptr, 0, ce); + get_edited_object()->set(get_edited_property(), prop_val); + } + + get_edited_object()->editor_set_section_unfold(get_edited_property(), edit->is_pressed()); + update_property(); +} + +void EditorPropertyOTFeatures::_page_changed(int p_page) { + if (updating) { + return; + } + page_index = p_page; + update_property(); +} + +EditorPropertyOTFeatures::EditorPropertyOTFeatures() { + object.instantiate(); + page_length = int(EDITOR_GET("interface/inspector/max_array_dictionary_items_per_page")); + + edit = memnew(Button); + edit->set_h_size_flags(SIZE_EXPAND_FILL); + edit->set_clip_text(true); + edit->connect("pressed", callable_mp(this, &EditorPropertyOTFeatures::_edit_pressed)); + edit->set_toggle_mode(true); + add_child(edit); + add_focusable(edit); + + menu = memnew(PopupMenu); + add_child(menu); + menu->connect("id_pressed", callable_mp(this, &EditorPropertyOTFeatures::_add_feature)); + + for (int i = 0; i < FGRP_MAX; i++) { + menu_sub[i] = memnew(PopupMenu); + menu_sub[i]->set_name("FTRMenu_" + itos(i)); + menu->add_child(menu_sub[i]); + menu_sub[i]->connect("id_pressed", callable_mp(this, &EditorPropertyOTFeatures::_add_feature)); + } + + group_names[FGRP_STYLISTIC_SET] = "Stylistic Sets"; + group_names[FGRP_CHARACTER_VARIANT] = "Character Variants"; + group_names[FGRP_CAPITLS] = "Capitals"; + group_names[FGRP_LIGATURES] = "Ligatures"; + group_names[FGRP_ALTERNATES] = "Alternates"; + group_names[FGRP_EAL] = "East Asian Language"; + group_names[FGRP_EAW] = "East Asian Widths"; + group_names[FGRP_NUMAL] = "Numeral Alignment"; + group_names[FGRP_CUSTOM] = "Custom"; +} + +/*************************************************************************/ +/* EditorInspectorPluginFontVariation */ +/*************************************************************************/ + +bool EditorInspectorPluginFontVariation::can_handle(Object *p_object) { + return (Object::cast_to<FontVariation>(p_object) != nullptr) || (Object::cast_to<DynamicFontImportSettingsData>(p_object) != nullptr); +} + +bool EditorInspectorPluginFontVariation::parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const uint32_t p_usage, const bool p_wide) { + if (p_path == "variation_opentype") { + add_property_editor(p_path, memnew(EditorPropertyOTVariation)); + return true; + } else if (p_path == "opentype_features") { + add_property_editor(p_path, memnew(EditorPropertyOTFeatures)); + return true; + } else if (p_path == "language_support") { + add_property_editor(p_path, memnew(EditorPropertyFontMetaOverride(false))); + return true; + } else if (p_path == "script_support") { + add_property_editor(p_path, memnew(EditorPropertyFontMetaOverride(true))); + return true; + } + return false; +} + +/*************************************************************************/ +/* FontPreview */ +/*************************************************************************/ + +void FontPreview::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_DRAW: { + // Draw font name (style). + Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label")); + int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label")); + Color text_color = get_theme_color(SNAME("font_color"), SNAME("Label")); + font->draw_string(get_canvas_item(), Point2(0, font->get_height(font_size) + 2 * EDSCALE), name, HORIZONTAL_ALIGNMENT_CENTER, get_size().x, font_size, text_color); + + // Draw font preview. + Vector2 pos = Vector2(0, font->get_height(font_size)) + (get_size() - Vector2(0, font->get_height(font_size)) - line->get_size()) / 2; + line->draw(get_canvas_item(), pos, text_color); + + // Draw font baseline. + Color line_color = text_color; + line_color.a *= 0.6; + draw_line(Vector2(0, pos.y + line->get_line_ascent()), Vector2(pos.x - 5, pos.y + line->get_line_ascent()), line_color); + draw_line(Vector2(pos.x + line->get_size().x + 5, pos.y + line->get_line_ascent()), Vector2(get_size().x, pos.y + line->get_line_ascent()), line_color); + } break; + } +} + +void FontPreview::_bind_methods() {} + +Size2 FontPreview::get_minimum_size() const { + return Vector2(64, 64) * EDSCALE; +} + +void FontPreview::set_data(const Ref<Font> &p_f) { + line->clear(); + if (p_f.is_valid()) { + name = vformat("%s (%s)", p_f->get_font_name(), p_f->get_font_style_name()); + if (p_f->is_class("FontVariation")) { + name += " " + TTR(" - Variation"); + } + String sample; + static const String sample_base = U"12漢字ԱբΑαАбΑαאבابܐܒހށआআਆઆଆஆఆಆആආกิກິༀကႠა한글ሀᎣᐁᚁᚠᜀᜠᝀᝠកᠠᤁᥐAb😀"; + for (int i = 0; i < sample_base.length(); i++) { + if (p_f->has_char(sample_base[i])) { + sample += sample_base[i]; + } + } + if (sample.is_empty()) { + sample = p_f->get_supported_chars().substr(0, 6); + } + line->add_string(sample, p_f, 50); + } + + update(); +} + +FontPreview::FontPreview() { + line.instantiate(); +} + +/*************************************************************************/ +/* EditorInspectorPluginFontPreview */ +/*************************************************************************/ + +bool EditorInspectorPluginFontPreview::can_handle(Object *p_object) { + return Object::cast_to<Font>(p_object) != nullptr; +} + +void EditorInspectorPluginFontPreview::parse_begin(Object *p_object) { + Font *fd = Object::cast_to<Font>(p_object); + ERR_FAIL_COND(!fd); + + FontPreview *editor = memnew(FontPreview); + editor->set_data(fd); + add_custom_control(editor); +} + +bool EditorInspectorPluginFontPreview::parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const uint32_t p_usage, const bool p_wide) { + return false; +} + +/*************************************************************************/ +/* FontEditorPlugin */ +/*************************************************************************/ + +FontEditorPlugin::FontEditorPlugin() { + Ref<EditorInspectorPluginFontVariation> fc_plugin; + fc_plugin.instantiate(); + EditorInspector::add_inspector_plugin(fc_plugin); + + Ref<EditorInspectorPluginFontPreview> fp_plugin; + fp_plugin.instantiate(); + EditorInspector::add_inspector_plugin(fp_plugin); +} |