font_config_plugin.cpp 35 KB

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