2
0

font_config_plugin.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053
  1. /*************************************************************************/
  2. /* font_config_plugin.cpp */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /*************************************************************************/
  30. #include "font_config_plugin.h"
  31. #include "editor/editor_scale.h"
  32. #include "editor/import/dynamic_font_import_settings.h"
  33. /*************************************************************************/
  34. /* EditorPropertyFontMetaObject */
  35. /*************************************************************************/
  36. bool EditorPropertyFontMetaObject::_set(const StringName &p_name, const Variant &p_value) {
  37. String name = p_name;
  38. if (name.begins_with("keys")) {
  39. String key = name.get_slicec('/', 1);
  40. dict[key] = p_value;
  41. return true;
  42. }
  43. return false;
  44. }
  45. bool EditorPropertyFontMetaObject::_get(const StringName &p_name, Variant &r_ret) const {
  46. String name = p_name;
  47. if (name.begins_with("keys")) {
  48. String key = name.get_slicec('/', 1);
  49. r_ret = dict[key];
  50. return true;
  51. }
  52. return false;
  53. }
  54. void EditorPropertyFontMetaObject::_bind_methods() {
  55. }
  56. void EditorPropertyFontMetaObject::set_dict(const Dictionary &p_dict) {
  57. dict = p_dict;
  58. }
  59. Dictionary EditorPropertyFontMetaObject::get_dict() {
  60. return dict;
  61. }
  62. /*************************************************************************/
  63. /* EditorPropertyFontOTObject */
  64. /*************************************************************************/
  65. bool EditorPropertyFontOTObject::_set(const StringName &p_name, const Variant &p_value) {
  66. String name = p_name;
  67. if (name.begins_with("keys")) {
  68. int key = name.get_slicec('/', 1).to_int();
  69. dict[key] = p_value;
  70. return true;
  71. }
  72. return false;
  73. }
  74. bool EditorPropertyFontOTObject::_get(const StringName &p_name, Variant &r_ret) const {
  75. String name = p_name;
  76. if (name.begins_with("keys")) {
  77. int key = name.get_slicec('/', 1).to_int();
  78. r_ret = dict[key];
  79. return true;
  80. }
  81. return false;
  82. }
  83. void EditorPropertyFontOTObject::set_dict(const Dictionary &p_dict) {
  84. dict = p_dict;
  85. }
  86. Dictionary EditorPropertyFontOTObject::get_dict() {
  87. return dict;
  88. }
  89. void EditorPropertyFontOTObject::set_defaults(const Dictionary &p_dict) {
  90. defaults_dict = p_dict;
  91. }
  92. Dictionary EditorPropertyFontOTObject::get_defaults() {
  93. return defaults_dict;
  94. }
  95. bool EditorPropertyFontOTObject::_property_can_revert(const StringName &p_name) const {
  96. String name = p_name;
  97. if (name.begins_with("keys")) {
  98. int key = name.get_slicec('/', 1).to_int();
  99. if (defaults_dict.has(key) && dict.has(key)) {
  100. int value = dict[key];
  101. Vector3i range = defaults_dict[key];
  102. return range.z != value;
  103. }
  104. }
  105. return false;
  106. }
  107. bool EditorPropertyFontOTObject::_property_get_revert(const StringName &p_name, Variant &r_property) const {
  108. String name = p_name;
  109. if (name.begins_with("keys")) {
  110. int key = name.get_slicec('/', 1).to_int();
  111. if (defaults_dict.has(key)) {
  112. Vector3i range = defaults_dict[key];
  113. r_property = range.z;
  114. return true;
  115. }
  116. }
  117. return false;
  118. }
  119. /*************************************************************************/
  120. /* EditorPropertyFontMetaOverride */
  121. /*************************************************************************/
  122. void EditorPropertyFontMetaOverride::_notification(int p_what) {
  123. switch (p_what) {
  124. case NOTIFICATION_ENTER_TREE:
  125. case NOTIFICATION_THEME_CHANGED: {
  126. if (Object::cast_to<Button>(button_add)) {
  127. button_add->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
  128. }
  129. } break;
  130. }
  131. }
  132. void EditorPropertyFontMetaOverride::_property_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing) {
  133. if (p_property.begins_with("keys")) {
  134. Dictionary dict = object->get_dict();
  135. String key = p_property.get_slice("/", 1);
  136. dict[key] = (bool)p_value;
  137. emit_changed(get_edited_property(), dict, "", true);
  138. dict = dict.duplicate(); // Duplicate, so undo/redo works better.
  139. object->set_dict(dict);
  140. }
  141. }
  142. void EditorPropertyFontMetaOverride::_remove(Object *p_button, const String &p_key) {
  143. Dictionary dict = object->get_dict();
  144. dict.erase(p_key);
  145. emit_changed(get_edited_property(), dict, "", false);
  146. dict = dict.duplicate(); // Duplicate, so undo/redo works better.
  147. object->set_dict(dict);
  148. update_property();
  149. }
  150. void EditorPropertyFontMetaOverride::_add_menu() {
  151. if (script_editor) {
  152. Size2 size = get_size();
  153. menu->set_position(get_screen_position() + Size2(0, size.height * get_global_transform().get_scale().y));
  154. menu->reset_size();
  155. menu->popup();
  156. } else {
  157. locale_select->popup_locale_dialog();
  158. }
  159. }
  160. void EditorPropertyFontMetaOverride::_add_script(int p_option) {
  161. Dictionary dict = object->get_dict();
  162. dict[script_codes[p_option]] = true;
  163. emit_changed(get_edited_property(), dict, "", false);
  164. dict = dict.duplicate(); // Duplicate, so undo/redo works better.
  165. object->set_dict(dict);
  166. update_property();
  167. }
  168. void EditorPropertyFontMetaOverride::_add_lang(const String &p_locale) {
  169. Dictionary dict = object->get_dict();
  170. dict[p_locale] = true;
  171. emit_changed(get_edited_property(), dict, "", false);
  172. dict = dict.duplicate(); // Duplicate, so undo/redo works better.
  173. object->set_dict(dict);
  174. update_property();
  175. }
  176. void EditorPropertyFontMetaOverride::_object_id_selected(const StringName &p_property, ObjectID p_id) {
  177. emit_signal(SNAME("object_id_selected"), p_property, p_id);
  178. }
  179. void EditorPropertyFontMetaOverride::update_property() {
  180. Variant updated_val = get_edited_object()->get(get_edited_property());
  181. Dictionary dict = updated_val;
  182. edit->set_text(vformat(TTR("Overrides (%d)"), dict.size()));
  183. bool unfolded = get_edited_object()->editor_is_section_unfolded(get_edited_property());
  184. if (edit->is_pressed() != unfolded) {
  185. edit->set_pressed(unfolded);
  186. }
  187. if (unfolded) {
  188. updating = true;
  189. if (!container) {
  190. container = memnew(MarginContainer);
  191. container->set_theme_type_variation("MarginContainer4px");
  192. add_child(container);
  193. set_bottom_editor(container);
  194. VBoxContainer *vbox = memnew(VBoxContainer);
  195. vbox->set_v_size_flags(SIZE_EXPAND_FILL);
  196. container->add_child(vbox);
  197. property_vbox = memnew(VBoxContainer);
  198. property_vbox->set_h_size_flags(SIZE_EXPAND_FILL);
  199. vbox->add_child(property_vbox);
  200. paginator = memnew(EditorPaginator);
  201. paginator->connect("page_changed", callable_mp(this, &EditorPropertyFontMetaOverride::_page_changed));
  202. vbox->add_child(paginator);
  203. } else {
  204. // Queue children for deletion, deleting immediately might cause errors.
  205. for (int i = property_vbox->get_child_count() - 1; i >= 0; i--) {
  206. property_vbox->get_child(i)->queue_delete();
  207. }
  208. button_add = nullptr;
  209. }
  210. int size = dict.size();
  211. int max_page = MAX(0, size - 1) / page_length;
  212. page_index = MIN(page_index, max_page);
  213. paginator->update(page_index, max_page);
  214. paginator->set_visible(max_page > 0);
  215. int offset = page_index * page_length;
  216. int amount = MIN(size - offset, page_length);
  217. dict = dict.duplicate();
  218. object->set_dict(dict);
  219. for (int i = 0; i < amount; i++) {
  220. String name = dict.get_key_at_index(i);
  221. EditorProperty *prop = memnew(EditorPropertyCheck);
  222. prop->set_object_and_property(object.ptr(), "keys/" + name);
  223. if (script_editor) {
  224. prop->set_label(TranslationServer::get_singleton()->get_script_name(name));
  225. } else {
  226. prop->set_label(TranslationServer::get_singleton()->get_locale_name(name));
  227. }
  228. prop->set_tooltip_text(name);
  229. prop->set_selectable(false);
  230. prop->connect("property_changed", callable_mp(this, &EditorPropertyFontMetaOverride::_property_changed));
  231. prop->connect("object_id_selected", callable_mp(this, &EditorPropertyFontMetaOverride::_object_id_selected));
  232. HBoxContainer *hbox = memnew(HBoxContainer);
  233. property_vbox->add_child(hbox);
  234. hbox->add_child(prop);
  235. prop->set_h_size_flags(SIZE_EXPAND_FILL);
  236. Button *remove = memnew(Button);
  237. remove->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
  238. hbox->add_child(remove);
  239. remove->connect("pressed", callable_mp(this, &EditorPropertyFontMetaOverride::_remove).bind(remove, name));
  240. prop->update_property();
  241. }
  242. if (script_editor) {
  243. button_add = EditorInspector::create_inspector_action_button(TTR("Add Script"));
  244. } else {
  245. button_add = EditorInspector::create_inspector_action_button(TTR("Add Locale"));
  246. }
  247. button_add->connect("pressed", callable_mp(this, &EditorPropertyFontMetaOverride::_add_menu));
  248. property_vbox->add_child(button_add);
  249. updating = false;
  250. } else {
  251. if (container) {
  252. set_bottom_editor(nullptr);
  253. memdelete(container);
  254. button_add = nullptr;
  255. container = nullptr;
  256. }
  257. }
  258. }
  259. void EditorPropertyFontMetaOverride::_edit_pressed() {
  260. Variant prop_val = get_edited_object()->get(get_edited_property());
  261. if (prop_val.get_type() == Variant::NIL) {
  262. Callable::CallError ce;
  263. Variant::construct(Variant::DICTIONARY, prop_val, nullptr, 0, ce);
  264. get_edited_object()->set(get_edited_property(), prop_val);
  265. }
  266. get_edited_object()->editor_set_section_unfold(get_edited_property(), edit->is_pressed());
  267. update_property();
  268. }
  269. void EditorPropertyFontMetaOverride::_page_changed(int p_page) {
  270. if (updating) {
  271. return;
  272. }
  273. page_index = p_page;
  274. update_property();
  275. }
  276. EditorPropertyFontMetaOverride::EditorPropertyFontMetaOverride(bool p_script) {
  277. script_editor = p_script;
  278. object.instantiate();
  279. page_length = int(EDITOR_GET("interface/inspector/max_array_dictionary_items_per_page"));
  280. edit = memnew(Button);
  281. edit->set_h_size_flags(SIZE_EXPAND_FILL);
  282. edit->set_clip_text(true);
  283. edit->connect("pressed", callable_mp(this, &EditorPropertyFontMetaOverride::_edit_pressed));
  284. edit->set_toggle_mode(true);
  285. add_child(edit);
  286. add_focusable(edit);
  287. menu = memnew(PopupMenu);
  288. if (script_editor) {
  289. script_codes = TranslationServer::get_singleton()->get_all_scripts();
  290. for (int i = 0; i < script_codes.size(); i++) {
  291. menu->add_item(TranslationServer::get_singleton()->get_script_name(script_codes[i]) + " (" + script_codes[i] + ")", i);
  292. }
  293. }
  294. add_child(menu);
  295. menu->connect("id_pressed", callable_mp(this, &EditorPropertyFontMetaOverride::_add_script));
  296. locale_select = memnew(EditorLocaleDialog);
  297. locale_select->connect("locale_selected", callable_mp(this, &EditorPropertyFontMetaOverride::_add_lang));
  298. add_child(locale_select);
  299. }
  300. /*************************************************************************/
  301. /* EditorPropertyOTVariation */
  302. /*************************************************************************/
  303. void EditorPropertyOTVariation::_notification(int p_what) {
  304. switch (p_what) {
  305. case NOTIFICATION_ENTER_TREE:
  306. case NOTIFICATION_THEME_CHANGED: {
  307. } break;
  308. }
  309. }
  310. void EditorPropertyOTVariation::_property_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing) {
  311. if (p_property.begins_with("keys")) {
  312. Dictionary dict = object->get_dict();
  313. Dictionary defaults_dict = object->get_defaults();
  314. int key = p_property.get_slice("/", 1).to_int();
  315. dict[key] = (int)p_value;
  316. if (defaults_dict.has(key)) {
  317. Vector3i range = defaults_dict[key];
  318. if (range.z == (int)p_value) {
  319. dict.erase(key);
  320. }
  321. }
  322. emit_changed(get_edited_property(), dict, "", true);
  323. dict = dict.duplicate(); // Duplicate, so undo/redo works better.
  324. object->set_dict(dict);
  325. }
  326. }
  327. void EditorPropertyOTVariation::_object_id_selected(const StringName &p_property, ObjectID p_id) {
  328. emit_signal(SNAME("object_id_selected"), p_property, p_id);
  329. }
  330. void EditorPropertyOTVariation::update_property() {
  331. Variant updated_val = get_edited_object()->get(get_edited_property());
  332. Dictionary dict = updated_val;
  333. Ref<Font> fd;
  334. if (Object::cast_to<Font>(get_edited_object()) != nullptr) {
  335. fd = get_edited_object();
  336. } else if (Object::cast_to<DynamicFontImportSettingsData>(get_edited_object()) != nullptr) {
  337. Ref<DynamicFontImportSettingsData> imp = Object::cast_to<DynamicFontImportSettingsData>(get_edited_object());
  338. fd = imp->get_font();
  339. }
  340. Dictionary supported = (fd.is_valid()) ? fd->get_supported_variation_list() : Dictionary();
  341. edit->set_text(vformat(TTR("Variation Coordinates (%d)"), supported.size()));
  342. bool unfolded = get_edited_object()->editor_is_section_unfolded(get_edited_property());
  343. if (edit->is_pressed() != unfolded) {
  344. edit->set_pressed(unfolded);
  345. }
  346. if (unfolded) {
  347. updating = true;
  348. if (!container) {
  349. container = memnew(MarginContainer);
  350. container->set_theme_type_variation("MarginContainer4px");
  351. add_child(container);
  352. set_bottom_editor(container);
  353. VBoxContainer *vbox = memnew(VBoxContainer);
  354. vbox->set_v_size_flags(SIZE_EXPAND_FILL);
  355. container->add_child(vbox);
  356. property_vbox = memnew(VBoxContainer);
  357. property_vbox->set_h_size_flags(SIZE_EXPAND_FILL);
  358. vbox->add_child(property_vbox);
  359. paginator = memnew(EditorPaginator);
  360. paginator->connect("page_changed", callable_mp(this, &EditorPropertyOTVariation::_page_changed));
  361. vbox->add_child(paginator);
  362. } else {
  363. // Queue children for deletion, deleting immediately might cause errors.
  364. for (int i = property_vbox->get_child_count() - 1; i >= 0; i--) {
  365. property_vbox->get_child(i)->queue_delete();
  366. }
  367. }
  368. int size = supported.size();
  369. int max_page = MAX(0, size - 1) / page_length;
  370. page_index = MIN(page_index, max_page);
  371. paginator->update(page_index, max_page);
  372. paginator->set_visible(max_page > 0);
  373. int offset = page_index * page_length;
  374. int amount = MIN(size - offset, page_length);
  375. dict = dict.duplicate();
  376. object->set_dict(dict);
  377. object->set_defaults(supported);
  378. for (int i = 0; i < amount; i++) {
  379. int name_tag = supported.get_key_at_index(i);
  380. Vector3i range = supported.get_value_at_index(i);
  381. EditorPropertyInteger *prop = memnew(EditorPropertyInteger);
  382. prop->setup(range.x, range.y, 1, false, false);
  383. prop->set_object_and_property(object.ptr(), "keys/" + itos(name_tag));
  384. String name = TS->tag_to_name(name_tag);
  385. prop->set_label(name.capitalize());
  386. prop->set_tooltip_text(name);
  387. prop->set_selectable(false);
  388. prop->connect("property_changed", callable_mp(this, &EditorPropertyOTVariation::_property_changed));
  389. prop->connect("object_id_selected", callable_mp(this, &EditorPropertyOTVariation::_object_id_selected));
  390. property_vbox->add_child(prop);
  391. prop->update_property();
  392. }
  393. updating = false;
  394. } else {
  395. if (container) {
  396. set_bottom_editor(nullptr);
  397. memdelete(container);
  398. container = nullptr;
  399. }
  400. }
  401. }
  402. void EditorPropertyOTVariation::_edit_pressed() {
  403. Variant prop_val = get_edited_object()->get(get_edited_property());
  404. if (prop_val.get_type() == Variant::NIL) {
  405. Callable::CallError ce;
  406. Variant::construct(Variant::DICTIONARY, prop_val, nullptr, 0, ce);
  407. get_edited_object()->set(get_edited_property(), prop_val);
  408. }
  409. get_edited_object()->editor_set_section_unfold(get_edited_property(), edit->is_pressed());
  410. update_property();
  411. }
  412. void EditorPropertyOTVariation::_page_changed(int p_page) {
  413. if (updating) {
  414. return;
  415. }
  416. page_index = p_page;
  417. update_property();
  418. }
  419. EditorPropertyOTVariation::EditorPropertyOTVariation() {
  420. object.instantiate();
  421. page_length = int(EDITOR_GET("interface/inspector/max_array_dictionary_items_per_page"));
  422. edit = memnew(Button);
  423. edit->set_h_size_flags(SIZE_EXPAND_FILL);
  424. edit->set_clip_text(true);
  425. edit->connect("pressed", callable_mp(this, &EditorPropertyOTVariation::_edit_pressed));
  426. edit->set_toggle_mode(true);
  427. add_child(edit);
  428. add_focusable(edit);
  429. }
  430. /*************************************************************************/
  431. /* EditorPropertyOTFeatures */
  432. /*************************************************************************/
  433. void EditorPropertyOTFeatures::_notification(int p_what) {
  434. switch (p_what) {
  435. case NOTIFICATION_ENTER_TREE:
  436. case NOTIFICATION_THEME_CHANGED: {
  437. if (Object::cast_to<Button>(button_add)) {
  438. button_add->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
  439. }
  440. } break;
  441. }
  442. }
  443. void EditorPropertyOTFeatures::_property_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing) {
  444. if (p_property.begins_with("keys")) {
  445. Dictionary dict = object->get_dict();
  446. int key = p_property.get_slice("/", 1).to_int();
  447. dict[key] = (int)p_value;
  448. emit_changed(get_edited_property(), dict, "", true);
  449. dict = dict.duplicate(); // Duplicate, so undo/redo works better.
  450. object->set_dict(dict);
  451. }
  452. }
  453. void EditorPropertyOTFeatures::_remove(Object *p_button, int p_key) {
  454. Dictionary dict = object->get_dict();
  455. dict.erase(p_key);
  456. emit_changed(get_edited_property(), dict, "", false);
  457. dict = dict.duplicate(); // Duplicate, so undo/redo works better.
  458. object->set_dict(dict);
  459. update_property();
  460. }
  461. void EditorPropertyOTFeatures::_add_menu() {
  462. Size2 size = get_size();
  463. menu->set_position(get_screen_position() + Size2(0, size.height * get_global_transform().get_scale().y));
  464. menu->reset_size();
  465. menu->popup();
  466. }
  467. void EditorPropertyOTFeatures::_add_feature(int p_option) {
  468. Dictionary dict = object->get_dict();
  469. dict[p_option] = 1;
  470. emit_changed(get_edited_property(), dict, "", false);
  471. dict = dict.duplicate(); // Duplicate, so undo/redo works better.
  472. object->set_dict(dict);
  473. update_property();
  474. }
  475. void EditorPropertyOTFeatures::_object_id_selected(const StringName &p_property, ObjectID p_id) {
  476. emit_signal(SNAME("object_id_selected"), p_property, p_id);
  477. }
  478. void EditorPropertyOTFeatures::update_property() {
  479. Variant updated_val = get_edited_object()->get(get_edited_property());
  480. Dictionary dict = updated_val;
  481. Ref<Font> fd;
  482. if (Object::cast_to<FontVariation>(get_edited_object()) != nullptr) {
  483. fd = get_edited_object();
  484. } else if (Object::cast_to<DynamicFontImportSettingsData>(get_edited_object()) != nullptr) {
  485. Ref<DynamicFontImportSettingsData> imp = Object::cast_to<DynamicFontImportSettingsData>(get_edited_object());
  486. fd = imp->get_font();
  487. }
  488. Dictionary supported;
  489. if (fd.is_valid()) {
  490. supported = fd->get_supported_feature_list();
  491. }
  492. edit->set_text(vformat(TTR("Features (%d of %d set)"), dict.size(), supported.size()));
  493. bool unfolded = get_edited_object()->editor_is_section_unfolded(get_edited_property());
  494. if (edit->is_pressed() != unfolded) {
  495. edit->set_pressed(unfolded);
  496. }
  497. if (unfolded) {
  498. updating = true;
  499. if (!container) {
  500. container = memnew(MarginContainer);
  501. container->set_theme_type_variation("MarginContainer4px");
  502. add_child(container);
  503. set_bottom_editor(container);
  504. VBoxContainer *vbox = memnew(VBoxContainer);
  505. vbox->set_v_size_flags(SIZE_EXPAND_FILL);
  506. container->add_child(vbox);
  507. property_vbox = memnew(VBoxContainer);
  508. property_vbox->set_h_size_flags(SIZE_EXPAND_FILL);
  509. vbox->add_child(property_vbox);
  510. paginator = memnew(EditorPaginator);
  511. paginator->connect("page_changed", callable_mp(this, &EditorPropertyOTFeatures::_page_changed));
  512. vbox->add_child(paginator);
  513. } else {
  514. // Queue children for deletion, deleting immediately might cause errors.
  515. for (int i = property_vbox->get_child_count() - 1; i >= 0; i--) {
  516. property_vbox->get_child(i)->queue_delete();
  517. }
  518. button_add = nullptr;
  519. }
  520. // Update add menu items.
  521. menu->clear();
  522. bool have_sub[FGRP_MAX];
  523. for (int i = 0; i < FGRP_MAX; i++) {
  524. menu_sub[i]->clear();
  525. have_sub[i] = false;
  526. }
  527. bool show_hidden = EDITOR_GET("interface/inspector/show_low_level_opentype_features");
  528. for (int i = 0; i < supported.size(); i++) {
  529. int name_tag = supported.get_key_at_index(i);
  530. Dictionary info = supported.get_value_at_index(i);
  531. bool hidden = info["hidden"].operator bool();
  532. String name = TS->tag_to_name(name_tag);
  533. FeatureGroups grp = FGRP_MAX;
  534. if (hidden && !show_hidden) {
  535. continue;
  536. }
  537. if (name.begins_with("stylistic_set_")) {
  538. grp = FGRP_STYLISTIC_SET;
  539. } else if (name.begins_with("character_variant_")) {
  540. grp = FGRP_CHARACTER_VARIANT;
  541. } else if (name.ends_with("_capitals")) {
  542. grp = FGRP_CAPITLS;
  543. } else if (name.ends_with("_ligatures")) {
  544. grp = FGRP_LIGATURES;
  545. } else if (name.ends_with("_alternates")) {
  546. grp = FGRP_ALTERNATES;
  547. } else if (name.ends_with("_kanji_forms") || name.begins_with("jis") || name == "simplified_forms" || name == "traditional_name_forms" || name == "traditional_forms") {
  548. grp = FGRP_EAL;
  549. } else if (name.ends_with("_widths")) {
  550. grp = FGRP_EAW;
  551. } else if (name == "tabular_figures" || name == "proportional_figures") {
  552. grp = FGRP_NUMAL;
  553. } else if (name.begins_with("custom_")) {
  554. grp = FGRP_CUSTOM;
  555. }
  556. String disp_name = name.capitalize();
  557. if (info.has("label")) {
  558. disp_name = vformat("%s (%s)", disp_name, info["label"].operator String());
  559. }
  560. if (grp == FGRP_MAX) {
  561. menu->add_item(disp_name, name_tag);
  562. } else {
  563. menu_sub[grp]->add_item(disp_name, name_tag);
  564. have_sub[grp] = true;
  565. }
  566. }
  567. for (int i = 0; i < FGRP_MAX; i++) {
  568. if (have_sub[i]) {
  569. menu->add_submenu_item(RTR(group_names[i]), "FTRMenu_" + itos(i));
  570. }
  571. }
  572. int size = dict.size();
  573. int max_page = MAX(0, size - 1) / page_length;
  574. page_index = MIN(page_index, max_page);
  575. paginator->update(page_index, max_page);
  576. paginator->set_visible(max_page > 0);
  577. int offset = page_index * page_length;
  578. int amount = MIN(size - offset, page_length);
  579. dict = dict.duplicate();
  580. object->set_dict(dict);
  581. for (int i = 0; i < amount; i++) {
  582. int name_tag = dict.get_key_at_index(i);
  583. if (supported.has(name_tag)) {
  584. Dictionary info = supported[name_tag];
  585. Variant::Type vtype = Variant::Type(info["type"].operator int());
  586. bool hidden = info["hidden"].operator bool();
  587. if (hidden && !show_hidden) {
  588. continue;
  589. }
  590. EditorProperty *prop = nullptr;
  591. switch (vtype) {
  592. case Variant::NIL: {
  593. prop = memnew(EditorPropertyNil);
  594. } break;
  595. case Variant::BOOL: {
  596. prop = memnew(EditorPropertyCheck);
  597. } break;
  598. case Variant::INT: {
  599. EditorPropertyInteger *editor = memnew(EditorPropertyInteger);
  600. editor->setup(0, 255, 1, false, false);
  601. prop = editor;
  602. } break;
  603. default: {
  604. ERR_CONTINUE_MSG(true, vformat("Unsupported OT feature data type %s", Variant::get_type_name(vtype)));
  605. }
  606. }
  607. prop->set_object_and_property(object.ptr(), "keys/" + itos(name_tag));
  608. String name = TS->tag_to_name(name_tag);
  609. String disp_name = name.capitalize();
  610. if (info.has("label")) {
  611. disp_name = vformat("%s (%s)", disp_name, info["label"].operator String());
  612. }
  613. prop->set_label(disp_name);
  614. prop->set_tooltip_text(name);
  615. prop->set_selectable(false);
  616. prop->connect("property_changed", callable_mp(this, &EditorPropertyOTFeatures::_property_changed));
  617. prop->connect("object_id_selected", callable_mp(this, &EditorPropertyOTFeatures::_object_id_selected));
  618. HBoxContainer *hbox = memnew(HBoxContainer);
  619. property_vbox->add_child(hbox);
  620. hbox->add_child(prop);
  621. prop->set_h_size_flags(SIZE_EXPAND_FILL);
  622. Button *remove = memnew(Button);
  623. remove->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
  624. hbox->add_child(remove);
  625. remove->connect("pressed", callable_mp(this, &EditorPropertyOTFeatures::_remove).bind(remove, name_tag));
  626. prop->update_property();
  627. }
  628. }
  629. button_add = EditorInspector::create_inspector_action_button(TTR("Add Feature"));
  630. button_add->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
  631. button_add->connect("pressed", callable_mp(this, &EditorPropertyOTFeatures::_add_menu));
  632. property_vbox->add_child(button_add);
  633. updating = false;
  634. } else {
  635. if (container) {
  636. set_bottom_editor(nullptr);
  637. memdelete(container);
  638. button_add = nullptr;
  639. container = nullptr;
  640. }
  641. }
  642. }
  643. void EditorPropertyOTFeatures::_edit_pressed() {
  644. Variant prop_val = get_edited_object()->get(get_edited_property());
  645. if (prop_val.get_type() == Variant::NIL) {
  646. Callable::CallError ce;
  647. Variant::construct(Variant::DICTIONARY, prop_val, nullptr, 0, ce);
  648. get_edited_object()->set(get_edited_property(), prop_val);
  649. }
  650. get_edited_object()->editor_set_section_unfold(get_edited_property(), edit->is_pressed());
  651. update_property();
  652. }
  653. void EditorPropertyOTFeatures::_page_changed(int p_page) {
  654. if (updating) {
  655. return;
  656. }
  657. page_index = p_page;
  658. update_property();
  659. }
  660. EditorPropertyOTFeatures::EditorPropertyOTFeatures() {
  661. object.instantiate();
  662. page_length = int(EDITOR_GET("interface/inspector/max_array_dictionary_items_per_page"));
  663. edit = memnew(Button);
  664. edit->set_h_size_flags(SIZE_EXPAND_FILL);
  665. edit->set_clip_text(true);
  666. edit->connect("pressed", callable_mp(this, &EditorPropertyOTFeatures::_edit_pressed));
  667. edit->set_toggle_mode(true);
  668. add_child(edit);
  669. add_focusable(edit);
  670. menu = memnew(PopupMenu);
  671. add_child(menu);
  672. menu->connect("id_pressed", callable_mp(this, &EditorPropertyOTFeatures::_add_feature));
  673. for (int i = 0; i < FGRP_MAX; i++) {
  674. menu_sub[i] = memnew(PopupMenu);
  675. menu_sub[i]->set_name("FTRMenu_" + itos(i));
  676. menu->add_child(menu_sub[i]);
  677. menu_sub[i]->connect("id_pressed", callable_mp(this, &EditorPropertyOTFeatures::_add_feature));
  678. }
  679. group_names[FGRP_STYLISTIC_SET] = "Stylistic Sets";
  680. group_names[FGRP_CHARACTER_VARIANT] = "Character Variants";
  681. group_names[FGRP_CAPITLS] = "Capitals";
  682. group_names[FGRP_LIGATURES] = "Ligatures";
  683. group_names[FGRP_ALTERNATES] = "Alternates";
  684. group_names[FGRP_EAL] = "East Asian Language";
  685. group_names[FGRP_EAW] = "East Asian Widths";
  686. group_names[FGRP_NUMAL] = "Numeral Alignment";
  687. group_names[FGRP_CUSTOM] = "Custom";
  688. }
  689. /*************************************************************************/
  690. /* EditorInspectorPluginFontVariation */
  691. /*************************************************************************/
  692. bool EditorInspectorPluginFontVariation::can_handle(Object *p_object) {
  693. return (Object::cast_to<FontVariation>(p_object) != nullptr) || (Object::cast_to<DynamicFontImportSettingsData>(p_object) != nullptr);
  694. }
  695. 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) {
  696. if (p_path == "variation_opentype") {
  697. add_property_editor(p_path, memnew(EditorPropertyOTVariation));
  698. return true;
  699. } else if (p_path == "opentype_features") {
  700. add_property_editor(p_path, memnew(EditorPropertyOTFeatures));
  701. return true;
  702. } else if (p_path == "language_support") {
  703. add_property_editor(p_path, memnew(EditorPropertyFontMetaOverride(false)));
  704. return true;
  705. } else if (p_path == "script_support") {
  706. add_property_editor(p_path, memnew(EditorPropertyFontMetaOverride(true)));
  707. return true;
  708. }
  709. return false;
  710. }
  711. /*************************************************************************/
  712. /* FontPreview */
  713. /*************************************************************************/
  714. void FontPreview::_notification(int p_what) {
  715. switch (p_what) {
  716. case NOTIFICATION_DRAW: {
  717. // Draw font name (style).
  718. Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label"));
  719. int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label"));
  720. Color text_color = get_theme_color(SNAME("font_color"), SNAME("Label"));
  721. // Draw font preview.
  722. bool prev_ok = true;
  723. if (prev_font.is_valid()) {
  724. if (prev_font->get_font_name().is_empty()) {
  725. prev_ok = false;
  726. } else {
  727. String name;
  728. if (prev_font->get_font_style_name().is_empty()) {
  729. name = prev_font->get_font_name();
  730. } else {
  731. name = vformat("%s (%s)", prev_font->get_font_name(), prev_font->get_font_style_name());
  732. }
  733. if (prev_font->is_class("FontVariation")) {
  734. name += " " + TTR(" - Variation");
  735. }
  736. 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);
  737. String sample;
  738. static const String sample_base = U"12漢字ԱբΑαАбΑαאבابܐܒހށआআਆઆଆஆఆಆആආกิກິༀကႠა한글ሀᎣᐁᚁᚠᜀᜠᝀᝠកᠠᤁᥐAb😀";
  739. for (int i = 0; i < sample_base.length(); i++) {
  740. if (prev_font->has_char(sample_base[i])) {
  741. sample += sample_base[i];
  742. }
  743. }
  744. if (sample.is_empty()) {
  745. sample = prev_font->get_supported_chars().substr(0, 6);
  746. }
  747. if (sample.is_empty()) {
  748. prev_ok = false;
  749. } else {
  750. prev_font->draw_string(get_canvas_item(), Point2(0, font->get_height(font_size) + prev_font->get_height(25 * EDSCALE)), sample, HORIZONTAL_ALIGNMENT_CENTER, get_size().x, 25 * EDSCALE, text_color);
  751. }
  752. }
  753. }
  754. if (!prev_ok) {
  755. text_color.a *= 0.5;
  756. font->draw_string(get_canvas_item(), Point2(0, font->get_height(font_size) + 2 * EDSCALE), TTR("Unable to preview font"), HORIZONTAL_ALIGNMENT_CENTER, get_size().x, font_size, text_color);
  757. }
  758. } break;
  759. }
  760. }
  761. void FontPreview::_bind_methods() {}
  762. Size2 FontPreview::get_minimum_size() const {
  763. return Vector2(64, 64) * EDSCALE;
  764. }
  765. void FontPreview::set_data(const Ref<Font> &p_f) {
  766. prev_font = p_f;
  767. queue_redraw();
  768. }
  769. FontPreview::FontPreview() {
  770. }
  771. /*************************************************************************/
  772. /* EditorInspectorPluginFontPreview */
  773. /*************************************************************************/
  774. bool EditorInspectorPluginFontPreview::can_handle(Object *p_object) {
  775. return Object::cast_to<Font>(p_object) != nullptr;
  776. }
  777. void EditorInspectorPluginFontPreview::parse_begin(Object *p_object) {
  778. Font *fd = Object::cast_to<Font>(p_object);
  779. ERR_FAIL_COND(!fd);
  780. FontPreview *editor = memnew(FontPreview);
  781. editor->set_data(fd);
  782. add_custom_control(editor);
  783. }
  784. 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) {
  785. return false;
  786. }
  787. /*************************************************************************/
  788. /* EditorPropertyFontNamesArray */
  789. /*************************************************************************/
  790. void EditorPropertyFontNamesArray::_add_element() {
  791. Size2 size = get_size();
  792. menu->set_position(get_screen_position() + Size2(0, size.height * get_global_transform().get_scale().y));
  793. menu->reset_size();
  794. menu->popup();
  795. }
  796. void EditorPropertyFontNamesArray::_add_font(int p_option) {
  797. if (updating) {
  798. return;
  799. }
  800. Variant array = object->get_array();
  801. int previous_size = array.call("size");
  802. array.call("resize", previous_size + 1);
  803. array.set(previous_size, menu->get_item_text(p_option));
  804. emit_changed(get_edited_property(), array, "", false);
  805. object->set_array(array);
  806. update_property();
  807. }
  808. EditorPropertyFontNamesArray::EditorPropertyFontNamesArray() {
  809. menu = memnew(PopupMenu);
  810. menu->add_item("Sans-Serif", 0);
  811. menu->add_item("Serif", 1);
  812. menu->add_item("Monospace", 2);
  813. menu->add_item("Fantasy", 3);
  814. menu->add_item("Cursive", 4);
  815. menu->add_separator();
  816. if (OS::get_singleton()) {
  817. Vector<String> fonts = OS::get_singleton()->get_system_fonts();
  818. for (int i = 0; i < fonts.size(); i++) {
  819. menu->add_item(fonts[i], i + 6);
  820. }
  821. }
  822. add_child(menu);
  823. menu->connect("id_pressed", callable_mp(this, &EditorPropertyFontNamesArray::_add_font));
  824. }
  825. /*************************************************************************/
  826. /* EditorInspectorPluginSystemFont */
  827. /*************************************************************************/
  828. bool EditorInspectorPluginSystemFont::can_handle(Object *p_object) {
  829. return Object::cast_to<SystemFont>(p_object) != nullptr;
  830. }
  831. bool EditorInspectorPluginSystemFont::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) {
  832. if (p_path == "font_names") {
  833. EditorPropertyFontNamesArray *editor = memnew(EditorPropertyFontNamesArray);
  834. editor->setup(p_type, p_hint_text);
  835. add_property_editor(p_path, editor);
  836. return true;
  837. }
  838. return false;
  839. }
  840. /*************************************************************************/
  841. /* FontEditorPlugin */
  842. /*************************************************************************/
  843. FontEditorPlugin::FontEditorPlugin() {
  844. Ref<EditorInspectorPluginFontVariation> fc_plugin;
  845. fc_plugin.instantiate();
  846. EditorInspector::add_inspector_plugin(fc_plugin);
  847. Ref<EditorInspectorPluginSystemFont> fs_plugin;
  848. fs_plugin.instantiate();
  849. EditorInspector::add_inspector_plugin(fs_plugin);
  850. Ref<EditorInspectorPluginFontPreview> fp_plugin;
  851. fp_plugin.instantiate();
  852. EditorInspector::add_inspector_plugin(fp_plugin);
  853. }