font_config_plugin.cpp 35 KB

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