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