Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/editor/scene/gui/theme_editor_plugin.cpp
9902 views
1
/**************************************************************************/
2
/* theme_editor_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 "theme_editor_plugin.h"
32
33
#include "editor/doc/editor_help.h"
34
#include "editor/docks/filesystem_dock.h"
35
#include "editor/docks/inspector_dock.h"
36
#include "editor/editor_node.h"
37
#include "editor/editor_string_names.h"
38
#include "editor/editor_undo_redo_manager.h"
39
#include "editor/file_system/editor_file_system.h"
40
#include "editor/gui/editor_bottom_panel.h"
41
#include "editor/gui/editor_file_dialog.h"
42
#include "editor/gui/progress_dialog.h"
43
#include "editor/inspector/editor_resource_picker.h"
44
#include "editor/settings/editor_command_palette.h"
45
#include "editor/themes/editor_scale.h"
46
#include "scene/gui/check_button.h"
47
#include "scene/gui/color_picker.h"
48
#include "scene/gui/item_list.h"
49
#include "scene/gui/option_button.h"
50
#include "scene/gui/panel_container.h"
51
#include "scene/gui/scroll_container.h"
52
#include "scene/gui/separator.h"
53
#include "scene/gui/spin_box.h"
54
#include "scene/gui/split_container.h"
55
#include "scene/gui/tab_bar.h"
56
#include "scene/gui/tab_container.h"
57
#include "scene/gui/texture_rect.h"
58
#include "scene/theme/theme_db.h"
59
60
static void _rename_theme_type(EditorUndoRedoManager *p_ur, Theme *p_theme, const String &p_old_theme_type, const String &p_new_theme_type) {
61
p_ur->add_do_method(p_theme, "rename_type", p_old_theme_type, p_new_theme_type);
62
p_ur->add_undo_method(p_theme, "rename_type", p_new_theme_type, p_old_theme_type);
63
64
// Renaming a theme type to an empty name or a variation to a type associated with a built-in class
65
// removes type variation connections in a way that cannot be undone by reversing the rename alone.
66
const StringName old_base_type = p_theme->get_type_variation_base(p_old_theme_type);
67
if ((!p_old_theme_type.is_empty() && p_new_theme_type.is_empty()) || (old_base_type != StringName() && ClassDB::class_exists(p_new_theme_type))) {
68
if (old_base_type != StringName()) {
69
p_ur->add_undo_method(p_theme, "set_type_variation", p_old_theme_type, old_base_type);
70
}
71
72
List<StringName> names;
73
p_theme->get_type_variation_list(p_old_theme_type, &names);
74
for (const StringName &E : names) {
75
p_ur->add_undo_method(p_theme, "set_type_variation", E, p_old_theme_type);
76
}
77
}
78
}
79
80
///////////////////////
81
82
void ThemeItemImportTree::_update_items_tree() {
83
import_items_tree->clear();
84
TreeItem *root = import_items_tree->create_item();
85
86
if (base_theme.is_null()) {
87
return;
88
}
89
90
String filter_text = import_items_filter->get_text();
91
92
List<StringName> types;
93
List<StringName> names;
94
List<StringName> filtered_names;
95
base_theme->get_type_list(&types);
96
types.sort_custom<StringName::AlphCompare>();
97
98
int color_amount = 0;
99
int constant_amount = 0;
100
int font_amount = 0;
101
int font_size_amount = 0;
102
int icon_amount = 0;
103
int stylebox_amount = 0;
104
105
tree_color_items.clear();
106
tree_constant_items.clear();
107
tree_font_items.clear();
108
tree_font_size_items.clear();
109
tree_icon_items.clear();
110
tree_stylebox_items.clear();
111
112
for (const StringName &E : types) {
113
String type_name = (String)E;
114
115
Ref<Texture2D> type_icon;
116
if (E == "") {
117
type_icon = get_editor_theme_icon(SNAME("NodeDisabled"));
118
} else {
119
type_icon = EditorNode::get_singleton()->get_class_icon(E, "NodeDisabled");
120
}
121
122
TreeItem *type_node = import_items_tree->create_item(root);
123
type_node->set_meta("_can_be_imported", false);
124
type_node->set_collapsed(true);
125
type_node->set_icon(0, type_icon);
126
type_node->set_text(0, type_name);
127
type_node->set_cell_mode(IMPORT_ITEM, TreeItem::CELL_MODE_CHECK);
128
type_node->set_checked(IMPORT_ITEM, false);
129
type_node->set_editable(IMPORT_ITEM, true);
130
type_node->set_cell_mode(IMPORT_ITEM_DATA, TreeItem::CELL_MODE_CHECK);
131
type_node->set_checked(IMPORT_ITEM_DATA, false);
132
type_node->set_editable(IMPORT_ITEM_DATA, true);
133
134
bool is_matching_filter = (filter_text.is_empty() || type_name.containsn(filter_text));
135
bool has_filtered_items = false;
136
137
for (int i = 0; i < Theme::DATA_TYPE_MAX; i++) {
138
Theme::DataType dt = (Theme::DataType)i;
139
140
names.clear();
141
filtered_names.clear();
142
base_theme->get_theme_item_list(dt, E, &names);
143
144
bool data_type_has_filtered_items = false;
145
146
for (const StringName &F : names) {
147
String item_name = (String)F;
148
bool is_item_matching_filter = item_name.containsn(filter_text);
149
if (!filter_text.is_empty() && !is_matching_filter && !is_item_matching_filter) {
150
continue;
151
}
152
153
// Only mark this if actual items match the filter and not just the type group.
154
if (!filter_text.is_empty() && is_item_matching_filter) {
155
has_filtered_items = true;
156
data_type_has_filtered_items = true;
157
}
158
filtered_names.push_back(F);
159
}
160
161
if (filtered_names.is_empty()) {
162
continue;
163
}
164
165
TreeItem *data_type_node = import_items_tree->create_item(type_node);
166
data_type_node->set_meta("_can_be_imported", false);
167
data_type_node->set_metadata(0, i);
168
data_type_node->set_collapsed(!data_type_has_filtered_items);
169
data_type_node->set_cell_mode(IMPORT_ITEM, TreeItem::CELL_MODE_CHECK);
170
data_type_node->set_checked(IMPORT_ITEM, false);
171
data_type_node->set_editable(IMPORT_ITEM, true);
172
data_type_node->set_cell_mode(IMPORT_ITEM_DATA, TreeItem::CELL_MODE_CHECK);
173
data_type_node->set_checked(IMPORT_ITEM_DATA, false);
174
data_type_node->set_editable(IMPORT_ITEM_DATA, true);
175
176
List<TreeItem *> *item_list = nullptr;
177
178
switch (dt) {
179
case Theme::DATA_TYPE_COLOR:
180
data_type_node->set_icon(0, get_editor_theme_icon(SNAME("Color")));
181
data_type_node->set_text(0, TTR("Colors"));
182
183
item_list = &tree_color_items;
184
color_amount += filtered_names.size();
185
break;
186
187
case Theme::DATA_TYPE_CONSTANT:
188
data_type_node->set_icon(0, get_editor_theme_icon(SNAME("MemberConstant")));
189
data_type_node->set_text(0, TTR("Constants"));
190
191
item_list = &tree_constant_items;
192
constant_amount += filtered_names.size();
193
break;
194
195
case Theme::DATA_TYPE_FONT:
196
data_type_node->set_icon(0, get_editor_theme_icon(SNAME("FontItem")));
197
data_type_node->set_text(0, TTR("Fonts"));
198
199
item_list = &tree_font_items;
200
font_amount += filtered_names.size();
201
break;
202
203
case Theme::DATA_TYPE_FONT_SIZE:
204
data_type_node->set_icon(0, get_editor_theme_icon(SNAME("FontSize")));
205
data_type_node->set_text(0, TTR("Font Sizes"));
206
207
item_list = &tree_font_size_items;
208
font_size_amount += filtered_names.size();
209
break;
210
211
case Theme::DATA_TYPE_ICON:
212
data_type_node->set_icon(0, get_editor_theme_icon(SNAME("ImageTexture")));
213
data_type_node->set_text(0, TTR("Icons"));
214
215
item_list = &tree_icon_items;
216
icon_amount += filtered_names.size();
217
break;
218
219
case Theme::DATA_TYPE_STYLEBOX:
220
data_type_node->set_icon(0, get_editor_theme_icon(SNAME("StyleBoxFlat")));
221
data_type_node->set_text(0, TTR("Styleboxes"));
222
223
item_list = &tree_stylebox_items;
224
stylebox_amount += filtered_names.size();
225
break;
226
227
case Theme::DATA_TYPE_MAX:
228
break; // Can't happen, but silences warning.
229
}
230
231
filtered_names.sort_custom<StringName::AlphCompare>();
232
for (const StringName &F : filtered_names) {
233
TreeItem *item_node = import_items_tree->create_item(data_type_node);
234
item_node->set_meta("_can_be_imported", true);
235
item_node->set_text(0, F);
236
item_node->set_cell_mode(IMPORT_ITEM, TreeItem::CELL_MODE_CHECK);
237
item_node->set_checked(IMPORT_ITEM, false);
238
item_node->set_editable(IMPORT_ITEM, true);
239
item_node->set_cell_mode(IMPORT_ITEM_DATA, TreeItem::CELL_MODE_CHECK);
240
item_node->set_checked(IMPORT_ITEM_DATA, false);
241
item_node->set_editable(IMPORT_ITEM_DATA, true);
242
243
_restore_selected_item(item_node);
244
item_node->propagate_check(IMPORT_ITEM, false);
245
item_node->propagate_check(IMPORT_ITEM_DATA, false);
246
247
item_list->push_back(item_node);
248
}
249
}
250
251
// Remove the item if it doesn't match the filter in any way.
252
if (!is_matching_filter && !has_filtered_items) {
253
root->remove_child(type_node);
254
memdelete(type_node);
255
continue;
256
}
257
258
// Show one level inside of a type group if there are matches in items.
259
if (!filter_text.is_empty() && has_filtered_items) {
260
type_node->set_collapsed(false);
261
}
262
}
263
264
if (color_amount > 0) {
265
Array arr = { color_amount };
266
select_colors_label->set_text(TTRN("1 color", "{num} colors", color_amount).format(arr, "{num}"));
267
select_all_colors_button->set_visible(true);
268
select_full_colors_button->set_visible(true);
269
deselect_all_colors_button->set_visible(true);
270
} else {
271
select_colors_label->set_text(TTR("No colors found."));
272
select_all_colors_button->set_visible(false);
273
select_full_colors_button->set_visible(false);
274
deselect_all_colors_button->set_visible(false);
275
}
276
277
if (constant_amount > 0) {
278
Array arr = { constant_amount };
279
select_constants_label->set_text(TTRN("1 constant", "{num} constants", constant_amount).format(arr, "{num}"));
280
select_all_constants_button->set_visible(true);
281
select_full_constants_button->set_visible(true);
282
deselect_all_constants_button->set_visible(true);
283
} else {
284
select_constants_label->set_text(TTR("No constants found."));
285
select_all_constants_button->set_visible(false);
286
select_full_constants_button->set_visible(false);
287
deselect_all_constants_button->set_visible(false);
288
}
289
290
if (font_amount > 0) {
291
Array arr = { font_amount };
292
select_fonts_label->set_text(TTRN("1 font", "{num} fonts", font_amount).format(arr, "{num}"));
293
select_all_fonts_button->set_visible(true);
294
select_full_fonts_button->set_visible(true);
295
deselect_all_fonts_button->set_visible(true);
296
} else {
297
select_fonts_label->set_text(TTR("No fonts found."));
298
select_all_fonts_button->set_visible(false);
299
select_full_fonts_button->set_visible(false);
300
deselect_all_fonts_button->set_visible(false);
301
}
302
303
if (font_size_amount > 0) {
304
Array arr = { font_size_amount };
305
select_font_sizes_label->set_text(TTRN("1 font size", "{num} font sizes", font_size_amount).format(arr, "{num}"));
306
select_all_font_sizes_button->set_visible(true);
307
select_full_font_sizes_button->set_visible(true);
308
deselect_all_font_sizes_button->set_visible(true);
309
} else {
310
select_font_sizes_label->set_text(TTR("No font sizes found."));
311
select_all_font_sizes_button->set_visible(false);
312
select_full_font_sizes_button->set_visible(false);
313
deselect_all_font_sizes_button->set_visible(false);
314
}
315
316
if (icon_amount > 0) {
317
Array arr = { icon_amount };
318
select_icons_label->set_text(TTRN("1 icon", "{num} icons", icon_amount).format(arr, "{num}"));
319
select_all_icons_button->set_visible(true);
320
select_full_icons_button->set_visible(true);
321
deselect_all_icons_button->set_visible(true);
322
select_icons_warning_hb->set_visible(true);
323
} else {
324
select_icons_label->set_text(TTR("No icons found."));
325
select_all_icons_button->set_visible(false);
326
select_full_icons_button->set_visible(false);
327
deselect_all_icons_button->set_visible(false);
328
select_icons_warning_hb->set_visible(false);
329
}
330
331
if (stylebox_amount > 0) {
332
Array arr = { stylebox_amount };
333
select_styleboxes_label->set_text(TTRN("1 stylebox", "{num} styleboxes", stylebox_amount).format(arr, "{num}"));
334
select_all_styleboxes_button->set_visible(true);
335
select_full_styleboxes_button->set_visible(true);
336
deselect_all_styleboxes_button->set_visible(true);
337
} else {
338
select_styleboxes_label->set_text(TTR("No styleboxes found."));
339
select_all_styleboxes_button->set_visible(false);
340
select_full_styleboxes_button->set_visible(false);
341
deselect_all_styleboxes_button->set_visible(false);
342
}
343
}
344
345
void ThemeItemImportTree::_toggle_type_items(bool p_collapse) {
346
TreeItem *root = import_items_tree->get_root();
347
if (!root) {
348
return;
349
}
350
351
TreeItem *type_node = root->get_first_child();
352
while (type_node) {
353
type_node->set_collapsed(p_collapse);
354
type_node = type_node->get_next();
355
}
356
}
357
358
void ThemeItemImportTree::_filter_text_changed(const String &p_value) {
359
_update_items_tree();
360
}
361
362
void ThemeItemImportTree::_store_selected_item(TreeItem *p_tree_item) {
363
if (!p_tree_item->get_meta("_can_be_imported")) {
364
return;
365
}
366
367
TreeItem *data_type_node = p_tree_item->get_parent();
368
if (!data_type_node || data_type_node == import_items_tree->get_root()) {
369
return;
370
}
371
372
TreeItem *type_node = data_type_node->get_parent();
373
if (!type_node || type_node == import_items_tree->get_root()) {
374
return;
375
}
376
377
ThemeItem ti;
378
ti.item_name = p_tree_item->get_text(0);
379
ti.data_type = (Theme::DataType)(int)data_type_node->get_metadata(0);
380
ti.type_name = type_node->get_text(0);
381
382
bool import = p_tree_item->is_checked(IMPORT_ITEM);
383
bool with_data = p_tree_item->is_checked(IMPORT_ITEM_DATA);
384
385
if (import && with_data) {
386
selected_items[ti] = SELECT_IMPORT_FULL;
387
} else if (import) {
388
selected_items[ti] = SELECT_IMPORT_DEFINITION;
389
} else {
390
selected_items.erase(ti);
391
}
392
393
_update_total_selected(ti.data_type);
394
}
395
396
void ThemeItemImportTree::_restore_selected_item(TreeItem *p_tree_item) {
397
if (!p_tree_item->get_meta("_can_be_imported")) {
398
return;
399
}
400
401
TreeItem *data_type_node = p_tree_item->get_parent();
402
if (!data_type_node || data_type_node == import_items_tree->get_root()) {
403
return;
404
}
405
406
TreeItem *type_node = data_type_node->get_parent();
407
if (!type_node || type_node == import_items_tree->get_root()) {
408
return;
409
}
410
411
ThemeItem ti;
412
ti.item_name = p_tree_item->get_text(0);
413
ti.data_type = (Theme::DataType)(int)data_type_node->get_metadata(0);
414
ti.type_name = type_node->get_text(0);
415
416
if (!selected_items.has(ti)) {
417
p_tree_item->set_checked(IMPORT_ITEM, false);
418
p_tree_item->set_checked(IMPORT_ITEM_DATA, false);
419
return;
420
}
421
422
if (selected_items[ti] == SELECT_IMPORT_FULL) {
423
p_tree_item->set_checked(IMPORT_ITEM, true);
424
p_tree_item->set_checked(IMPORT_ITEM_DATA, true);
425
} else if (selected_items[ti] == SELECT_IMPORT_DEFINITION) {
426
p_tree_item->set_checked(IMPORT_ITEM, true);
427
p_tree_item->set_checked(IMPORT_ITEM_DATA, false);
428
}
429
}
430
431
void ThemeItemImportTree::_update_total_selected(Theme::DataType p_data_type) {
432
ERR_FAIL_INDEX_MSG(p_data_type, Theme::DATA_TYPE_MAX, "Theme item data type is out of bounds.");
433
434
Label *total_selected_items_label = nullptr;
435
switch (p_data_type) {
436
case Theme::DATA_TYPE_COLOR:
437
total_selected_items_label = total_selected_colors_label;
438
break;
439
440
case Theme::DATA_TYPE_CONSTANT:
441
total_selected_items_label = total_selected_constants_label;
442
break;
443
444
case Theme::DATA_TYPE_FONT:
445
total_selected_items_label = total_selected_fonts_label;
446
break;
447
448
case Theme::DATA_TYPE_FONT_SIZE:
449
total_selected_items_label = total_selected_font_sizes_label;
450
break;
451
452
case Theme::DATA_TYPE_ICON:
453
total_selected_items_label = total_selected_icons_label;
454
break;
455
456
case Theme::DATA_TYPE_STYLEBOX:
457
total_selected_items_label = total_selected_styleboxes_label;
458
break;
459
460
case Theme::DATA_TYPE_MAX:
461
return; // Can't happen, but silences warning.
462
}
463
464
if (!total_selected_items_label) {
465
return;
466
}
467
468
int count = 0;
469
for (const KeyValue<ThemeItem, ItemCheckedState> &E : selected_items) {
470
ThemeItem ti = E.key;
471
if (ti.data_type == p_data_type) {
472
count++;
473
}
474
}
475
476
if (count == 0) {
477
total_selected_items_label->hide();
478
} else {
479
Array arr = { count };
480
total_selected_items_label->set_text(TTRN("{num} currently selected", "{num} currently selected", count).format(arr, "{num}"));
481
total_selected_items_label->show();
482
}
483
}
484
485
void ThemeItemImportTree::_tree_item_edited() {
486
if (updating_tree) {
487
return;
488
}
489
490
TreeItem *edited_item = import_items_tree->get_edited();
491
if (!edited_item) {
492
return;
493
}
494
495
updating_tree = true;
496
497
int edited_column = import_items_tree->get_edited_column();
498
bool is_checked = edited_item->is_checked(edited_column);
499
if (is_checked) {
500
if (edited_column == IMPORT_ITEM_DATA) {
501
edited_item->set_checked(IMPORT_ITEM, true);
502
edited_item->propagate_check(IMPORT_ITEM);
503
}
504
} else {
505
if (edited_column == IMPORT_ITEM) {
506
edited_item->set_checked(IMPORT_ITEM_DATA, false);
507
edited_item->propagate_check(IMPORT_ITEM_DATA);
508
}
509
}
510
edited_item->propagate_check(edited_column);
511
updating_tree = false;
512
}
513
514
void ThemeItemImportTree::_check_propagated_to_tree_item(Object *p_obj, int p_column) {
515
TreeItem *item = Object::cast_to<TreeItem>(p_obj);
516
// Skip "category" tree items by checking for children.
517
if (item && !item->get_first_child()) {
518
_store_selected_item(item);
519
}
520
}
521
522
void ThemeItemImportTree::_select_all_subitems(TreeItem *p_root_item, bool p_select_with_data) {
523
TreeItem *child_item = p_root_item->get_first_child();
524
while (child_item) {
525
child_item->set_checked(IMPORT_ITEM, true);
526
if (p_select_with_data) {
527
child_item->set_checked(IMPORT_ITEM_DATA, true);
528
}
529
_store_selected_item(child_item);
530
531
_select_all_subitems(child_item, p_select_with_data);
532
child_item = child_item->get_next();
533
}
534
}
535
536
void ThemeItemImportTree::_deselect_all_subitems(TreeItem *p_root_item, bool p_deselect_completely) {
537
TreeItem *child_item = p_root_item->get_first_child();
538
while (child_item) {
539
child_item->set_checked(IMPORT_ITEM_DATA, false);
540
if (p_deselect_completely) {
541
child_item->set_checked(IMPORT_ITEM, false);
542
}
543
_store_selected_item(child_item);
544
545
_deselect_all_subitems(child_item, p_deselect_completely);
546
child_item = child_item->get_next();
547
}
548
}
549
550
void ThemeItemImportTree::_select_all_items_pressed() {
551
if (updating_tree) {
552
return;
553
}
554
555
updating_tree = true;
556
557
TreeItem *root = import_items_tree->get_root();
558
_select_all_subitems(root, false);
559
560
updating_tree = false;
561
}
562
563
void ThemeItemImportTree::_select_full_items_pressed() {
564
if (updating_tree) {
565
return;
566
}
567
568
updating_tree = true;
569
570
TreeItem *root = import_items_tree->get_root();
571
_select_all_subitems(root, true);
572
573
updating_tree = false;
574
}
575
576
void ThemeItemImportTree::_deselect_all_items_pressed() {
577
if (updating_tree) {
578
return;
579
}
580
581
updating_tree = true;
582
583
TreeItem *root = import_items_tree->get_root();
584
_deselect_all_subitems(root, true);
585
586
updating_tree = false;
587
}
588
589
void ThemeItemImportTree::_select_all_data_type_pressed(int p_data_type) {
590
ERR_FAIL_INDEX_MSG(p_data_type, Theme::DATA_TYPE_MAX, "Theme item data type is out of bounds.");
591
592
if (updating_tree) {
593
return;
594
}
595
596
Theme::DataType data_type = (Theme::DataType)p_data_type;
597
List<TreeItem *> *item_list = nullptr;
598
599
switch (data_type) {
600
case Theme::DATA_TYPE_COLOR:
601
item_list = &tree_color_items;
602
break;
603
604
case Theme::DATA_TYPE_CONSTANT:
605
item_list = &tree_constant_items;
606
break;
607
608
case Theme::DATA_TYPE_FONT:
609
item_list = &tree_font_items;
610
break;
611
612
case Theme::DATA_TYPE_FONT_SIZE:
613
item_list = &tree_font_size_items;
614
break;
615
616
case Theme::DATA_TYPE_ICON:
617
item_list = &tree_icon_items;
618
break;
619
620
case Theme::DATA_TYPE_STYLEBOX:
621
item_list = &tree_stylebox_items;
622
break;
623
624
case Theme::DATA_TYPE_MAX:
625
return; // Can't happen, but silences warning.
626
}
627
628
updating_tree = true;
629
630
for (List<TreeItem *>::Element *E = item_list->front(); E; E = E->next()) {
631
TreeItem *child_item = E->get();
632
if (!child_item) {
633
continue;
634
}
635
636
child_item->set_checked(IMPORT_ITEM, true);
637
child_item->propagate_check(IMPORT_ITEM, false);
638
_store_selected_item(child_item);
639
}
640
641
updating_tree = false;
642
}
643
644
void ThemeItemImportTree::_select_full_data_type_pressed(int p_data_type) {
645
ERR_FAIL_INDEX_MSG(p_data_type, Theme::DATA_TYPE_MAX, "Theme item data type is out of bounds.");
646
647
if (updating_tree) {
648
return;
649
}
650
651
Theme::DataType data_type = (Theme::DataType)p_data_type;
652
List<TreeItem *> *item_list = nullptr;
653
654
switch (data_type) {
655
case Theme::DATA_TYPE_COLOR:
656
item_list = &tree_color_items;
657
break;
658
659
case Theme::DATA_TYPE_CONSTANT:
660
item_list = &tree_constant_items;
661
break;
662
663
case Theme::DATA_TYPE_FONT:
664
item_list = &tree_font_items;
665
break;
666
667
case Theme::DATA_TYPE_FONT_SIZE:
668
item_list = &tree_font_size_items;
669
break;
670
671
case Theme::DATA_TYPE_ICON:
672
item_list = &tree_icon_items;
673
break;
674
675
case Theme::DATA_TYPE_STYLEBOX:
676
item_list = &tree_stylebox_items;
677
break;
678
679
case Theme::DATA_TYPE_MAX:
680
return; // Can't happen, but silences warning.
681
}
682
683
updating_tree = true;
684
685
for (List<TreeItem *>::Element *E = item_list->front(); E; E = E->next()) {
686
TreeItem *child_item = E->get();
687
if (!child_item) {
688
continue;
689
}
690
691
child_item->set_checked(IMPORT_ITEM, true);
692
child_item->set_checked(IMPORT_ITEM_DATA, true);
693
child_item->propagate_check(IMPORT_ITEM, false);
694
child_item->propagate_check(IMPORT_ITEM_DATA, false);
695
_store_selected_item(child_item);
696
}
697
698
updating_tree = false;
699
}
700
701
void ThemeItemImportTree::_deselect_all_data_type_pressed(int p_data_type) {
702
ERR_FAIL_INDEX_MSG(p_data_type, Theme::DATA_TYPE_MAX, "Theme item data type is out of bounds.");
703
704
if (updating_tree) {
705
return;
706
}
707
708
Theme::DataType data_type = (Theme::DataType)p_data_type;
709
List<TreeItem *> *item_list = nullptr;
710
711
switch (data_type) {
712
case Theme::DATA_TYPE_COLOR:
713
item_list = &tree_color_items;
714
break;
715
716
case Theme::DATA_TYPE_CONSTANT:
717
item_list = &tree_constant_items;
718
break;
719
720
case Theme::DATA_TYPE_FONT:
721
item_list = &tree_font_items;
722
break;
723
724
case Theme::DATA_TYPE_FONT_SIZE:
725
item_list = &tree_font_size_items;
726
break;
727
728
case Theme::DATA_TYPE_ICON:
729
item_list = &tree_icon_items;
730
break;
731
732
case Theme::DATA_TYPE_STYLEBOX:
733
item_list = &tree_stylebox_items;
734
break;
735
736
case Theme::DATA_TYPE_MAX:
737
return; // Can't happen, but silences warning.
738
}
739
740
updating_tree = true;
741
742
for (List<TreeItem *>::Element *E = item_list->front(); E; E = E->next()) {
743
TreeItem *child_item = E->get();
744
if (!child_item) {
745
continue;
746
}
747
748
child_item->set_checked(IMPORT_ITEM, false);
749
child_item->set_checked(IMPORT_ITEM_DATA, false);
750
child_item->propagate_check(IMPORT_ITEM, false);
751
child_item->propagate_check(IMPORT_ITEM_DATA, false);
752
_store_selected_item(child_item);
753
}
754
755
updating_tree = false;
756
}
757
758
void ThemeItemImportTree::_import_selected() {
759
if (selected_items.is_empty()) {
760
EditorNode::get_singleton()->show_accept(TTR("Nothing was selected for the import."), TTR("OK"));
761
return;
762
}
763
764
Ref<Theme> old_snapshot = edited_theme->duplicate();
765
Ref<Theme> new_snapshot = edited_theme->duplicate();
766
767
ProgressDialog::get_singleton()->add_task("import_theme_items", TTR("Importing Theme Items"), selected_items.size() + 2);
768
769
int idx = 0;
770
for (KeyValue<ThemeItem, ItemCheckedState> &E : selected_items) {
771
// Arbitrary number of items to skip from reporting.
772
// Reduces the number of UI updates that this causes when copying large themes.
773
if (idx % 10 == 0) {
774
Array arr = { idx + 1, selected_items.size() };
775
ProgressDialog::get_singleton()->task_step("import_theme_items", TTR("Importing items {n}/{n}").format(arr, "{n}"), idx);
776
}
777
778
ItemCheckedState cs = E.value;
779
ThemeItem ti = E.key;
780
781
if (cs == SELECT_IMPORT_DEFINITION || cs == SELECT_IMPORT_FULL) {
782
Variant item_value = Variant();
783
784
if (cs == SELECT_IMPORT_FULL) {
785
item_value = base_theme->get_theme_item(ti.data_type, ti.item_name, ti.type_name);
786
} else {
787
switch (ti.data_type) {
788
case Theme::DATA_TYPE_COLOR:
789
item_value = Color();
790
break;
791
792
case Theme::DATA_TYPE_CONSTANT:
793
item_value = 0;
794
break;
795
796
case Theme::DATA_TYPE_FONT:
797
item_value = Ref<Font>();
798
break;
799
800
case Theme::DATA_TYPE_FONT_SIZE:
801
item_value = -1;
802
break;
803
804
case Theme::DATA_TYPE_ICON:
805
item_value = Ref<Texture2D>();
806
break;
807
808
case Theme::DATA_TYPE_STYLEBOX:
809
item_value = Ref<StyleBox>();
810
break;
811
812
case Theme::DATA_TYPE_MAX:
813
break; // Can't happen, but silences warning.
814
}
815
}
816
817
new_snapshot->set_theme_item(ti.data_type, ti.item_name, ti.type_name, item_value);
818
}
819
820
idx++;
821
}
822
823
// Allow changes to be reported now that the operation is finished.
824
ProgressDialog::get_singleton()->task_step("import_theme_items", TTR("Updating the editor"), idx++);
825
826
// Make sure the task is not ended before the editor freezes to update the Inspector.
827
ProgressDialog::get_singleton()->task_step("import_theme_items", TTR("Finalizing"), idx++);
828
829
ProgressDialog::get_singleton()->end_task("import_theme_items");
830
831
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
832
ur->create_action(TTR("Import Theme Items"));
833
834
ur->add_do_method(*edited_theme, "clear");
835
ur->add_do_method(*edited_theme, "merge_with", new_snapshot);
836
ur->add_undo_method(*edited_theme, "clear");
837
ur->add_undo_method(*edited_theme, "merge_with", old_snapshot);
838
839
ur->add_do_method(this, "emit_signal", SNAME("items_imported"));
840
ur->add_undo_method(this, "emit_signal", SNAME("items_imported"));
841
842
ur->commit_action();
843
}
844
845
void ThemeItemImportTree::set_edited_theme(const Ref<Theme> &p_theme) {
846
edited_theme = p_theme;
847
}
848
849
void ThemeItemImportTree::set_base_theme(const Ref<Theme> &p_theme) {
850
base_theme = p_theme;
851
}
852
853
void ThemeItemImportTree::reset_item_tree() {
854
import_items_filter->clear();
855
selected_items.clear();
856
857
total_selected_colors_label->hide();
858
total_selected_constants_label->hide();
859
total_selected_fonts_label->hide();
860
total_selected_font_sizes_label->hide();
861
total_selected_icons_label->hide();
862
total_selected_styleboxes_label->hide();
863
864
_update_items_tree();
865
}
866
867
bool ThemeItemImportTree::has_selected_items() const {
868
return (selected_items.size() > 0);
869
}
870
871
void ThemeItemImportTree::_notification(int p_what) {
872
switch (p_what) {
873
case NOTIFICATION_THEME_CHANGED: {
874
select_icons_warning_icon->set_texture(get_editor_theme_icon(SNAME("StatusWarning")));
875
select_icons_warning->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("font_disabled_color"), EditorStringName(Editor)));
876
877
import_items_filter->set_right_icon(get_editor_theme_icon(SNAME("Search")));
878
879
// Bottom panel buttons.
880
import_collapse_types_button->set_button_icon(get_editor_theme_icon(SNAME("CollapseTree")));
881
import_expand_types_button->set_button_icon(get_editor_theme_icon(SNAME("ExpandTree")));
882
883
import_select_all_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeSelectAll")));
884
import_select_full_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeSelectFull")));
885
import_deselect_all_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeDeselectAll")));
886
887
// Side panel buttons.
888
select_colors_icon->set_texture(get_editor_theme_icon(SNAME("Color")));
889
deselect_all_colors_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeDeselectAll")));
890
select_all_colors_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeSelectAll")));
891
select_full_colors_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeSelectFull")));
892
893
select_constants_icon->set_texture(get_editor_theme_icon(SNAME("MemberConstant")));
894
deselect_all_constants_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeDeselectAll")));
895
select_all_constants_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeSelectAll")));
896
select_full_constants_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeSelectFull")));
897
898
select_fonts_icon->set_texture(get_editor_theme_icon(SNAME("FontItem")));
899
deselect_all_fonts_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeDeselectAll")));
900
select_all_fonts_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeSelectAll")));
901
select_full_fonts_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeSelectFull")));
902
903
select_font_sizes_icon->set_texture(get_editor_theme_icon(SNAME("FontSize")));
904
deselect_all_font_sizes_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeDeselectAll")));
905
select_all_font_sizes_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeSelectAll")));
906
select_full_font_sizes_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeSelectFull")));
907
908
select_icons_icon->set_texture(get_editor_theme_icon(SNAME("ImageTexture")));
909
deselect_all_icons_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeDeselectAll")));
910
select_all_icons_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeSelectAll")));
911
select_full_icons_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeSelectFull")));
912
913
select_styleboxes_icon->set_texture(get_editor_theme_icon(SNAME("StyleBoxFlat")));
914
deselect_all_styleboxes_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeDeselectAll")));
915
select_all_styleboxes_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeSelectAll")));
916
select_full_styleboxes_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeSelectFull")));
917
} break;
918
}
919
}
920
921
void ThemeItemImportTree::_bind_methods() {
922
ADD_SIGNAL(MethodInfo("items_imported"));
923
}
924
925
ThemeItemImportTree::ThemeItemImportTree() {
926
import_items_filter = memnew(LineEdit);
927
import_items_filter->set_placeholder(TTR("Filter Items"));
928
import_items_filter->set_clear_button_enabled(true);
929
add_child(import_items_filter);
930
import_items_filter->connect(SceneStringName(text_changed), callable_mp(this, &ThemeItemImportTree::_filter_text_changed));
931
932
HBoxContainer *import_main_hb = memnew(HBoxContainer);
933
import_main_hb->set_v_size_flags(Control::SIZE_EXPAND_FILL);
934
add_child(import_main_hb);
935
936
import_items_tree = memnew(Tree);
937
import_items_tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
938
import_items_tree->set_hide_root(true);
939
import_items_tree->set_h_size_flags(Control::SIZE_EXPAND_FILL);
940
import_main_hb->add_child(import_items_tree);
941
import_items_tree->connect("item_edited", callable_mp(this, &ThemeItemImportTree::_tree_item_edited));
942
import_items_tree->connect("check_propagated_to_item", callable_mp(this, &ThemeItemImportTree::_check_propagated_to_tree_item));
943
944
import_items_tree->set_columns(3);
945
import_items_tree->set_column_titles_visible(true);
946
import_items_tree->set_column_title(IMPORT_ITEM, TTR("Import"));
947
import_items_tree->set_column_title(IMPORT_ITEM_DATA, TTR("With Data"));
948
import_items_tree->set_column_expand(0, true);
949
import_items_tree->set_column_clip_content(0, true);
950
import_items_tree->set_column_expand(IMPORT_ITEM, false);
951
import_items_tree->set_column_expand(IMPORT_ITEM_DATA, false);
952
import_items_tree->set_column_custom_minimum_width(0, 160 * EDSCALE);
953
import_items_tree->set_column_custom_minimum_width(IMPORT_ITEM, 80 * EDSCALE);
954
import_items_tree->set_column_custom_minimum_width(IMPORT_ITEM_DATA, 80 * EDSCALE);
955
import_items_tree->set_column_clip_content(1, true);
956
import_items_tree->set_column_clip_content(2, true);
957
import_items_tree->set_theme_type_variation("TreeSecondary");
958
959
ScrollContainer *import_bulk_sc = memnew(ScrollContainer);
960
import_bulk_sc->set_custom_minimum_size(Size2(260.0, 0.0) * EDSCALE);
961
import_bulk_sc->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
962
import_main_hb->add_child(import_bulk_sc);
963
VBoxContainer *import_bulk_vb = memnew(VBoxContainer);
964
import_bulk_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
965
import_bulk_sc->add_child(import_bulk_vb);
966
967
Label *import_bulk_label = memnew(Label);
968
import_bulk_label->set_text(TTR("Select by data type:"));
969
import_bulk_vb->add_child(import_bulk_label);
970
971
select_colors_icon = memnew(TextureRect);
972
select_colors_label = memnew(Label);
973
deselect_all_colors_button = memnew(Button);
974
select_all_colors_button = memnew(Button);
975
select_full_colors_button = memnew(Button);
976
total_selected_colors_label = memnew(Label);
977
978
select_constants_icon = memnew(TextureRect);
979
select_constants_label = memnew(Label);
980
deselect_all_constants_button = memnew(Button);
981
select_all_constants_button = memnew(Button);
982
select_full_constants_button = memnew(Button);
983
total_selected_constants_label = memnew(Label);
984
985
select_fonts_icon = memnew(TextureRect);
986
select_fonts_label = memnew(Label);
987
deselect_all_fonts_button = memnew(Button);
988
select_all_fonts_button = memnew(Button);
989
select_full_fonts_button = memnew(Button);
990
total_selected_fonts_label = memnew(Label);
991
992
select_font_sizes_icon = memnew(TextureRect);
993
select_font_sizes_label = memnew(Label);
994
deselect_all_font_sizes_button = memnew(Button);
995
select_all_font_sizes_button = memnew(Button);
996
select_full_font_sizes_button = memnew(Button);
997
total_selected_font_sizes_label = memnew(Label);
998
999
select_icons_icon = memnew(TextureRect);
1000
select_icons_label = memnew(Label);
1001
deselect_all_icons_button = memnew(Button);
1002
select_all_icons_button = memnew(Button);
1003
select_full_icons_button = memnew(Button);
1004
total_selected_icons_label = memnew(Label);
1005
1006
select_styleboxes_icon = memnew(TextureRect);
1007
select_styleboxes_label = memnew(Label);
1008
deselect_all_styleboxes_button = memnew(Button);
1009
select_all_styleboxes_button = memnew(Button);
1010
select_full_styleboxes_button = memnew(Button);
1011
total_selected_styleboxes_label = memnew(Label);
1012
1013
for (int i = 0; i < Theme::DATA_TYPE_MAX; i++) {
1014
Theme::DataType dt = (Theme::DataType)i;
1015
1016
TextureRect *select_items_icon = nullptr;
1017
Label *select_items_label = nullptr;
1018
Button *deselect_all_items_button = nullptr;
1019
Button *select_all_items_button = nullptr;
1020
Button *select_full_items_button = nullptr;
1021
Label *total_selected_items_label = nullptr;
1022
1023
String items_title;
1024
String select_all_items_tooltip;
1025
String select_full_items_tooltip;
1026
String deselect_all_items_tooltip;
1027
1028
switch (dt) {
1029
case Theme::DATA_TYPE_COLOR:
1030
select_items_icon = select_colors_icon;
1031
select_items_label = select_colors_label;
1032
deselect_all_items_button = deselect_all_colors_button;
1033
select_all_items_button = select_all_colors_button;
1034
select_full_items_button = select_full_colors_button;
1035
total_selected_items_label = total_selected_colors_label;
1036
1037
items_title = TTR("Colors");
1038
select_all_items_tooltip = TTR("Select all visible color items.");
1039
select_full_items_tooltip = TTR("Select all visible color items and their data.");
1040
deselect_all_items_tooltip = TTR("Deselect all visible color items.");
1041
break;
1042
1043
case Theme::DATA_TYPE_CONSTANT:
1044
select_items_icon = select_constants_icon;
1045
select_items_label = select_constants_label;
1046
deselect_all_items_button = deselect_all_constants_button;
1047
select_all_items_button = select_all_constants_button;
1048
select_full_items_button = select_full_constants_button;
1049
total_selected_items_label = total_selected_constants_label;
1050
1051
items_title = TTR("Constants");
1052
select_all_items_tooltip = TTR("Select all visible constant items.");
1053
select_full_items_tooltip = TTR("Select all visible constant items and their data.");
1054
deselect_all_items_tooltip = TTR("Deselect all visible constant items.");
1055
break;
1056
1057
case Theme::DATA_TYPE_FONT:
1058
select_items_icon = select_fonts_icon;
1059
select_items_label = select_fonts_label;
1060
deselect_all_items_button = deselect_all_fonts_button;
1061
select_all_items_button = select_all_fonts_button;
1062
select_full_items_button = select_full_fonts_button;
1063
total_selected_items_label = total_selected_fonts_label;
1064
1065
items_title = TTR("Fonts");
1066
select_all_items_tooltip = TTR("Select all visible font items.");
1067
select_full_items_tooltip = TTR("Select all visible font items and their data.");
1068
deselect_all_items_tooltip = TTR("Deselect all visible font items.");
1069
break;
1070
1071
case Theme::DATA_TYPE_FONT_SIZE:
1072
select_items_icon = select_font_sizes_icon;
1073
select_items_label = select_font_sizes_label;
1074
deselect_all_items_button = deselect_all_font_sizes_button;
1075
select_all_items_button = select_all_font_sizes_button;
1076
select_full_items_button = select_full_font_sizes_button;
1077
total_selected_items_label = total_selected_font_sizes_label;
1078
1079
items_title = TTR("Font sizes");
1080
select_all_items_tooltip = TTR("Select all visible font size items.");
1081
select_full_items_tooltip = TTR("Select all visible font size items and their data.");
1082
deselect_all_items_tooltip = TTR("Deselect all visible font size items.");
1083
break;
1084
1085
case Theme::DATA_TYPE_ICON:
1086
select_items_icon = select_icons_icon;
1087
select_items_label = select_icons_label;
1088
deselect_all_items_button = deselect_all_icons_button;
1089
select_all_items_button = select_all_icons_button;
1090
select_full_items_button = select_full_icons_button;
1091
total_selected_items_label = total_selected_icons_label;
1092
1093
items_title = TTR("Icons");
1094
select_all_items_tooltip = TTR("Select all visible icon items.");
1095
select_full_items_tooltip = TTR("Select all visible icon items and their data.");
1096
deselect_all_items_tooltip = TTR("Deselect all visible icon items.");
1097
break;
1098
1099
case Theme::DATA_TYPE_STYLEBOX:
1100
select_items_icon = select_styleboxes_icon;
1101
select_items_label = select_styleboxes_label;
1102
deselect_all_items_button = deselect_all_styleboxes_button;
1103
select_all_items_button = select_all_styleboxes_button;
1104
select_full_items_button = select_full_styleboxes_button;
1105
total_selected_items_label = total_selected_styleboxes_label;
1106
1107
items_title = TTR("Styleboxes");
1108
select_all_items_tooltip = TTR("Select all visible stylebox items.");
1109
select_full_items_tooltip = TTR("Select all visible stylebox items and their data.");
1110
deselect_all_items_tooltip = TTR("Deselect all visible stylebox items.");
1111
break;
1112
1113
case Theme::DATA_TYPE_MAX:
1114
continue; // Can't happen, but silences warning.
1115
}
1116
1117
if (i > 0) {
1118
import_bulk_vb->add_child(memnew(HSeparator));
1119
}
1120
1121
HBoxContainer *all_set = memnew(HBoxContainer);
1122
import_bulk_vb->add_child(all_set);
1123
1124
HBoxContainer *label_set = memnew(HBoxContainer);
1125
label_set->set_h_size_flags(Control::SIZE_EXPAND_FILL);
1126
all_set->add_child(label_set);
1127
select_items_icon->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
1128
label_set->add_child(select_items_icon);
1129
select_items_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
1130
select_items_label->set_clip_text(true);
1131
select_items_label->set_text(items_title);
1132
label_set->add_child(select_items_label);
1133
1134
HBoxContainer *button_set = memnew(HBoxContainer);
1135
button_set->set_alignment(BoxContainer::ALIGNMENT_END);
1136
all_set->add_child(button_set);
1137
select_all_items_button->set_flat(true);
1138
select_all_items_button->set_tooltip_text(select_all_items_tooltip);
1139
button_set->add_child(select_all_items_button);
1140
select_all_items_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeItemImportTree::_select_all_data_type_pressed).bind(i));
1141
select_full_items_button->set_flat(true);
1142
select_full_items_button->set_tooltip_text(select_full_items_tooltip);
1143
button_set->add_child(select_full_items_button);
1144
select_full_items_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeItemImportTree::_select_full_data_type_pressed).bind(i));
1145
deselect_all_items_button->set_flat(true);
1146
deselect_all_items_button->set_tooltip_text(deselect_all_items_tooltip);
1147
button_set->add_child(deselect_all_items_button);
1148
deselect_all_items_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeItemImportTree::_deselect_all_data_type_pressed).bind(i));
1149
1150
total_selected_items_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
1151
total_selected_items_label->hide();
1152
import_bulk_vb->add_child(total_selected_items_label);
1153
1154
if (dt == Theme::DATA_TYPE_ICON) {
1155
select_icons_warning_hb = memnew(HBoxContainer);
1156
import_bulk_vb->add_child(select_icons_warning_hb);
1157
1158
select_icons_warning_icon = memnew(TextureRect);
1159
select_icons_warning_icon->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
1160
select_icons_warning_hb->add_child(select_icons_warning_icon);
1161
1162
select_icons_warning = memnew(Label);
1163
select_icons_warning->set_text(TTR("Caution: Adding icon data may considerably increase the size of your Theme resource."));
1164
select_icons_warning->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
1165
select_icons_warning->set_h_size_flags(Control::SIZE_EXPAND_FILL);
1166
select_icons_warning_hb->add_child(select_icons_warning);
1167
}
1168
}
1169
1170
add_child(memnew(HSeparator));
1171
1172
HBoxContainer *import_buttons = memnew(HBoxContainer);
1173
add_child(import_buttons);
1174
1175
import_collapse_types_button = memnew(Button);
1176
import_collapse_types_button->set_flat(true);
1177
import_collapse_types_button->set_tooltip_text(TTR("Collapse types."));
1178
import_buttons->add_child(import_collapse_types_button);
1179
import_collapse_types_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeItemImportTree::_toggle_type_items).bind(true));
1180
import_expand_types_button = memnew(Button);
1181
import_expand_types_button->set_flat(true);
1182
import_expand_types_button->set_tooltip_text(TTR("Expand types."));
1183
import_buttons->add_child(import_expand_types_button);
1184
import_expand_types_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeItemImportTree::_toggle_type_items).bind(false));
1185
1186
import_buttons->add_child(memnew(VSeparator));
1187
1188
import_select_all_button = memnew(Button);
1189
import_select_all_button->set_flat(true);
1190
import_select_all_button->set_text(TTR("Select All"));
1191
import_select_all_button->set_tooltip_text(TTR("Select all Theme items."));
1192
import_buttons->add_child(import_select_all_button);
1193
import_select_all_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeItemImportTree::_select_all_items_pressed));
1194
import_select_full_button = memnew(Button);
1195
import_select_full_button->set_flat(true);
1196
import_select_full_button->set_text(TTR("Select With Data"));
1197
import_select_full_button->set_tooltip_text(TTR("Select all Theme items with item data."));
1198
import_buttons->add_child(import_select_full_button);
1199
import_select_full_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeItemImportTree::_select_full_items_pressed));
1200
import_deselect_all_button = memnew(Button);
1201
import_deselect_all_button->set_flat(true);
1202
import_deselect_all_button->set_text(TTR("Deselect All"));
1203
import_deselect_all_button->set_tooltip_text(TTR("Deselect all Theme items."));
1204
import_buttons->add_child(import_deselect_all_button);
1205
import_deselect_all_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeItemImportTree::_deselect_all_items_pressed));
1206
1207
import_buttons->add_spacer();
1208
1209
Button *import_add_selected_button = memnew(Button);
1210
import_add_selected_button->set_text(TTR("Import Selected"));
1211
import_buttons->add_child(import_add_selected_button);
1212
import_add_selected_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeItemImportTree::_import_selected));
1213
}
1214
1215
///////////////////////
1216
1217
void ThemeItemEditorDialog::ok_pressed() {
1218
if (import_default_theme_items->has_selected_items() || import_editor_theme_items->has_selected_items() || import_other_theme_items->has_selected_items()) {
1219
confirm_closing_dialog->set_text(TTR("Import Items tab has some items selected. Selection will be lost upon closing this window.\nClose anyway?"));
1220
confirm_closing_dialog->popup_centered(Size2(380, 120) * EDSCALE);
1221
return;
1222
}
1223
1224
hide();
1225
}
1226
1227
void ThemeItemEditorDialog::_close_dialog() {
1228
hide();
1229
}
1230
1231
void ThemeItemEditorDialog::_dialog_about_to_show() {
1232
ERR_FAIL_COND_MSG(edited_theme.is_null(), "Invalid state of the Theme Editor; the Theme resource is missing.");
1233
1234
_update_edit_types();
1235
1236
import_default_theme_items->set_edited_theme(edited_theme);
1237
import_default_theme_items->set_base_theme(ThemeDB::get_singleton()->get_default_theme());
1238
import_default_theme_items->reset_item_tree();
1239
1240
import_editor_theme_items->set_edited_theme(edited_theme);
1241
import_editor_theme_items->set_base_theme(EditorNode::get_singleton()->get_editor_theme());
1242
import_editor_theme_items->reset_item_tree();
1243
1244
import_other_theme_items->set_edited_theme(edited_theme);
1245
import_other_theme_items->reset_item_tree();
1246
}
1247
1248
void ThemeItemEditorDialog::_update_edit_types() {
1249
Ref<Theme> base_theme = ThemeDB::get_singleton()->get_default_theme();
1250
1251
List<StringName> theme_types;
1252
edited_theme->get_type_list(&theme_types);
1253
theme_types.sort_custom<StringName::AlphCompare>();
1254
1255
bool item_reselected = false;
1256
edit_type_list->clear();
1257
TreeItem *list_root = edit_type_list->create_item();
1258
1259
for (const StringName &E : theme_types) {
1260
Ref<Texture2D> item_icon;
1261
if (E == "") {
1262
item_icon = get_editor_theme_icon(SNAME("NodeDisabled"));
1263
} else {
1264
item_icon = EditorNode::get_singleton()->get_class_icon(E, "NodeDisabled");
1265
}
1266
TreeItem *list_item = edit_type_list->create_item(list_root);
1267
list_item->set_text(0, E);
1268
list_item->set_metadata(0, E);
1269
list_item->set_editable(0, true);
1270
list_item->set_icon(0, item_icon);
1271
list_item->add_button(0, get_editor_theme_icon(SNAME("Remove")), TYPES_TREE_REMOVE_ITEM, false, TTRC("Remove Type"));
1272
1273
if (E == edited_item_type) {
1274
list_item->select(0);
1275
item_reselected = true;
1276
}
1277
}
1278
if (!item_reselected) {
1279
edited_item_type = "";
1280
1281
if (list_root->get_child_count() > 0) {
1282
list_root->get_child(0)->select(0);
1283
}
1284
}
1285
1286
List<StringName> default_types;
1287
base_theme->get_type_list(&default_types);
1288
default_types.sort_custom<StringName::AlphCompare>();
1289
1290
String selected_type = "";
1291
TreeItem *selected_item = edit_type_list->get_selected();
1292
if (selected_item) {
1293
selected_type = selected_item->get_text(0);
1294
1295
edit_items_add_color->set_disabled(false);
1296
edit_items_add_constant->set_disabled(false);
1297
edit_items_add_font->set_disabled(false);
1298
edit_items_add_font_size->set_disabled(false);
1299
edit_items_add_icon->set_disabled(false);
1300
edit_items_add_stylebox->set_disabled(false);
1301
1302
edit_items_remove_class->set_disabled(false);
1303
edit_items_remove_custom->set_disabled(false);
1304
edit_items_remove_all->set_disabled(false);
1305
1306
edit_items_message->set_text("");
1307
edit_items_message->hide();
1308
} else {
1309
edit_items_add_color->set_disabled(true);
1310
edit_items_add_constant->set_disabled(true);
1311
edit_items_add_font->set_disabled(true);
1312
edit_items_add_font_size->set_disabled(true);
1313
edit_items_add_icon->set_disabled(true);
1314
edit_items_add_stylebox->set_disabled(true);
1315
1316
edit_items_remove_class->set_disabled(true);
1317
edit_items_remove_custom->set_disabled(true);
1318
edit_items_remove_all->set_disabled(true);
1319
1320
edit_items_message->set_text(TTR("Select a theme type from the list to edit its items.\nYou can add a custom type or import a type with its items from another theme."));
1321
edit_items_message->show();
1322
}
1323
1324
_update_edit_item_tree(selected_type);
1325
}
1326
1327
void ThemeItemEditorDialog::_edited_type_selected() {
1328
TreeItem *selected_item = edit_type_list->get_selected();
1329
String selected_type = selected_item->get_text(0);
1330
_update_edit_item_tree(selected_type);
1331
}
1332
1333
void ThemeItemEditorDialog::_edited_type_edited() {
1334
TreeItem *edited_item = edit_type_list->get_selected();
1335
const String old_type_name = edited_item->get_metadata(0);
1336
1337
const String &new_type_name = Theme::validate_type_name(edited_item->get_text(0));
1338
if (old_type_name == new_type_name) {
1339
edited_item->set_text(0, old_type_name);
1340
return;
1341
}
1342
1343
List<StringName> theme_types;
1344
edited_theme->get_type_list(&theme_types);
1345
if (theme_types.find(new_type_name) != nullptr) {
1346
edited_item->set_text(0, old_type_name);
1347
return;
1348
}
1349
1350
// The list will be recreated, but let's update the item just in case.
1351
edited_item->set_metadata(0, new_type_name);
1352
edited_item->set_text(0, new_type_name);
1353
1354
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
1355
ur->create_action(TTR("Rename Theme Type"));
1356
1357
_rename_theme_type(ur, *edited_theme, old_type_name, new_type_name);
1358
1359
// Set `edited_item_type`.
1360
ur->add_do_method(this, "_update_edit_item_tree", new_type_name);
1361
ur->add_undo_method(this, "_update_edit_item_tree", old_type_name);
1362
1363
ur->add_do_method(this, "_update_edit_types");
1364
ur->add_undo_method(this, "_update_edit_types");
1365
1366
ur->commit_action();
1367
}
1368
1369
void ThemeItemEditorDialog::_edited_type_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button) {
1370
if (p_button != MouseButton::LEFT) {
1371
return;
1372
}
1373
1374
TreeItem *item = Object::cast_to<TreeItem>(p_item);
1375
if (!item) {
1376
return;
1377
}
1378
1379
switch (p_id) {
1380
case TYPES_TREE_REMOVE_ITEM: {
1381
String type_name = item->get_text(0);
1382
_remove_theme_type(type_name);
1383
} break;
1384
}
1385
}
1386
1387
void ThemeItemEditorDialog::_update_edit_item_tree(String p_item_type) {
1388
edited_item_type = p_item_type;
1389
1390
edit_items_tree->clear();
1391
TreeItem *root = edit_items_tree->create_item();
1392
1393
List<StringName> names;
1394
bool has_any_items = false;
1395
1396
{ // Colors.
1397
names.clear();
1398
edited_theme->get_color_list(p_item_type, &names);
1399
1400
if (names.size() > 0) {
1401
TreeItem *color_root = edit_items_tree->create_item(root);
1402
color_root->set_metadata(0, Theme::DATA_TYPE_COLOR);
1403
color_root->set_icon(0, get_editor_theme_icon(SNAME("Color")));
1404
color_root->set_text(0, TTR("Colors"));
1405
color_root->add_button(0, get_editor_theme_icon(SNAME("Clear")), ITEMS_TREE_REMOVE_DATA_TYPE, false, TTR("Remove All Color Items"));
1406
1407
names.sort_custom<StringName::AlphCompare>();
1408
for (const StringName &E : names) {
1409
TreeItem *item = edit_items_tree->create_item(color_root);
1410
item->set_text(0, E);
1411
item->add_button(0, get_editor_theme_icon(SNAME("Edit")), ITEMS_TREE_RENAME_ITEM, false, TTR("Rename Item"));
1412
item->add_button(0, get_editor_theme_icon(SNAME("Remove")), ITEMS_TREE_REMOVE_ITEM, false, TTR("Remove Item"));
1413
}
1414
1415
has_any_items = true;
1416
}
1417
}
1418
1419
{ // Constants.
1420
names.clear();
1421
edited_theme->get_constant_list(p_item_type, &names);
1422
1423
if (names.size() > 0) {
1424
TreeItem *constant_root = edit_items_tree->create_item(root);
1425
constant_root->set_metadata(0, Theme::DATA_TYPE_CONSTANT);
1426
constant_root->set_icon(0, get_editor_theme_icon(SNAME("MemberConstant")));
1427
constant_root->set_text(0, TTR("Constants"));
1428
constant_root->add_button(0, get_editor_theme_icon(SNAME("Clear")), ITEMS_TREE_REMOVE_DATA_TYPE, false, TTR("Remove All Constant Items"));
1429
1430
names.sort_custom<StringName::AlphCompare>();
1431
for (const StringName &E : names) {
1432
TreeItem *item = edit_items_tree->create_item(constant_root);
1433
item->set_text(0, E);
1434
item->add_button(0, get_editor_theme_icon(SNAME("Edit")), ITEMS_TREE_RENAME_ITEM, false, TTR("Rename Item"));
1435
item->add_button(0, get_editor_theme_icon(SNAME("Remove")), ITEMS_TREE_REMOVE_ITEM, false, TTR("Remove Item"));
1436
}
1437
1438
has_any_items = true;
1439
}
1440
}
1441
1442
{ // Fonts.
1443
names.clear();
1444
edited_theme->get_font_list(p_item_type, &names);
1445
1446
if (names.size() > 0) {
1447
TreeItem *font_root = edit_items_tree->create_item(root);
1448
font_root->set_metadata(0, Theme::DATA_TYPE_FONT);
1449
font_root->set_icon(0, get_editor_theme_icon(SNAME("FontItem")));
1450
font_root->set_text(0, TTR("Fonts"));
1451
font_root->add_button(0, get_editor_theme_icon(SNAME("Clear")), ITEMS_TREE_REMOVE_DATA_TYPE, false, TTR("Remove All Font Items"));
1452
1453
names.sort_custom<StringName::AlphCompare>();
1454
for (const StringName &E : names) {
1455
TreeItem *item = edit_items_tree->create_item(font_root);
1456
item->set_text(0, E);
1457
item->add_button(0, get_editor_theme_icon(SNAME("Edit")), ITEMS_TREE_RENAME_ITEM, false, TTR("Rename Item"));
1458
item->add_button(0, get_editor_theme_icon(SNAME("Remove")), ITEMS_TREE_REMOVE_ITEM, false, TTR("Remove Item"));
1459
}
1460
1461
has_any_items = true;
1462
}
1463
}
1464
1465
{ // Font sizes.
1466
names.clear();
1467
edited_theme->get_font_size_list(p_item_type, &names);
1468
1469
if (names.size() > 0) {
1470
TreeItem *font_size_root = edit_items_tree->create_item(root);
1471
font_size_root->set_metadata(0, Theme::DATA_TYPE_FONT_SIZE);
1472
font_size_root->set_icon(0, get_editor_theme_icon(SNAME("FontSize")));
1473
font_size_root->set_text(0, TTR("Font Sizes"));
1474
font_size_root->add_button(0, get_editor_theme_icon(SNAME("Clear")), ITEMS_TREE_REMOVE_DATA_TYPE, false, TTR("Remove All Font Size Items"));
1475
1476
names.sort_custom<StringName::AlphCompare>();
1477
for (const StringName &E : names) {
1478
TreeItem *item = edit_items_tree->create_item(font_size_root);
1479
item->set_text(0, E);
1480
item->add_button(0, get_editor_theme_icon(SNAME("Edit")), ITEMS_TREE_RENAME_ITEM, false, TTR("Rename Item"));
1481
item->add_button(0, get_editor_theme_icon(SNAME("Remove")), ITEMS_TREE_REMOVE_ITEM, false, TTR("Remove Item"));
1482
}
1483
1484
has_any_items = true;
1485
}
1486
}
1487
1488
{ // Icons.
1489
names.clear();
1490
edited_theme->get_icon_list(p_item_type, &names);
1491
1492
if (names.size() > 0) {
1493
TreeItem *icon_root = edit_items_tree->create_item(root);
1494
icon_root->set_metadata(0, Theme::DATA_TYPE_ICON);
1495
icon_root->set_icon(0, get_editor_theme_icon(SNAME("ImageTexture")));
1496
icon_root->set_text(0, TTR("Icons"));
1497
icon_root->add_button(0, get_editor_theme_icon(SNAME("Clear")), ITEMS_TREE_REMOVE_DATA_TYPE, false, TTR("Remove All Icon Items"));
1498
1499
names.sort_custom<StringName::AlphCompare>();
1500
for (const StringName &E : names) {
1501
TreeItem *item = edit_items_tree->create_item(icon_root);
1502
item->set_text(0, E);
1503
item->add_button(0, get_editor_theme_icon(SNAME("Edit")), ITEMS_TREE_RENAME_ITEM, false, TTR("Rename Item"));
1504
item->add_button(0, get_editor_theme_icon(SNAME("Remove")), ITEMS_TREE_REMOVE_ITEM, false, TTR("Remove Item"));
1505
}
1506
1507
has_any_items = true;
1508
}
1509
}
1510
1511
{ // Styleboxes.
1512
names.clear();
1513
edited_theme->get_stylebox_list(p_item_type, &names);
1514
1515
if (names.size() > 0) {
1516
TreeItem *stylebox_root = edit_items_tree->create_item(root);
1517
stylebox_root->set_metadata(0, Theme::DATA_TYPE_STYLEBOX);
1518
stylebox_root->set_icon(0, get_editor_theme_icon(SNAME("StyleBoxFlat")));
1519
stylebox_root->set_text(0, TTR("Styleboxes"));
1520
stylebox_root->add_button(0, get_editor_theme_icon(SNAME("Clear")), ITEMS_TREE_REMOVE_DATA_TYPE, false, TTR("Remove All StyleBox Items"));
1521
1522
names.sort_custom<StringName::AlphCompare>();
1523
for (const StringName &E : names) {
1524
TreeItem *item = edit_items_tree->create_item(stylebox_root);
1525
item->set_text(0, E);
1526
item->add_button(0, get_editor_theme_icon(SNAME("Edit")), ITEMS_TREE_RENAME_ITEM, false, TTR("Rename Item"));
1527
item->add_button(0, get_editor_theme_icon(SNAME("Remove")), ITEMS_TREE_REMOVE_ITEM, false, TTR("Remove Item"));
1528
}
1529
1530
has_any_items = true;
1531
}
1532
}
1533
1534
// If some type is selected, but it doesn't seem to have any items, show a guiding message.
1535
TreeItem *selected_item = edit_type_list->get_selected();
1536
if (selected_item) {
1537
if (!has_any_items) {
1538
edit_items_message->set_text(TTR("This theme type is empty.\nAdd more items to it manually or by importing from another theme."));
1539
edit_items_message->show();
1540
} else {
1541
edit_items_message->set_text("");
1542
edit_items_message->hide();
1543
}
1544
}
1545
}
1546
1547
void ThemeItemEditorDialog::_item_tree_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button) {
1548
if (p_button != MouseButton::LEFT) {
1549
return;
1550
}
1551
1552
TreeItem *item = Object::cast_to<TreeItem>(p_item);
1553
if (!item) {
1554
return;
1555
}
1556
1557
switch (p_id) {
1558
case ITEMS_TREE_RENAME_ITEM: {
1559
String item_name = item->get_text(0);
1560
int data_type = item->get_parent()->get_metadata(0);
1561
_open_rename_theme_item_dialog((Theme::DataType)data_type, item_name);
1562
_update_edit_item_tree(edited_item_type);
1563
} break;
1564
case ITEMS_TREE_REMOVE_ITEM: {
1565
String item_name = item->get_text(0);
1566
int data_type = item->get_parent()->get_metadata(0);
1567
1568
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
1569
ur->create_action(TTR("Remove Theme Item"));
1570
ur->add_do_method(*edited_theme, "clear_theme_item", (Theme::DataType)data_type, item_name, edited_item_type);
1571
ur->add_undo_method(*edited_theme, "set_theme_item", (Theme::DataType)data_type, item_name, edited_item_type, edited_theme->get_theme_item((Theme::DataType)data_type, item_name, edited_item_type));
1572
ur->add_do_method(this, "_update_edit_item_tree", edited_item_type);
1573
ur->add_undo_method(this, "_update_edit_item_tree", edited_item_type);
1574
ur->commit_action();
1575
} break;
1576
case ITEMS_TREE_REMOVE_DATA_TYPE: {
1577
int data_type = item->get_metadata(0);
1578
_remove_data_type_items((Theme::DataType)data_type, edited_item_type);
1579
} break;
1580
}
1581
}
1582
1583
void ThemeItemEditorDialog::_add_theme_type() {
1584
const String &new_type_name = Theme::validate_type_name(edit_add_type_value->get_text());
1585
edit_add_type_value->clear();
1586
1587
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
1588
ur->create_action(TTR("Add Theme Type"));
1589
1590
ur->add_do_method(*edited_theme, "add_type", new_type_name);
1591
ur->add_undo_method(*edited_theme, "remove_type", new_type_name);
1592
1593
ur->add_do_method(this, "_update_edit_types");
1594
ur->add_undo_method(this, "_update_edit_types");
1595
1596
ur->commit_action();
1597
}
1598
1599
void ThemeItemEditorDialog::_add_theme_item(Theme::DataType p_data_type, const String &p_item_name, const String &p_item_type) {
1600
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
1601
ur->create_action(TTR("Create Theme Item"));
1602
1603
switch (p_data_type) {
1604
case Theme::DATA_TYPE_ICON:
1605
ur->add_do_method(*edited_theme, "set_icon", p_item_name, p_item_type, Ref<Texture2D>());
1606
ur->add_undo_method(*edited_theme, "clear_icon", p_item_name, p_item_type);
1607
break;
1608
case Theme::DATA_TYPE_STYLEBOX:
1609
ur->add_do_method(*edited_theme, "set_stylebox", p_item_name, p_item_type, Ref<StyleBox>());
1610
ur->add_undo_method(*edited_theme, "clear_stylebox", p_item_name, p_item_type);
1611
1612
if (theme_type_editor->is_stylebox_pinned(edited_theme->get_stylebox(p_item_name, p_item_type))) {
1613
ur->add_undo_method(theme_type_editor, "_unpin_leading_stylebox");
1614
}
1615
break;
1616
case Theme::DATA_TYPE_FONT:
1617
ur->add_do_method(*edited_theme, "set_font", p_item_name, p_item_type, Ref<Font>());
1618
ur->add_undo_method(*edited_theme, "clear_font", p_item_name, p_item_type);
1619
break;
1620
case Theme::DATA_TYPE_FONT_SIZE:
1621
ur->add_do_method(*edited_theme, "set_font_size", p_item_name, p_item_type, -1);
1622
ur->add_undo_method(*edited_theme, "clear_font_size", p_item_name, p_item_type);
1623
break;
1624
case Theme::DATA_TYPE_COLOR:
1625
ur->add_do_method(*edited_theme, "set_color", p_item_name, p_item_type, Color());
1626
ur->add_undo_method(*edited_theme, "clear_color", p_item_name, p_item_type);
1627
break;
1628
case Theme::DATA_TYPE_CONSTANT:
1629
ur->add_do_method(*edited_theme, "set_constant", p_item_name, p_item_type, 0);
1630
ur->add_undo_method(*edited_theme, "clear_constant", p_item_name, p_item_type);
1631
break;
1632
case Theme::DATA_TYPE_MAX:
1633
break; // Can't happen, but silences warning.
1634
}
1635
1636
ur->add_do_method(this, "_update_edit_item_tree", edited_item_type);
1637
ur->add_undo_method(this, "_update_edit_item_tree", edited_item_type);
1638
ur->commit_action();
1639
}
1640
1641
void ThemeItemEditorDialog::_remove_theme_type(const String &p_theme_type) {
1642
Ref<Theme> old_snapshot = edited_theme->duplicate();
1643
1644
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
1645
ur->create_action(TTR("Remove Theme Type"));
1646
1647
ur->add_do_method(*edited_theme, "remove_type", p_theme_type);
1648
// If the type was empty, it cannot be restored with merge, but thankfully we can fake it.
1649
ur->add_undo_method(*edited_theme, "add_type", p_theme_type);
1650
ur->add_undo_method(*edited_theme, "merge_with", old_snapshot);
1651
1652
ur->add_do_method(this, "_update_edit_types");
1653
ur->add_undo_method(this, "_update_edit_types");
1654
1655
ur->commit_action();
1656
}
1657
1658
void ThemeItemEditorDialog::_remove_data_type_items(Theme::DataType p_data_type, String p_item_type) {
1659
List<StringName> names;
1660
1661
Ref<Theme> old_snapshot = edited_theme->duplicate();
1662
Ref<Theme> new_snapshot = edited_theme->duplicate();
1663
1664
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
1665
ur->create_action(TTR("Remove Data Type Items From Theme"));
1666
1667
new_snapshot->get_theme_item_list(p_data_type, p_item_type, &names);
1668
for (const StringName &E : names) {
1669
new_snapshot->clear_theme_item(p_data_type, E, edited_item_type);
1670
1671
if (p_data_type == Theme::DATA_TYPE_STYLEBOX && theme_type_editor->is_stylebox_pinned(edited_theme->get_stylebox(E, p_item_type))) {
1672
ur->add_do_method(theme_type_editor, "_unpin_leading_stylebox");
1673
ur->add_undo_method(theme_type_editor, "_pin_leading_stylebox", E, edited_theme->get_stylebox(E, p_item_type));
1674
}
1675
}
1676
1677
ur->add_do_method(*edited_theme, "clear");
1678
ur->add_do_method(*edited_theme, "merge_with", new_snapshot);
1679
ur->add_undo_method(*edited_theme, "merge_with", old_snapshot);
1680
1681
ur->add_do_method(this, "_update_edit_item_tree", edited_item_type);
1682
ur->add_undo_method(this, "_update_edit_item_tree", edited_item_type);
1683
1684
ur->commit_action();
1685
}
1686
1687
void ThemeItemEditorDialog::_remove_class_items() {
1688
List<StringName> names;
1689
1690
Ref<Theme> old_snapshot = edited_theme->duplicate();
1691
Ref<Theme> new_snapshot = edited_theme->duplicate();
1692
1693
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
1694
ur->create_action(TTR("Remove Class Items From Theme"));
1695
1696
for (int dt = 0; dt < Theme::DATA_TYPE_MAX; dt++) {
1697
Theme::DataType data_type = (Theme::DataType)dt;
1698
1699
names.clear();
1700
ThemeDB::get_singleton()->get_default_theme()->get_theme_item_list(data_type, edited_item_type, &names);
1701
for (const StringName &E : names) {
1702
if (new_snapshot->has_theme_item_nocheck(data_type, E, edited_item_type)) {
1703
new_snapshot->clear_theme_item(data_type, E, edited_item_type);
1704
1705
if (dt == Theme::DATA_TYPE_STYLEBOX && theme_type_editor->is_stylebox_pinned(edited_theme->get_stylebox(E, edited_item_type))) {
1706
ur->add_do_method(theme_type_editor, "_unpin_leading_stylebox");
1707
ur->add_undo_method(theme_type_editor, "_pin_leading_stylebox", E, edited_theme->get_stylebox(E, edited_item_type));
1708
}
1709
}
1710
}
1711
}
1712
1713
ur->add_do_method(*edited_theme, "clear");
1714
ur->add_do_method(*edited_theme, "merge_with", new_snapshot);
1715
ur->add_undo_method(*edited_theme, "merge_with", old_snapshot);
1716
1717
ur->add_do_method(this, "_update_edit_item_tree", edited_item_type);
1718
ur->add_undo_method(this, "_update_edit_item_tree", edited_item_type);
1719
1720
ur->commit_action();
1721
}
1722
1723
void ThemeItemEditorDialog::_remove_custom_items() {
1724
List<StringName> names;
1725
1726
Ref<Theme> old_snapshot = edited_theme->duplicate();
1727
Ref<Theme> new_snapshot = edited_theme->duplicate();
1728
1729
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
1730
ur->create_action(TTR("Remove Custom Items From Theme"));
1731
1732
for (int dt = 0; dt < Theme::DATA_TYPE_MAX; dt++) {
1733
Theme::DataType data_type = (Theme::DataType)dt;
1734
1735
names.clear();
1736
new_snapshot->get_theme_item_list(data_type, edited_item_type, &names);
1737
for (const StringName &E : names) {
1738
if (!ThemeDB::get_singleton()->get_default_theme()->has_theme_item_nocheck(data_type, E, edited_item_type)) {
1739
new_snapshot->clear_theme_item(data_type, E, edited_item_type);
1740
1741
if (dt == Theme::DATA_TYPE_STYLEBOX && theme_type_editor->is_stylebox_pinned(edited_theme->get_stylebox(E, edited_item_type))) {
1742
ur->add_do_method(theme_type_editor, "_unpin_leading_stylebox");
1743
ur->add_undo_method(theme_type_editor, "_pin_leading_stylebox", E, edited_theme->get_stylebox(E, edited_item_type));
1744
}
1745
}
1746
}
1747
}
1748
1749
ur->add_do_method(*edited_theme, "clear");
1750
ur->add_do_method(*edited_theme, "merge_with", new_snapshot);
1751
ur->add_undo_method(*edited_theme, "merge_with", old_snapshot);
1752
1753
ur->add_do_method(this, "_update_edit_item_tree", edited_item_type);
1754
ur->add_undo_method(this, "_update_edit_item_tree", edited_item_type);
1755
1756
ur->commit_action();
1757
}
1758
1759
void ThemeItemEditorDialog::_remove_all_items() {
1760
List<StringName> names;
1761
1762
Ref<Theme> old_snapshot = edited_theme->duplicate();
1763
Ref<Theme> new_snapshot = edited_theme->duplicate();
1764
1765
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
1766
ur->create_action(TTR("Remove All Items From Theme"));
1767
1768
for (int dt = 0; dt < Theme::DATA_TYPE_MAX; dt++) {
1769
Theme::DataType data_type = (Theme::DataType)dt;
1770
1771
names.clear();
1772
new_snapshot->get_theme_item_list(data_type, edited_item_type, &names);
1773
for (const StringName &E : names) {
1774
new_snapshot->clear_theme_item(data_type, E, edited_item_type);
1775
1776
if (dt == Theme::DATA_TYPE_STYLEBOX && theme_type_editor->is_stylebox_pinned(edited_theme->get_stylebox(E, edited_item_type))) {
1777
ur->add_do_method(theme_type_editor, "_unpin_leading_stylebox");
1778
ur->add_undo_method(theme_type_editor, "_pin_leading_stylebox", E, edited_theme->get_stylebox(E, edited_item_type));
1779
}
1780
}
1781
}
1782
1783
ur->add_do_method(*edited_theme, "clear");
1784
ur->add_do_method(*edited_theme, "merge_with", new_snapshot);
1785
ur->add_undo_method(*edited_theme, "merge_with", old_snapshot);
1786
1787
ur->add_do_method(this, "_update_edit_item_tree", edited_item_type);
1788
ur->add_undo_method(this, "_update_edit_item_tree", edited_item_type);
1789
1790
ur->commit_action();
1791
}
1792
1793
void ThemeItemEditorDialog::_open_add_theme_item_dialog(int p_data_type) {
1794
ERR_FAIL_INDEX_MSG(p_data_type, Theme::DATA_TYPE_MAX, "Theme item data type is out of bounds.");
1795
1796
item_popup_mode = CREATE_THEME_ITEM;
1797
edit_item_data_type = (Theme::DataType)p_data_type;
1798
1799
switch (edit_item_data_type) {
1800
case Theme::DATA_TYPE_COLOR:
1801
edit_theme_item_dialog->set_title(TTR("Add Color Item"));
1802
break;
1803
case Theme::DATA_TYPE_CONSTANT:
1804
edit_theme_item_dialog->set_title(TTR("Add Constant Item"));
1805
break;
1806
case Theme::DATA_TYPE_FONT:
1807
edit_theme_item_dialog->set_title(TTR("Add Font Item"));
1808
break;
1809
case Theme::DATA_TYPE_FONT_SIZE:
1810
edit_theme_item_dialog->set_title(TTR("Add Font Size Item"));
1811
break;
1812
case Theme::DATA_TYPE_ICON:
1813
edit_theme_item_dialog->set_title(TTR("Add Icon Item"));
1814
break;
1815
case Theme::DATA_TYPE_STYLEBOX:
1816
edit_theme_item_dialog->set_title(TTR("Add Stylebox Item"));
1817
break;
1818
case Theme::DATA_TYPE_MAX:
1819
break; // Can't happen, but silences warning.
1820
}
1821
1822
edit_theme_item_old_vb->hide();
1823
theme_item_name->clear();
1824
edit_theme_item_dialog->popup_centered(Size2(380, 110) * EDSCALE);
1825
theme_item_name->grab_focus();
1826
}
1827
1828
void ThemeItemEditorDialog::_open_rename_theme_item_dialog(Theme::DataType p_data_type, String p_item_name) {
1829
ERR_FAIL_INDEX_MSG(p_data_type, Theme::DATA_TYPE_MAX, "Theme item data type is out of bounds.");
1830
1831
item_popup_mode = RENAME_THEME_ITEM;
1832
edit_item_data_type = p_data_type;
1833
edit_item_old_name = p_item_name;
1834
1835
switch (edit_item_data_type) {
1836
case Theme::DATA_TYPE_COLOR:
1837
edit_theme_item_dialog->set_title(TTR("Rename Color Item"));
1838
break;
1839
case Theme::DATA_TYPE_CONSTANT:
1840
edit_theme_item_dialog->set_title(TTR("Rename Constant Item"));
1841
break;
1842
case Theme::DATA_TYPE_FONT:
1843
edit_theme_item_dialog->set_title(TTR("Rename Font Item"));
1844
break;
1845
case Theme::DATA_TYPE_FONT_SIZE:
1846
edit_theme_item_dialog->set_title(TTR("Rename Font Size Item"));
1847
break;
1848
case Theme::DATA_TYPE_ICON:
1849
edit_theme_item_dialog->set_title(TTR("Rename Icon Item"));
1850
break;
1851
case Theme::DATA_TYPE_STYLEBOX:
1852
edit_theme_item_dialog->set_title(TTR("Rename Stylebox Item"));
1853
break;
1854
case Theme::DATA_TYPE_MAX:
1855
break; // Can't happen, but silences warning.
1856
}
1857
1858
edit_theme_item_old_vb->show();
1859
theme_item_old_name->set_text(p_item_name);
1860
theme_item_name->set_text(p_item_name);
1861
edit_theme_item_dialog->popup_centered(Size2(380, 140) * EDSCALE);
1862
theme_item_name->grab_focus();
1863
}
1864
1865
void ThemeItemEditorDialog::_confirm_edit_theme_item() {
1866
const String new_item_name = theme_item_name->get_text().strip_edges().validate_ascii_identifier();
1867
1868
if (item_popup_mode == CREATE_THEME_ITEM) {
1869
_add_theme_item(edit_item_data_type, new_item_name, edited_item_type);
1870
} else if (item_popup_mode == RENAME_THEME_ITEM) {
1871
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
1872
ur->create_action(TTR("Rename Theme Item"));
1873
1874
ur->add_do_method(*edited_theme, "rename_theme_item", edit_item_data_type, edit_item_old_name, new_item_name, edited_item_type);
1875
ur->add_undo_method(*edited_theme, "rename_theme_item", edit_item_data_type, new_item_name, edit_item_old_name, edited_item_type);
1876
1877
ur->add_do_method(this, "_update_edit_item_tree", edited_item_type);
1878
ur->add_undo_method(this, "_update_edit_item_tree", edited_item_type);
1879
1880
ur->commit_action();
1881
}
1882
1883
item_popup_mode = ITEM_POPUP_MODE_MAX;
1884
edit_item_data_type = Theme::DATA_TYPE_MAX;
1885
edit_item_old_name = "";
1886
}
1887
1888
void ThemeItemEditorDialog::_edit_theme_item_gui_input(const Ref<InputEvent> &p_event) {
1889
Ref<InputEventKey> k = p_event;
1890
1891
if (k.is_valid()) {
1892
if (!k->is_pressed()) {
1893
return;
1894
}
1895
1896
if (k->is_action_pressed(SNAME("ui_text_submit"), false, true)) {
1897
_confirm_edit_theme_item();
1898
edit_theme_item_dialog->hide();
1899
edit_theme_item_dialog->set_input_as_handled();
1900
} else if (k->is_action_pressed(SNAME("ui_cancel"), false, true)) {
1901
edit_theme_item_dialog->hide();
1902
edit_theme_item_dialog->set_input_as_handled();
1903
}
1904
}
1905
}
1906
1907
void ThemeItemEditorDialog::_open_select_another_theme() {
1908
import_another_theme_dialog->popup_file_dialog();
1909
}
1910
1911
void ThemeItemEditorDialog::_select_another_theme_cbk(const String &p_path) {
1912
Ref<Theme> loaded_theme = ResourceLoader::load(p_path);
1913
if (loaded_theme.is_null()) {
1914
EditorNode::get_singleton()->show_warning(TTR("Invalid file, not a Theme resource."));
1915
return;
1916
}
1917
if (loaded_theme == edited_theme) {
1918
EditorNode::get_singleton()->show_warning(TTR("Invalid file, same as the edited Theme resource."));
1919
return;
1920
}
1921
1922
import_another_theme_value->set_text(p_path);
1923
import_other_theme_items->set_base_theme(loaded_theme);
1924
import_other_theme_items->reset_item_tree();
1925
}
1926
1927
void ThemeItemEditorDialog::_notification(int p_what) {
1928
switch (p_what) {
1929
case NOTIFICATION_ENTER_TREE: {
1930
connect("about_to_popup", callable_mp(this, &ThemeItemEditorDialog::_dialog_about_to_show));
1931
[[fallthrough]];
1932
}
1933
case NOTIFICATION_THEME_CHANGED: {
1934
edit_items_add_color->set_button_icon(get_editor_theme_icon(SNAME("Color")));
1935
edit_items_add_constant->set_button_icon(get_editor_theme_icon(SNAME("MemberConstant")));
1936
edit_items_add_font->set_button_icon(get_editor_theme_icon(SNAME("FontItem")));
1937
edit_items_add_font_size->set_button_icon(get_editor_theme_icon(SNAME("FontSize")));
1938
edit_items_add_icon->set_button_icon(get_editor_theme_icon(SNAME("ImageTexture")));
1939
edit_items_add_stylebox->set_button_icon(get_editor_theme_icon(SNAME("StyleBoxFlat")));
1940
1941
edit_items_remove_class->set_button_icon(get_editor_theme_icon(SNAME("Control")));
1942
edit_items_remove_custom->set_button_icon(get_editor_theme_icon(SNAME("ThemeRemoveCustomItems")));
1943
edit_items_remove_all->set_button_icon(get_editor_theme_icon(SNAME("ThemeRemoveAllItems")));
1944
1945
edit_add_type_button->set_button_icon(get_editor_theme_icon(SNAME("Add")));
1946
1947
import_another_theme_button->set_button_icon(get_editor_theme_icon(SNAME("Folder")));
1948
} break;
1949
}
1950
}
1951
1952
void ThemeItemEditorDialog::_bind_methods() {
1953
ClassDB::bind_method(D_METHOD("_update_edit_types"), &ThemeItemEditorDialog::_update_edit_types);
1954
ClassDB::bind_method(D_METHOD("_update_edit_item_tree"), &ThemeItemEditorDialog::_update_edit_item_tree);
1955
}
1956
1957
void ThemeItemEditorDialog::set_edited_theme(const Ref<Theme> &p_theme) {
1958
edited_theme = p_theme;
1959
}
1960
1961
ThemeItemEditorDialog::ThemeItemEditorDialog(ThemeTypeEditor *p_theme_type_editor) {
1962
set_title(TTR("Manage Theme Items"));
1963
set_ok_button_text(TTR("Close"));
1964
set_hide_on_ok(false); // Closing may require a confirmation in some cases.
1965
1966
theme_type_editor = p_theme_type_editor;
1967
1968
tc = memnew(TabContainer);
1969
add_child(tc);
1970
tc->set_theme_type_variation("TabContainerOdd");
1971
1972
// Edit Items tab.
1973
HSplitContainer *edit_dialog_hs = memnew(HSplitContainer);
1974
tc->add_child(edit_dialog_hs);
1975
tc->set_tab_title(0, TTR("Edit Items"));
1976
1977
VBoxContainer *edit_dialog_side_vb = memnew(VBoxContainer);
1978
edit_dialog_side_vb->set_custom_minimum_size(Size2(200.0, 0.0) * EDSCALE);
1979
edit_dialog_hs->add_child(edit_dialog_side_vb);
1980
1981
Label *edit_type_label = memnew(Label);
1982
edit_type_label->set_text(TTR("Types:"));
1983
edit_dialog_side_vb->add_child(edit_type_label);
1984
1985
edit_type_list = memnew(Tree);
1986
edit_type_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
1987
edit_type_list->set_hide_root(true);
1988
edit_type_list->set_hide_folding(true);
1989
edit_type_list->set_columns(1);
1990
edit_type_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
1991
edit_dialog_side_vb->add_child(edit_type_list);
1992
edit_type_list->connect(SceneStringName(item_selected), callable_mp(this, &ThemeItemEditorDialog::_edited_type_selected));
1993
edit_type_list->connect("item_edited", callable_mp(this, &ThemeItemEditorDialog::_edited_type_edited));
1994
edit_type_list->connect("button_clicked", callable_mp(this, &ThemeItemEditorDialog::_edited_type_button_pressed));
1995
edit_type_list->set_theme_type_variation("TreeSecondary");
1996
1997
Label *edit_add_type_label = memnew(Label);
1998
edit_add_type_label->set_text(TTR("Add Type:"));
1999
edit_dialog_side_vb->add_child(edit_add_type_label);
2000
2001
HBoxContainer *edit_add_type_hb = memnew(HBoxContainer);
2002
edit_dialog_side_vb->add_child(edit_add_type_hb);
2003
edit_add_type_value = memnew(LineEdit);
2004
edit_add_type_value->set_h_size_flags(Control::SIZE_EXPAND_FILL);
2005
edit_add_type_value->connect(SceneStringName(text_submitted), callable_mp(this, &ThemeItemEditorDialog::_add_theme_type).unbind(1));
2006
edit_add_type_hb->add_child(edit_add_type_value);
2007
edit_add_type_button = memnew(Button);
2008
edit_add_type_hb->add_child(edit_add_type_button);
2009
edit_add_type_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeItemEditorDialog::_add_theme_type));
2010
2011
VBoxContainer *edit_items_vb = memnew(VBoxContainer);
2012
edit_items_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
2013
edit_dialog_hs->add_child(edit_items_vb);
2014
2015
HBoxContainer *edit_items_toolbar = memnew(HBoxContainer);
2016
edit_items_vb->add_child(edit_items_toolbar);
2017
2018
Label *edit_items_toolbar_add_label = memnew(Label);
2019
edit_items_toolbar_add_label->set_text(TTR("Add Item:"));
2020
edit_items_toolbar->add_child(edit_items_toolbar_add_label);
2021
2022
edit_items_add_color = memnew(Button);
2023
edit_items_add_color->set_tooltip_text(TTR("Add Color Item"));
2024
edit_items_add_color->set_flat(true);
2025
edit_items_add_color->set_disabled(true);
2026
edit_items_toolbar->add_child(edit_items_add_color);
2027
edit_items_add_color->connect(SceneStringName(pressed), callable_mp(this, &ThemeItemEditorDialog::_open_add_theme_item_dialog).bind(Theme::DATA_TYPE_COLOR));
2028
2029
edit_items_add_constant = memnew(Button);
2030
edit_items_add_constant->set_tooltip_text(TTR("Add Constant Item"));
2031
edit_items_add_constant->set_flat(true);
2032
edit_items_add_constant->set_disabled(true);
2033
edit_items_toolbar->add_child(edit_items_add_constant);
2034
edit_items_add_constant->connect(SceneStringName(pressed), callable_mp(this, &ThemeItemEditorDialog::_open_add_theme_item_dialog).bind(Theme::DATA_TYPE_CONSTANT));
2035
2036
edit_items_add_font = memnew(Button);
2037
edit_items_add_font->set_tooltip_text(TTR("Add Font Item"));
2038
edit_items_add_font->set_flat(true);
2039
edit_items_add_font->set_disabled(true);
2040
edit_items_toolbar->add_child(edit_items_add_font);
2041
edit_items_add_font->connect(SceneStringName(pressed), callable_mp(this, &ThemeItemEditorDialog::_open_add_theme_item_dialog).bind(Theme::DATA_TYPE_FONT));
2042
2043
edit_items_add_font_size = memnew(Button);
2044
edit_items_add_font_size->set_tooltip_text(TTR("Add Font Size Item"));
2045
edit_items_add_font_size->set_flat(true);
2046
edit_items_add_font_size->set_disabled(true);
2047
edit_items_toolbar->add_child(edit_items_add_font_size);
2048
edit_items_add_font_size->connect(SceneStringName(pressed), callable_mp(this, &ThemeItemEditorDialog::_open_add_theme_item_dialog).bind(Theme::DATA_TYPE_FONT_SIZE));
2049
2050
edit_items_add_icon = memnew(Button);
2051
edit_items_add_icon->set_tooltip_text(TTR("Add Icon Item"));
2052
edit_items_add_icon->set_flat(true);
2053
edit_items_add_icon->set_disabled(true);
2054
edit_items_toolbar->add_child(edit_items_add_icon);
2055
edit_items_add_icon->connect(SceneStringName(pressed), callable_mp(this, &ThemeItemEditorDialog::_open_add_theme_item_dialog).bind(Theme::DATA_TYPE_ICON));
2056
2057
edit_items_add_stylebox = memnew(Button);
2058
edit_items_add_stylebox->set_tooltip_text(TTR("Add StyleBox Item"));
2059
edit_items_add_stylebox->set_flat(true);
2060
edit_items_add_stylebox->set_disabled(true);
2061
edit_items_toolbar->add_child(edit_items_add_stylebox);
2062
edit_items_add_stylebox->connect(SceneStringName(pressed), callable_mp(this, &ThemeItemEditorDialog::_open_add_theme_item_dialog).bind(Theme::DATA_TYPE_STYLEBOX));
2063
2064
edit_items_toolbar->add_child(memnew(VSeparator));
2065
2066
Label *edit_items_toolbar_remove_label = memnew(Label);
2067
edit_items_toolbar_remove_label->set_text(TTR("Remove Items:"));
2068
edit_items_toolbar->add_child(edit_items_toolbar_remove_label);
2069
2070
edit_items_remove_class = memnew(Button);
2071
edit_items_remove_class->set_tooltip_text(TTR("Remove Class Items"));
2072
edit_items_remove_class->set_flat(true);
2073
edit_items_remove_class->set_disabled(true);
2074
edit_items_toolbar->add_child(edit_items_remove_class);
2075
edit_items_remove_class->connect(SceneStringName(pressed), callable_mp(this, &ThemeItemEditorDialog::_remove_class_items));
2076
2077
edit_items_remove_custom = memnew(Button);
2078
edit_items_remove_custom->set_tooltip_text(TTR("Remove Custom Items"));
2079
edit_items_remove_custom->set_flat(true);
2080
edit_items_remove_custom->set_disabled(true);
2081
edit_items_toolbar->add_child(edit_items_remove_custom);
2082
edit_items_remove_custom->connect(SceneStringName(pressed), callable_mp(this, &ThemeItemEditorDialog::_remove_custom_items));
2083
2084
edit_items_remove_all = memnew(Button);
2085
edit_items_remove_all->set_tooltip_text(TTR("Remove All Items"));
2086
edit_items_remove_all->set_flat(true);
2087
edit_items_remove_all->set_disabled(true);
2088
edit_items_toolbar->add_child(edit_items_remove_all);
2089
edit_items_remove_all->connect(SceneStringName(pressed), callable_mp(this, &ThemeItemEditorDialog::_remove_all_items));
2090
2091
edit_items_tree = memnew(Tree);
2092
edit_items_tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
2093
edit_items_tree->set_v_size_flags(Control::SIZE_EXPAND_FILL);
2094
edit_items_tree->set_hide_root(true);
2095
edit_items_tree->set_columns(1);
2096
edit_items_vb->add_child(edit_items_tree);
2097
edit_items_tree->connect("button_clicked", callable_mp(this, &ThemeItemEditorDialog::_item_tree_button_pressed));
2098
2099
edit_items_message = memnew(Label);
2100
edit_items_message->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
2101
edit_items_message->set_mouse_filter(Control::MOUSE_FILTER_STOP);
2102
edit_items_message->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
2103
edit_items_message->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
2104
edit_items_message->set_autowrap_mode(TextServer::AUTOWRAP_WORD);
2105
edit_items_tree->add_child(edit_items_message);
2106
2107
edit_theme_item_dialog = memnew(ConfirmationDialog);
2108
edit_theme_item_dialog->set_title(TTR("Add Theme Item"));
2109
add_child(edit_theme_item_dialog);
2110
VBoxContainer *edit_theme_item_vb = memnew(VBoxContainer);
2111
edit_theme_item_dialog->add_child(edit_theme_item_vb);
2112
2113
edit_theme_item_old_vb = memnew(VBoxContainer);
2114
edit_theme_item_vb->add_child(edit_theme_item_old_vb);
2115
Label *edit_theme_item_old = memnew(Label);
2116
edit_theme_item_old->set_text(TTR("Old Name:"));
2117
edit_theme_item_old_vb->add_child(edit_theme_item_old);
2118
theme_item_old_name = memnew(Label);
2119
edit_theme_item_old_vb->add_child(theme_item_old_name);
2120
2121
Label *edit_theme_item_label = memnew(Label);
2122
edit_theme_item_label->set_text(TTR("Name:"));
2123
edit_theme_item_vb->add_child(edit_theme_item_label);
2124
theme_item_name = memnew(LineEdit);
2125
edit_theme_item_vb->add_child(theme_item_name);
2126
theme_item_name->connect(SceneStringName(gui_input), callable_mp(this, &ThemeItemEditorDialog::_edit_theme_item_gui_input));
2127
edit_theme_item_dialog->connect(SceneStringName(confirmed), callable_mp(this, &ThemeItemEditorDialog::_confirm_edit_theme_item));
2128
2129
// Import Items tab.
2130
TabContainer *import_tc = memnew(TabContainer);
2131
import_tc->set_tab_alignment(TabBar::ALIGNMENT_CENTER);
2132
tc->add_child(import_tc);
2133
tc->set_tab_title(1, TTR("Import Items"));
2134
2135
import_default_theme_items = memnew(ThemeItemImportTree);
2136
import_tc->add_child(import_default_theme_items);
2137
import_tc->set_tab_title(0, TTR("Default Theme"));
2138
import_default_theme_items->connect("items_imported", callable_mp(this, &ThemeItemEditorDialog::_update_edit_types));
2139
2140
import_editor_theme_items = memnew(ThemeItemImportTree);
2141
import_tc->add_child(import_editor_theme_items);
2142
import_tc->set_tab_title(1, TTR("Editor Theme"));
2143
import_editor_theme_items->connect("items_imported", callable_mp(this, &ThemeItemEditorDialog::_update_edit_types));
2144
2145
VBoxContainer *import_another_theme_vb = memnew(VBoxContainer);
2146
2147
HBoxContainer *import_another_file_hb = memnew(HBoxContainer);
2148
import_another_theme_vb->add_child(import_another_file_hb);
2149
import_another_theme_value = memnew(LineEdit);
2150
import_another_theme_value->set_h_size_flags(Control::SIZE_EXPAND_FILL);
2151
import_another_theme_value->set_editable(false);
2152
import_another_file_hb->add_child(import_another_theme_value);
2153
import_another_theme_button = memnew(Button);
2154
import_another_file_hb->add_child(import_another_theme_button);
2155
import_another_theme_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeItemEditorDialog::_open_select_another_theme));
2156
2157
import_another_theme_dialog = memnew(EditorFileDialog);
2158
import_another_theme_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
2159
import_another_theme_dialog->set_title(TTR("Select Another Theme Resource:"));
2160
List<String> ext;
2161
ResourceLoader::get_recognized_extensions_for_type("Theme", &ext);
2162
for (const String &E : ext) {
2163
import_another_theme_dialog->add_filter("*." + E, TTR("Theme Resource"));
2164
}
2165
import_another_file_hb->add_child(import_another_theme_dialog);
2166
import_another_theme_dialog->connect("file_selected", callable_mp(this, &ThemeItemEditorDialog::_select_another_theme_cbk));
2167
2168
import_other_theme_items = memnew(ThemeItemImportTree);
2169
import_other_theme_items->set_v_size_flags(Control::SIZE_EXPAND_FILL);
2170
import_another_theme_vb->add_child(import_other_theme_items);
2171
2172
import_tc->add_child(import_another_theme_vb);
2173
import_tc->set_tab_title(2, TTR("Another Theme"));
2174
import_other_theme_items->connect("items_imported", callable_mp(this, &ThemeItemEditorDialog::_update_edit_types));
2175
2176
confirm_closing_dialog = memnew(ConfirmationDialog);
2177
confirm_closing_dialog->set_autowrap(true);
2178
add_child(confirm_closing_dialog);
2179
confirm_closing_dialog->connect(SceneStringName(confirmed), callable_mp(this, &ThemeItemEditorDialog::_close_dialog));
2180
}
2181
2182
///////////////////////
2183
2184
void ThemeTypeDialog::_dialog_about_to_show() {
2185
add_type_filter->set_text("");
2186
add_type_filter->grab_focus();
2187
2188
_update_add_type_options();
2189
}
2190
2191
void ThemeTypeDialog::ok_pressed() {
2192
_add_type_selected(add_type_filter->get_text().strip_edges());
2193
}
2194
2195
void ThemeTypeDialog::_update_add_type_options(const String &p_filter) {
2196
add_type_options->clear();
2197
2198
List<StringName> names;
2199
ThemeDB::get_singleton()->get_default_theme()->get_type_list(&names);
2200
if (include_own_types) {
2201
edited_theme->get_type_list(&names);
2202
}
2203
names.sort_custom<StringName::AlphCompare>();
2204
2205
Vector<StringName> unique_names;
2206
for (const StringName &E : names) {
2207
// Filter out undesired values.
2208
if (!p_filter.is_subsequence_ofn(String(E))) {
2209
continue;
2210
}
2211
2212
// Skip duplicate values.
2213
if (unique_names.has(E)) {
2214
continue;
2215
}
2216
unique_names.append(E);
2217
2218
Ref<Texture2D> item_icon;
2219
if (E == "") {
2220
item_icon = get_editor_theme_icon(SNAME("NodeDisabled"));
2221
} else {
2222
item_icon = EditorNode::get_singleton()->get_class_icon(E, "NodeDisabled");
2223
}
2224
2225
add_type_options->add_item(E, item_icon);
2226
}
2227
}
2228
2229
void ThemeTypeDialog::_add_type_filter_cbk(const String &p_value) {
2230
_update_add_type_options(p_value);
2231
}
2232
2233
void ThemeTypeDialog::_type_filter_input(const Ref<InputEvent> &p_event) {
2234
// Redirect navigational key events to the item list.
2235
Ref<InputEventKey> key = p_event;
2236
if (key.is_valid()) {
2237
if (key->is_action("ui_up", true) || key->is_action("ui_down", true) || key->is_action("ui_page_up") || key->is_action("ui_page_down")) {
2238
add_type_options->gui_input(key);
2239
add_type_filter->accept_event();
2240
}
2241
}
2242
}
2243
2244
void ThemeTypeDialog::_add_type_options_cbk(int p_index) {
2245
add_type_filter->set_text(add_type_options->get_item_text(p_index));
2246
add_type_filter->set_caret_column(add_type_filter->get_text().length());
2247
}
2248
2249
void ThemeTypeDialog::_add_type_dialog_entered(const String &p_value) {
2250
_add_type_selected(Theme::validate_type_name(p_value));
2251
}
2252
2253
void ThemeTypeDialog::_add_type_dialog_activated(int p_index) {
2254
_add_type_selected(add_type_options->get_item_text(p_index));
2255
}
2256
2257
void ThemeTypeDialog::_add_type_selected(const String &p_type_name) {
2258
pre_submitted_value = p_type_name;
2259
if (p_type_name.is_empty()) {
2260
add_type_confirmation->popup_centered();
2261
return;
2262
}
2263
2264
_add_type_confirmed();
2265
}
2266
2267
void ThemeTypeDialog::_add_type_confirmed() {
2268
emit_signal(SNAME("type_selected"), pre_submitted_value);
2269
hide();
2270
}
2271
2272
void ThemeTypeDialog::_notification(int p_what) {
2273
switch (p_what) {
2274
case NOTIFICATION_ENTER_TREE: {
2275
connect("about_to_popup", callable_mp(this, &ThemeTypeDialog::_dialog_about_to_show));
2276
[[fallthrough]];
2277
}
2278
case NOTIFICATION_THEME_CHANGED: {
2279
_update_add_type_options();
2280
} break;
2281
2282
case NOTIFICATION_VISIBILITY_CHANGED: {
2283
if (is_visible()) {
2284
add_type_filter->grab_focus();
2285
}
2286
} break;
2287
}
2288
}
2289
2290
void ThemeTypeDialog::_bind_methods() {
2291
ADD_SIGNAL(MethodInfo("type_selected", PropertyInfo(Variant::STRING, "type_name")));
2292
}
2293
2294
void ThemeTypeDialog::set_edited_theme(const Ref<Theme> &p_theme) {
2295
edited_theme = p_theme;
2296
}
2297
2298
void ThemeTypeDialog::set_include_own_types(bool p_enable) {
2299
include_own_types = p_enable;
2300
}
2301
2302
ThemeTypeDialog::ThemeTypeDialog() {
2303
set_hide_on_ok(false);
2304
2305
VBoxContainer *add_type_vb = memnew(VBoxContainer);
2306
add_child(add_type_vb);
2307
2308
Label *add_type_filter_label = memnew(Label);
2309
add_type_filter_label->set_text(TTR("Filter the list of types or create a new custom type:"));
2310
add_type_vb->add_child(add_type_filter_label);
2311
2312
add_type_filter = memnew(LineEdit);
2313
add_type_vb->add_child(add_type_filter);
2314
add_type_filter->connect(SceneStringName(text_changed), callable_mp(this, &ThemeTypeDialog::_add_type_filter_cbk));
2315
add_type_filter->connect(SceneStringName(text_submitted), callable_mp(this, &ThemeTypeDialog::_add_type_dialog_entered));
2316
add_type_filter->connect(SceneStringName(gui_input), callable_mp(this, &ThemeTypeDialog::_type_filter_input));
2317
2318
Label *add_type_options_label = memnew(Label);
2319
add_type_options_label->set_text(TTR("Available Node-based types:"));
2320
add_type_vb->add_child(add_type_options_label);
2321
2322
add_type_options = memnew(ItemList);
2323
add_type_options->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
2324
add_type_options->set_v_size_flags(Control::SIZE_EXPAND_FILL);
2325
add_type_vb->add_child(add_type_options);
2326
add_type_options->connect(SceneStringName(item_selected), callable_mp(this, &ThemeTypeDialog::_add_type_options_cbk));
2327
add_type_options->connect("item_activated", callable_mp(this, &ThemeTypeDialog::_add_type_dialog_activated));
2328
2329
add_type_confirmation = memnew(ConfirmationDialog);
2330
add_type_confirmation->set_title(TTR("Type name is empty!"));
2331
add_type_confirmation->set_text(TTR("Are you sure you want to create an empty type?"));
2332
add_type_confirmation->connect(SceneStringName(confirmed), callable_mp(this, &ThemeTypeDialog::_add_type_confirmed));
2333
add_child(add_type_confirmation);
2334
}
2335
2336
///////////////////////
2337
2338
Control *ThemeItemLabel::make_custom_tooltip(const String &p_text) const {
2339
return EditorHelpBitTooltip::show_tooltip(const_cast<ThemeItemLabel *>(this), p_text);
2340
}
2341
2342
VBoxContainer *ThemeTypeEditor::_create_item_list(Theme::DataType p_data_type) {
2343
VBoxContainer *items_tab = memnew(VBoxContainer);
2344
items_tab->set_custom_minimum_size(Size2(0, 160) * EDSCALE);
2345
data_type_tabs->add_child(items_tab);
2346
data_type_tabs->set_tab_title(data_type_tabs->get_tab_count() - 1, "");
2347
2348
ScrollContainer *items_sc = memnew(ScrollContainer);
2349
items_sc->set_v_size_flags(SIZE_EXPAND_FILL);
2350
items_sc->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
2351
items_tab->add_child(items_sc);
2352
VBoxContainer *items_list = memnew(VBoxContainer);
2353
items_list->set_h_size_flags(SIZE_EXPAND_FILL);
2354
items_sc->add_child(items_list);
2355
2356
HBoxContainer *item_add_hb = memnew(HBoxContainer);
2357
items_tab->add_child(item_add_hb);
2358
LineEdit *item_add_edit = memnew(LineEdit);
2359
item_add_edit->set_h_size_flags(SIZE_EXPAND_FILL);
2360
item_add_hb->add_child(item_add_edit);
2361
item_add_edit->connect(SceneStringName(text_submitted), callable_mp(this, &ThemeTypeEditor::_item_add_lineedit_cbk).bind(p_data_type, item_add_edit));
2362
Button *item_add_button = memnew(Button);
2363
item_add_button->set_text(TTR("Add"));
2364
item_add_button->set_disabled(true);
2365
item_add_hb->add_child(item_add_button);
2366
item_add_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeTypeEditor::_item_add_cbk).bind(p_data_type, item_add_edit));
2367
item_add_edit->set_meta("button", item_add_button);
2368
item_add_edit->connect(SceneStringName(text_changed), callable_mp(this, &ThemeTypeEditor::_update_add_button).bind(item_add_edit));
2369
2370
return items_list;
2371
}
2372
2373
void ThemeTypeEditor::_update_type_list() {
2374
ERR_FAIL_COND(edited_theme.is_null());
2375
2376
if (updating) {
2377
return;
2378
}
2379
updating = true;
2380
2381
Control *focused = get_viewport()->gui_get_focus_owner();
2382
if (focused) {
2383
if (focusables.has(focused)) {
2384
// If focus is currently on one of the internal property editors, don't update.
2385
updating = false;
2386
return;
2387
}
2388
2389
Node *focus_parent = focused->get_parent();
2390
while (focus_parent) {
2391
Control *c = Object::cast_to<Control>(focus_parent);
2392
if (c && focusables.has(c)) {
2393
// If focus is currently on one of the internal property editors, don't update.
2394
updating = false;
2395
return;
2396
}
2397
2398
focus_parent = focus_parent->get_parent();
2399
}
2400
}
2401
2402
List<StringName> theme_types;
2403
edited_theme->get_type_list(&theme_types);
2404
theme_types.sort_custom<StringName::AlphCompare>();
2405
2406
theme_type_list->clear();
2407
2408
if (theme_types.is_empty()) {
2409
theme_type_list->set_disabled(true);
2410
theme_type_list->add_item(TTRC("None"));
2411
theme_type_list->set_item_auto_translate_mode(-1, AUTO_TRANSLATE_MODE_ALWAYS);
2412
2413
edited_type = "";
2414
_update_type_items();
2415
} else {
2416
theme_type_list->set_disabled(false);
2417
2418
bool item_reselected = false;
2419
int e_idx = 0;
2420
for (const StringName &E : theme_types) {
2421
Ref<Texture2D> item_icon;
2422
if (E == "") {
2423
item_icon = get_editor_theme_icon(SNAME("NodeDisabled"));
2424
} else {
2425
item_icon = EditorNode::get_singleton()->get_class_icon(E, "NodeDisabled");
2426
}
2427
theme_type_list->add_icon_item(item_icon, E);
2428
2429
if (E == edited_type) {
2430
theme_type_list->select(e_idx);
2431
item_reselected = true;
2432
}
2433
e_idx++;
2434
}
2435
2436
if (item_reselected) {
2437
_update_type_items();
2438
} else {
2439
theme_type_list->select(0);
2440
_list_type_selected(0);
2441
}
2442
}
2443
2444
rename_type_button->set_disabled(theme_types.is_empty());
2445
remove_type_button->set_disabled(theme_types.is_empty());
2446
2447
updating = false;
2448
}
2449
2450
void ThemeTypeEditor::_update_type_list_debounced() {
2451
update_debounce_timer->start();
2452
}
2453
2454
HashMap<StringName, bool> ThemeTypeEditor::_get_type_items(String p_type_name, Theme::DataType p_type, bool p_include_default) {
2455
HashMap<StringName, bool> items;
2456
List<StringName> names;
2457
2458
if (p_include_default) {
2459
names.clear();
2460
String default_type;
2461
2462
{
2463
const StringName variation_base = edited_theme->get_type_variation_base(p_type_name);
2464
if (variation_base != StringName()) {
2465
default_type = variation_base;
2466
}
2467
}
2468
2469
if (default_type.is_empty()) {
2470
// If variation base was not found in the edited theme, look in the default theme.
2471
const StringName variation_base = ThemeDB::get_singleton()->get_default_theme()->get_type_variation_base(p_type_name);
2472
if (variation_base != StringName()) {
2473
default_type = variation_base;
2474
}
2475
}
2476
2477
if (default_type.is_empty()) {
2478
default_type = p_type_name;
2479
}
2480
2481
List<ThemeDB::ThemeItemBind> theme_binds;
2482
ThemeDB::get_singleton()->get_class_items(default_type, &theme_binds, true, p_type);
2483
for (const ThemeDB::ThemeItemBind &E : theme_binds) {
2484
names.push_back(E.item_name);
2485
}
2486
2487
names.sort_custom<StringName::AlphCompare>();
2488
for (const StringName &E : names) {
2489
items[E] = false;
2490
}
2491
}
2492
2493
{
2494
names.clear();
2495
edited_theme->get_theme_item_list(p_type, p_type_name, &names);
2496
names.sort_custom<StringName::AlphCompare>();
2497
for (const StringName &E : names) {
2498
items[E] = true;
2499
}
2500
}
2501
2502
List<StringName> keys;
2503
for (const KeyValue<StringName, bool> &E : items) {
2504
keys.push_back(E.key);
2505
}
2506
keys.sort_custom<StringName::AlphCompare>();
2507
2508
HashMap<StringName, bool> ordered_items;
2509
for (const StringName &E : keys) {
2510
ordered_items[E] = items[E];
2511
}
2512
2513
return ordered_items;
2514
}
2515
2516
HBoxContainer *ThemeTypeEditor::_create_property_control(Theme::DataType p_data_type, String p_item_name, bool p_editable) {
2517
HBoxContainer *item_control = memnew(HBoxContainer);
2518
2519
HBoxContainer *item_name_container = memnew(HBoxContainer);
2520
item_name_container->set_h_size_flags(SIZE_EXPAND_FILL);
2521
item_name_container->set_stretch_ratio(2.0);
2522
item_control->add_child(item_name_container);
2523
2524
Label *item_name = memnew(ThemeItemLabel);
2525
item_name->set_h_size_flags(SIZE_EXPAND_FILL);
2526
item_name->set_clip_text(true);
2527
item_name->set_text(p_item_name);
2528
// `|` separators used in `EditorHelpBit`.
2529
item_name->set_tooltip_text("theme_item|" + edited_type + "|" + p_item_name);
2530
item_name->set_mouse_filter(Control::MOUSE_FILTER_STOP);
2531
item_name_container->add_child(item_name);
2532
2533
if (p_editable) {
2534
LineEdit *item_name_edit = memnew(LineEdit);
2535
item_name_edit->set_h_size_flags(SIZE_EXPAND_FILL);
2536
item_name_edit->set_text(p_item_name);
2537
item_name_container->add_child(item_name_edit);
2538
item_name_edit->connect(SceneStringName(text_submitted), callable_mp(this, &ThemeTypeEditor::_item_rename_entered).bind(p_data_type, p_item_name, item_name_container));
2539
item_name_edit->hide();
2540
2541
Button *item_rename_button = memnew(Button);
2542
item_rename_button->set_button_icon(get_editor_theme_icon(SNAME("Edit")));
2543
item_rename_button->set_tooltip_text(TTR("Rename Item"));
2544
item_rename_button->set_flat(true);
2545
item_name_container->add_child(item_rename_button);
2546
item_rename_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeTypeEditor::_item_rename_cbk).bind(p_data_type, p_item_name, item_name_container));
2547
2548
Button *item_remove_button = memnew(Button);
2549
item_remove_button->set_button_icon(get_editor_theme_icon(SNAME("Remove")));
2550
item_remove_button->set_tooltip_text(TTR("Remove Item"));
2551
item_remove_button->set_flat(true);
2552
item_name_container->add_child(item_remove_button);
2553
item_remove_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeTypeEditor::_item_remove_cbk).bind(p_data_type, p_item_name));
2554
2555
Button *item_rename_confirm_button = memnew(Button);
2556
item_rename_confirm_button->set_button_icon(get_editor_theme_icon(SNAME("ImportCheck")));
2557
item_rename_confirm_button->set_tooltip_text(TTR("Confirm Item Rename"));
2558
item_rename_confirm_button->set_flat(true);
2559
item_name_container->add_child(item_rename_confirm_button);
2560
item_rename_confirm_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeTypeEditor::_item_rename_confirmed).bind(p_data_type, p_item_name, item_name_container));
2561
item_rename_confirm_button->hide();
2562
2563
Button *item_rename_cancel_button = memnew(Button);
2564
item_rename_cancel_button->set_button_icon(get_editor_theme_icon(SNAME("ImportFail")));
2565
item_rename_cancel_button->set_tooltip_text(TTR("Cancel Item Rename"));
2566
item_rename_cancel_button->set_flat(true);
2567
item_name_container->add_child(item_rename_cancel_button);
2568
item_rename_cancel_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeTypeEditor::_item_rename_canceled).bind(p_data_type, p_item_name, item_name_container));
2569
item_rename_cancel_button->hide();
2570
} else {
2571
item_name->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("font_disabled_color"), EditorStringName(Editor)));
2572
2573
Button *item_override_button = memnew(Button);
2574
item_override_button->set_button_icon(get_editor_theme_icon(SNAME("Add")));
2575
item_override_button->set_tooltip_text(TTR("Override Item"));
2576
item_override_button->set_flat(true);
2577
item_name_container->add_child(item_override_button);
2578
item_override_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeTypeEditor::_item_override_cbk).bind(p_data_type, p_item_name));
2579
}
2580
2581
return item_control;
2582
}
2583
2584
void ThemeTypeEditor::_add_focusable(Control *p_control) {
2585
focusables.append(p_control);
2586
}
2587
2588
void ThemeTypeEditor::_update_type_items() {
2589
bool show_default = show_default_items_button->is_pressed();
2590
2591
focusables.clear();
2592
2593
// Colors.
2594
{
2595
for (int i = color_items_list->get_child_count() - 1; i >= 0; i--) {
2596
Node *node = color_items_list->get_child(i);
2597
node->queue_free();
2598
color_items_list->remove_child(node);
2599
}
2600
2601
HashMap<StringName, bool> color_items = _get_type_items(edited_type, Theme::DATA_TYPE_COLOR, show_default);
2602
for (const KeyValue<StringName, bool> &E : color_items) {
2603
HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_COLOR, E.key, E.value);
2604
ColorPickerButton *item_editor = memnew(ColorPickerButton);
2605
item_editor->set_h_size_flags(SIZE_EXPAND_FILL);
2606
item_control->add_child(item_editor);
2607
2608
if (E.value) {
2609
item_editor->set_pick_color(edited_theme->get_color(E.key, edited_type));
2610
item_editor->connect("color_changed", callable_mp(this, &ThemeTypeEditor::_color_item_changed).bind(E.key));
2611
item_editor->get_popup()->connect("about_to_popup", callable_mp(EditorNode::get_singleton(), &EditorNode::setup_color_picker).bind(item_editor->get_picker()));
2612
} else {
2613
item_editor->set_pick_color(ThemeDB::get_singleton()->get_default_theme()->get_color(E.key, edited_type));
2614
item_editor->set_disabled(true);
2615
}
2616
2617
_add_focusable(item_editor);
2618
color_items_list->add_child(item_control);
2619
}
2620
}
2621
2622
// Constants.
2623
{
2624
for (int i = constant_items_list->get_child_count() - 1; i >= 0; i--) {
2625
Node *node = constant_items_list->get_child(i);
2626
node->queue_free();
2627
constant_items_list->remove_child(node);
2628
}
2629
2630
HashMap<StringName, bool> constant_items = _get_type_items(edited_type, Theme::DATA_TYPE_CONSTANT, show_default);
2631
for (const KeyValue<StringName, bool> &E : constant_items) {
2632
HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_CONSTANT, E.key, E.value);
2633
SpinBox *item_editor = memnew(SpinBox);
2634
item_editor->set_h_size_flags(SIZE_EXPAND_FILL);
2635
item_editor->set_min(-100000);
2636
item_editor->set_max(100000);
2637
item_editor->set_step(1);
2638
item_editor->set_allow_lesser(true);
2639
item_editor->set_allow_greater(true);
2640
item_control->add_child(item_editor);
2641
2642
if (E.value) {
2643
item_editor->set_value(edited_theme->get_constant(E.key, edited_type));
2644
item_editor->connect(SceneStringName(value_changed), callable_mp(this, &ThemeTypeEditor::_constant_item_changed).bind(E.key));
2645
} else {
2646
item_editor->set_value(ThemeDB::get_singleton()->get_default_theme()->get_constant(E.key, edited_type));
2647
item_editor->set_editable(false);
2648
}
2649
2650
_add_focusable(item_editor);
2651
constant_items_list->add_child(item_control);
2652
}
2653
}
2654
2655
// Fonts.
2656
{
2657
for (int i = font_items_list->get_child_count() - 1; i >= 0; i--) {
2658
Node *node = font_items_list->get_child(i);
2659
node->queue_free();
2660
font_items_list->remove_child(node);
2661
}
2662
2663
HashMap<StringName, bool> font_items = _get_type_items(edited_type, Theme::DATA_TYPE_FONT, show_default);
2664
for (const KeyValue<StringName, bool> &E : font_items) {
2665
HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_FONT, E.key, E.value);
2666
EditorResourcePicker *item_editor = memnew(EditorResourcePicker);
2667
item_editor->set_h_size_flags(SIZE_EXPAND_FILL);
2668
item_editor->set_base_type("Font");
2669
item_editor->set_resource_owner(*edited_theme);
2670
item_control->add_child(item_editor);
2671
2672
if (E.value) {
2673
if (edited_theme->has_font(E.key, edited_type)) {
2674
item_editor->set_edited_resource(edited_theme->get_font(E.key, edited_type));
2675
} else {
2676
item_editor->set_edited_resource(Ref<Resource>());
2677
}
2678
item_editor->connect("resource_selected", callable_mp(this, &ThemeTypeEditor::_edit_resource_item));
2679
item_editor->connect("resource_changed", callable_mp(this, &ThemeTypeEditor::_font_item_changed).bind(E.key));
2680
} else {
2681
if (ThemeDB::get_singleton()->get_default_theme()->has_font(E.key, edited_type)) {
2682
item_editor->set_edited_resource(ThemeDB::get_singleton()->get_default_theme()->get_font(E.key, edited_type));
2683
} else {
2684
item_editor->set_edited_resource(Ref<Resource>());
2685
}
2686
item_editor->set_editable(false);
2687
}
2688
2689
_add_focusable(item_editor);
2690
font_items_list->add_child(item_control);
2691
}
2692
}
2693
2694
// Fonts sizes.
2695
{
2696
for (int i = font_size_items_list->get_child_count() - 1; i >= 0; i--) {
2697
Node *node = font_size_items_list->get_child(i);
2698
node->queue_free();
2699
font_size_items_list->remove_child(node);
2700
}
2701
2702
HashMap<StringName, bool> font_size_items = _get_type_items(edited_type, Theme::DATA_TYPE_FONT_SIZE, show_default);
2703
for (const KeyValue<StringName, bool> &E : font_size_items) {
2704
HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_FONT_SIZE, E.key, E.value);
2705
SpinBox *item_editor = memnew(SpinBox);
2706
item_editor->set_h_size_flags(SIZE_EXPAND_FILL);
2707
item_editor->set_min(-100000);
2708
item_editor->set_max(100000);
2709
item_editor->set_step(1);
2710
item_editor->set_allow_lesser(true);
2711
item_editor->set_allow_greater(true);
2712
item_control->add_child(item_editor);
2713
2714
if (E.value) {
2715
item_editor->set_value(edited_theme->get_font_size(E.key, edited_type));
2716
item_editor->connect(SceneStringName(value_changed), callable_mp(this, &ThemeTypeEditor::_font_size_item_changed).bind(E.key));
2717
} else {
2718
item_editor->set_value(ThemeDB::get_singleton()->get_default_theme()->get_font_size(E.key, edited_type));
2719
item_editor->set_editable(false);
2720
}
2721
2722
_add_focusable(item_editor);
2723
font_size_items_list->add_child(item_control);
2724
}
2725
}
2726
2727
// Icons.
2728
{
2729
for (int i = icon_items_list->get_child_count() - 1; i >= 0; i--) {
2730
Node *node = icon_items_list->get_child(i);
2731
node->queue_free();
2732
icon_items_list->remove_child(node);
2733
}
2734
2735
HashMap<StringName, bool> icon_items = _get_type_items(edited_type, Theme::DATA_TYPE_ICON, show_default);
2736
for (const KeyValue<StringName, bool> &E : icon_items) {
2737
HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_ICON, E.key, E.value);
2738
EditorResourcePicker *item_editor = memnew(EditorResourcePicker);
2739
item_editor->set_h_size_flags(SIZE_EXPAND_FILL);
2740
item_editor->set_base_type("Texture2D");
2741
item_editor->set_resource_owner(*edited_theme);
2742
item_control->add_child(item_editor);
2743
2744
if (E.value) {
2745
if (edited_theme->has_icon(E.key, edited_type)) {
2746
item_editor->set_edited_resource(edited_theme->get_icon(E.key, edited_type));
2747
} else {
2748
item_editor->set_edited_resource(Ref<Resource>());
2749
}
2750
item_editor->connect("resource_selected", callable_mp(this, &ThemeTypeEditor::_edit_resource_item));
2751
item_editor->connect("resource_changed", callable_mp(this, &ThemeTypeEditor::_icon_item_changed).bind(E.key));
2752
} else {
2753
if (ThemeDB::get_singleton()->get_default_theme()->has_icon(E.key, edited_type)) {
2754
item_editor->set_edited_resource(ThemeDB::get_singleton()->get_default_theme()->get_icon(E.key, edited_type));
2755
} else {
2756
item_editor->set_edited_resource(Ref<Resource>());
2757
}
2758
item_editor->set_editable(false);
2759
}
2760
2761
_add_focusable(item_editor);
2762
icon_items_list->add_child(item_control);
2763
}
2764
}
2765
2766
// Styleboxes.
2767
{
2768
for (int i = stylebox_items_list->get_child_count() - 1; i >= 0; i--) {
2769
Node *node = stylebox_items_list->get_child(i);
2770
node->queue_free();
2771
stylebox_items_list->remove_child(node);
2772
}
2773
2774
if (leading_stylebox.pinned) {
2775
HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_STYLEBOX, leading_stylebox.item_name, true);
2776
EditorResourcePicker *item_editor = memnew(EditorResourcePicker);
2777
item_editor->set_h_size_flags(SIZE_EXPAND_FILL);
2778
item_editor->set_stretch_ratio(1.5);
2779
item_editor->set_base_type("StyleBox");
2780
item_editor->set_resource_owner(*edited_theme);
2781
2782
Button *pin_leader_button = memnew(Button);
2783
pin_leader_button->set_flat(true);
2784
pin_leader_button->set_toggle_mode(true);
2785
pin_leader_button->set_pressed(true);
2786
pin_leader_button->set_button_icon(get_editor_theme_icon(SNAME("Pin")));
2787
pin_leader_button->set_tooltip_text(TTR("Unpin this StyleBox as a main style."));
2788
item_control->add_child(pin_leader_button);
2789
pin_leader_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeTypeEditor::_on_unpin_leader_button_pressed));
2790
2791
item_control->add_child(item_editor);
2792
2793
if (edited_theme->has_stylebox(leading_stylebox.item_name, edited_type)) {
2794
item_editor->set_edited_resource(leading_stylebox.stylebox);
2795
} else {
2796
item_editor->set_edited_resource(Ref<Resource>());
2797
}
2798
item_editor->connect("resource_selected", callable_mp(this, &ThemeTypeEditor::_edit_resource_item));
2799
item_editor->connect("resource_changed", callable_mp(this, &ThemeTypeEditor::_stylebox_item_changed).bind(leading_stylebox.item_name));
2800
2801
stylebox_items_list->add_child(item_control);
2802
stylebox_items_list->add_child(memnew(HSeparator));
2803
}
2804
2805
HashMap<StringName, bool> stylebox_items = _get_type_items(edited_type, Theme::DATA_TYPE_STYLEBOX, show_default);
2806
for (const KeyValue<StringName, bool> &E : stylebox_items) {
2807
if (leading_stylebox.pinned && leading_stylebox.item_name == E.key) {
2808
continue;
2809
}
2810
2811
HBoxContainer *item_control = _create_property_control(Theme::DATA_TYPE_STYLEBOX, E.key, E.value);
2812
EditorResourcePicker *item_editor = memnew(EditorResourcePicker);
2813
item_editor->set_h_size_flags(SIZE_EXPAND_FILL);
2814
item_editor->set_stretch_ratio(1.5);
2815
item_editor->set_base_type("StyleBox");
2816
item_editor->set_resource_owner(*edited_theme);
2817
2818
if (E.value) {
2819
if (edited_theme->has_stylebox(E.key, edited_type)) {
2820
item_editor->set_edited_resource(edited_theme->get_stylebox(E.key, edited_type));
2821
} else {
2822
item_editor->set_edited_resource(Ref<Resource>());
2823
}
2824
item_editor->connect("resource_selected", callable_mp(this, &ThemeTypeEditor::_edit_resource_item));
2825
item_editor->connect("resource_changed", callable_mp(this, &ThemeTypeEditor::_stylebox_item_changed).bind(E.key));
2826
2827
Button *pin_leader_button = memnew(Button);
2828
pin_leader_button->set_flat(true);
2829
pin_leader_button->set_toggle_mode(true);
2830
pin_leader_button->set_button_icon(get_editor_theme_icon(SNAME("Pin")));
2831
pin_leader_button->set_tooltip_text(TTR("Pin this StyleBox as a main style. Editing its properties will update the same properties in all other StyleBoxes of this type."));
2832
pin_leader_button->set_accessibility_name(TTRC("Pin this StyleBox as a main style."));
2833
item_control->add_child(pin_leader_button);
2834
pin_leader_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeTypeEditor::_on_pin_leader_button_pressed).bind(item_editor, E.key));
2835
} else {
2836
if (ThemeDB::get_singleton()->get_default_theme()->has_stylebox(E.key, edited_type)) {
2837
item_editor->set_edited_resource(ThemeDB::get_singleton()->get_default_theme()->get_stylebox(E.key, edited_type));
2838
} else {
2839
item_editor->set_edited_resource(Ref<Resource>());
2840
}
2841
item_editor->set_editable(false);
2842
}
2843
2844
item_control->add_child(item_editor);
2845
_add_focusable(item_editor);
2846
stylebox_items_list->add_child(item_control);
2847
}
2848
}
2849
2850
// Various type settings.
2851
if (edited_type.is_empty() || ClassDB::class_exists(edited_type)) {
2852
type_variation_edit->set_editable(false);
2853
type_variation_edit->set_text("");
2854
type_variation_button->hide();
2855
type_variation_locked->set_visible(!edited_type.is_empty());
2856
} else {
2857
type_variation_edit->set_editable(true);
2858
type_variation_edit->set_text(edited_theme->get_type_variation_base(edited_type));
2859
_add_focusable(type_variation_edit);
2860
type_variation_button->show();
2861
type_variation_locked->hide();
2862
}
2863
}
2864
2865
void ThemeTypeEditor::_list_type_selected(int p_index) {
2866
edited_type = theme_type_list->get_item_text(p_index);
2867
_update_type_items();
2868
}
2869
2870
void ThemeTypeEditor::_add_type_button_cbk() {
2871
add_type_mode = ADD_THEME_TYPE;
2872
add_type_dialog->set_title(TTR("Add Item Type"));
2873
add_type_dialog->set_ok_button_text(TTR("Add Type"));
2874
add_type_dialog->set_include_own_types(false);
2875
add_type_dialog->popup_centered(Size2(560, 420) * EDSCALE);
2876
}
2877
2878
void ThemeTypeEditor::_rename_type_button_cbk() {
2879
theme_type_rename_line_edit->set_text(edited_type);
2880
theme_type_rename_dialog->reset_size();
2881
theme_type_rename_dialog->popup_centered();
2882
theme_type_rename_line_edit->grab_focus();
2883
}
2884
2885
void ThemeTypeEditor::_theme_type_rename_dialog_confirmed() {
2886
const String &new_type_name = Theme::validate_type_name(theme_type_rename_line_edit->get_text());
2887
if (edited_type == new_type_name) {
2888
return;
2889
}
2890
2891
List<StringName> theme_types;
2892
edited_theme->get_type_list(&theme_types);
2893
if (theme_types.find(new_type_name) != nullptr) {
2894
return;
2895
}
2896
2897
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
2898
ur->create_action(TTR("Rename Theme Type"));
2899
2900
_rename_theme_type(ur, *edited_theme, edited_type, new_type_name);
2901
2902
ur->add_do_method(this, "select_type", new_type_name);
2903
ur->add_undo_method(this, "select_type", edited_type);
2904
2905
ur->commit_action();
2906
}
2907
2908
void ThemeTypeEditor::_remove_type_button_cbk() {
2909
Ref<Theme> old_snapshot = edited_theme->duplicate();
2910
2911
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
2912
ur->create_action(TTR("Remove Theme Type"));
2913
2914
ur->add_do_method(*edited_theme, "remove_type", edited_type);
2915
// If the type was empty, it cannot be restored with merge, but thankfully we can fake it.
2916
ur->add_undo_method(*edited_theme, "add_type", edited_type);
2917
ur->add_undo_method(*edited_theme, "merge_with", old_snapshot);
2918
2919
ur->commit_action();
2920
}
2921
2922
void ThemeTypeEditor::_add_default_type_items() {
2923
List<StringName> names;
2924
String default_type = edited_type;
2925
if (edited_theme->get_type_variation_base(edited_type) != StringName()) {
2926
default_type = edited_theme->get_type_variation_base(edited_type);
2927
}
2928
2929
Ref<Theme> old_snapshot = edited_theme->duplicate();
2930
Ref<Theme> new_snapshot = edited_theme->duplicate();
2931
2932
updating = true;
2933
2934
{
2935
names.clear();
2936
ThemeDB::get_singleton()->get_default_theme()->get_icon_list(default_type, &names);
2937
for (const StringName &E : names) {
2938
if (!new_snapshot->has_icon(E, edited_type)) {
2939
new_snapshot->set_icon(E, edited_type, ThemeDB::get_singleton()->get_default_theme()->get_icon(E, edited_type));
2940
}
2941
}
2942
}
2943
{
2944
names.clear();
2945
ThemeDB::get_singleton()->get_default_theme()->get_stylebox_list(default_type, &names);
2946
for (const StringName &E : names) {
2947
if (!new_snapshot->has_stylebox(E, edited_type)) {
2948
new_snapshot->set_stylebox(E, edited_type, ThemeDB::get_singleton()->get_default_theme()->get_stylebox(E, edited_type));
2949
}
2950
}
2951
}
2952
{
2953
names.clear();
2954
ThemeDB::get_singleton()->get_default_theme()->get_font_list(default_type, &names);
2955
for (const StringName &E : names) {
2956
if (!new_snapshot->has_font(E, edited_type)) {
2957
new_snapshot->set_font(E, edited_type, ThemeDB::get_singleton()->get_default_theme()->get_font(E, edited_type));
2958
}
2959
}
2960
}
2961
{
2962
names.clear();
2963
ThemeDB::get_singleton()->get_default_theme()->get_font_size_list(default_type, &names);
2964
for (const StringName &E : names) {
2965
if (!new_snapshot->has_font_size(E, edited_type)) {
2966
new_snapshot->set_font_size(E, edited_type, ThemeDB::get_singleton()->get_default_theme()->get_font_size(E, edited_type));
2967
}
2968
}
2969
}
2970
{
2971
names.clear();
2972
ThemeDB::get_singleton()->get_default_theme()->get_color_list(default_type, &names);
2973
for (const StringName &E : names) {
2974
if (!new_snapshot->has_color(E, edited_type)) {
2975
new_snapshot->set_color(E, edited_type, ThemeDB::get_singleton()->get_default_theme()->get_color(E, edited_type));
2976
}
2977
}
2978
}
2979
{
2980
names.clear();
2981
ThemeDB::get_singleton()->get_default_theme()->get_constant_list(default_type, &names);
2982
for (const StringName &E : names) {
2983
if (!new_snapshot->has_constant(E, edited_type)) {
2984
new_snapshot->set_constant(E, edited_type, ThemeDB::get_singleton()->get_default_theme()->get_constant(E, edited_type));
2985
}
2986
}
2987
}
2988
2989
updating = false;
2990
2991
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
2992
ur->create_action(TTR("Override All Default Theme Items"));
2993
2994
ur->add_do_method(*edited_theme, "merge_with", new_snapshot);
2995
ur->add_undo_method(*edited_theme, "clear");
2996
ur->add_undo_method(*edited_theme, "merge_with", old_snapshot);
2997
2998
ur->add_do_method(this, "_update_type_items");
2999
ur->add_undo_method(this, "_update_type_items");
3000
3001
ur->commit_action();
3002
}
3003
3004
void ThemeTypeEditor::_update_add_button(const String &p_text, LineEdit *p_for_edit) {
3005
Button *button = Object::cast_to<Button>(p_for_edit->get_meta("button"));
3006
button->set_disabled(p_text.strip_edges().is_empty());
3007
}
3008
3009
void ThemeTypeEditor::_item_add_cbk(int p_data_type, Control *p_control) {
3010
LineEdit *le = Object::cast_to<LineEdit>(p_control);
3011
if (le->get_text().strip_edges().is_empty()) {
3012
return;
3013
}
3014
3015
const String item_name = le->get_text().strip_edges().validate_ascii_identifier();
3016
3017
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
3018
ur->create_action(TTR("Add Theme Item"));
3019
3020
switch (p_data_type) {
3021
case Theme::DATA_TYPE_COLOR: {
3022
ur->add_do_method(*edited_theme, "set_color", item_name, edited_type, Color());
3023
ur->add_undo_method(*edited_theme, "clear_color", item_name, edited_type);
3024
} break;
3025
case Theme::DATA_TYPE_CONSTANT: {
3026
ur->add_do_method(*edited_theme, "set_constant", item_name, edited_type, 0);
3027
ur->add_undo_method(*edited_theme, "clear_constant", item_name, edited_type);
3028
} break;
3029
case Theme::DATA_TYPE_FONT: {
3030
ur->add_do_method(*edited_theme, "set_font", item_name, edited_type, Ref<Font>());
3031
ur->add_undo_method(*edited_theme, "clear_font", item_name, edited_type);
3032
} break;
3033
case Theme::DATA_TYPE_FONT_SIZE: {
3034
ur->add_do_method(*edited_theme, "set_font_size", item_name, edited_type, -1);
3035
ur->add_undo_method(*edited_theme, "clear_font_size", item_name, edited_type);
3036
} break;
3037
case Theme::DATA_TYPE_ICON: {
3038
ur->add_do_method(*edited_theme, "set_icon", item_name, edited_type, Ref<Texture2D>());
3039
ur->add_undo_method(*edited_theme, "clear_icon", item_name, edited_type);
3040
} break;
3041
case Theme::DATA_TYPE_STYLEBOX: {
3042
Ref<StyleBox> sb;
3043
ur->add_do_method(*edited_theme, "set_stylebox", item_name, edited_type, sb);
3044
ur->add_undo_method(*edited_theme, "clear_stylebox", item_name, edited_type);
3045
3046
if (is_stylebox_pinned(sb)) {
3047
ur->add_undo_method(this, "_unpin_leading_stylebox");
3048
}
3049
} break;
3050
}
3051
3052
ur->commit_action();
3053
3054
le->set_text("");
3055
_update_add_button("", le);
3056
}
3057
3058
void ThemeTypeEditor::_item_add_lineedit_cbk(String p_value, int p_data_type, Control *p_control) {
3059
_item_add_cbk(p_data_type, p_control);
3060
}
3061
3062
void ThemeTypeEditor::_item_override_cbk(int p_data_type, String p_item_name) {
3063
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
3064
ur->create_action(TTR("Override Theme Item"));
3065
3066
switch (p_data_type) {
3067
case Theme::DATA_TYPE_COLOR: {
3068
ur->add_do_method(*edited_theme, "set_color", p_item_name, edited_type, ThemeDB::get_singleton()->get_default_theme()->get_color(p_item_name, edited_type));
3069
ur->add_undo_method(*edited_theme, "clear_color", p_item_name, edited_type);
3070
} break;
3071
case Theme::DATA_TYPE_CONSTANT: {
3072
ur->add_do_method(*edited_theme, "set_constant", p_item_name, edited_type, ThemeDB::get_singleton()->get_default_theme()->get_constant(p_item_name, edited_type));
3073
ur->add_undo_method(*edited_theme, "clear_constant", p_item_name, edited_type);
3074
} break;
3075
case Theme::DATA_TYPE_FONT: {
3076
ur->add_do_method(*edited_theme, "set_font", p_item_name, edited_type, Ref<Font>());
3077
ur->add_undo_method(*edited_theme, "clear_font", p_item_name, edited_type);
3078
} break;
3079
case Theme::DATA_TYPE_FONT_SIZE: {
3080
ur->add_do_method(*edited_theme, "set_font_size", p_item_name, edited_type, ThemeDB::get_singleton()->get_default_theme()->get_font_size(p_item_name, edited_type));
3081
ur->add_undo_method(*edited_theme, "clear_font_size", p_item_name, edited_type);
3082
} break;
3083
case Theme::DATA_TYPE_ICON: {
3084
ur->add_do_method(*edited_theme, "set_icon", p_item_name, edited_type, Ref<Texture2D>());
3085
ur->add_undo_method(*edited_theme, "clear_icon", p_item_name, edited_type);
3086
} break;
3087
case Theme::DATA_TYPE_STYLEBOX: {
3088
Ref<StyleBox> sb;
3089
ur->add_do_method(*edited_theme, "set_stylebox", p_item_name, edited_type, sb);
3090
ur->add_undo_method(*edited_theme, "clear_stylebox", p_item_name, edited_type);
3091
3092
if (is_stylebox_pinned(sb)) {
3093
ur->add_undo_method(this, "_unpin_leading_stylebox");
3094
}
3095
} break;
3096
}
3097
3098
ur->commit_action();
3099
}
3100
3101
void ThemeTypeEditor::_item_remove_cbk(int p_data_type, String p_item_name) {
3102
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
3103
ur->create_action(TTR("Remove Theme Item"));
3104
3105
switch (p_data_type) {
3106
case Theme::DATA_TYPE_COLOR: {
3107
ur->add_do_method(*edited_theme, "clear_color", p_item_name, edited_type);
3108
ur->add_undo_method(*edited_theme, "set_color", p_item_name, edited_type, edited_theme->get_color(p_item_name, edited_type));
3109
} break;
3110
case Theme::DATA_TYPE_CONSTANT: {
3111
ur->add_do_method(*edited_theme, "clear_constant", p_item_name, edited_type);
3112
ur->add_undo_method(*edited_theme, "set_constant", p_item_name, edited_type, edited_theme->get_constant(p_item_name, edited_type));
3113
} break;
3114
case Theme::DATA_TYPE_FONT: {
3115
ur->add_do_method(*edited_theme, "clear_font", p_item_name, edited_type);
3116
if (edited_theme->has_font(p_item_name, edited_type)) {
3117
ur->add_undo_method(*edited_theme, "set_font", p_item_name, edited_type, edited_theme->get_font(p_item_name, edited_type));
3118
} else {
3119
ur->add_undo_method(*edited_theme, "set_font", p_item_name, edited_type, Ref<Font>());
3120
}
3121
} break;
3122
case Theme::DATA_TYPE_FONT_SIZE: {
3123
ur->add_do_method(*edited_theme, "clear_font_size", p_item_name, edited_type);
3124
ur->add_undo_method(*edited_theme, "set_font_size", p_item_name, edited_type, edited_theme->get_font_size(p_item_name, edited_type));
3125
} break;
3126
case Theme::DATA_TYPE_ICON: {
3127
ur->add_do_method(*edited_theme, "clear_icon", p_item_name, edited_type);
3128
if (edited_theme->has_icon(p_item_name, edited_type)) {
3129
ur->add_undo_method(*edited_theme, "set_icon", p_item_name, edited_type, edited_theme->get_icon(p_item_name, edited_type));
3130
} else {
3131
ur->add_undo_method(*edited_theme, "set_icon", p_item_name, edited_type, Ref<Texture2D>());
3132
}
3133
} break;
3134
case Theme::DATA_TYPE_STYLEBOX: {
3135
Ref<StyleBox> sb = edited_theme->get_stylebox(p_item_name, edited_type);
3136
ur->add_do_method(*edited_theme, "clear_stylebox", p_item_name, edited_type);
3137
if (edited_theme->has_stylebox(p_item_name, edited_type)) {
3138
ur->add_undo_method(*edited_theme, "set_stylebox", p_item_name, edited_type, sb);
3139
} else {
3140
ur->add_undo_method(*edited_theme, "set_stylebox", p_item_name, edited_type, Ref<StyleBox>());
3141
}
3142
3143
if (is_stylebox_pinned(sb)) {
3144
ur->add_do_method(this, "_unpin_leading_stylebox");
3145
ur->add_undo_method(this, "_pin_leading_stylebox", p_item_name, sb);
3146
}
3147
} break;
3148
}
3149
3150
ur->commit_action();
3151
}
3152
3153
void ThemeTypeEditor::_item_rename_cbk(int p_data_type, String p_item_name, Control *p_control) {
3154
// Label
3155
Object::cast_to<Label>(p_control->get_child(0))->hide();
3156
// Label buttons
3157
Object::cast_to<Button>(p_control->get_child(2))->hide();
3158
Object::cast_to<Button>(p_control->get_child(3))->hide();
3159
3160
// LineEdit
3161
Object::cast_to<LineEdit>(p_control->get_child(1))->set_text(p_item_name);
3162
Object::cast_to<LineEdit>(p_control->get_child(1))->show();
3163
// LineEdit buttons
3164
Object::cast_to<Button>(p_control->get_child(4))->show();
3165
Object::cast_to<Button>(p_control->get_child(5))->show();
3166
}
3167
3168
void ThemeTypeEditor::_item_rename_confirmed(int p_data_type, String p_item_name, Control *p_control) {
3169
LineEdit *le = Object::cast_to<LineEdit>(p_control->get_child(1));
3170
if (le->get_text().strip_edges().is_empty()) {
3171
return;
3172
}
3173
3174
const String new_name = le->get_text().strip_edges().validate_ascii_identifier();
3175
if (new_name == p_item_name) {
3176
_item_rename_canceled(p_data_type, p_item_name, p_control);
3177
return;
3178
}
3179
3180
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
3181
ur->create_action(TTR("Rename Theme Item"));
3182
3183
switch (p_data_type) {
3184
case Theme::DATA_TYPE_COLOR: {
3185
ur->add_do_method(*edited_theme, "rename_color", p_item_name, new_name, edited_type);
3186
ur->add_undo_method(*edited_theme, "rename_color", new_name, p_item_name, edited_type);
3187
} break;
3188
case Theme::DATA_TYPE_CONSTANT: {
3189
ur->add_do_method(*edited_theme, "rename_constant", p_item_name, new_name, edited_type);
3190
ur->add_undo_method(*edited_theme, "rename_constant", new_name, p_item_name, edited_type);
3191
} break;
3192
case Theme::DATA_TYPE_FONT: {
3193
ur->add_do_method(*edited_theme, "rename_font", p_item_name, new_name, edited_type);
3194
ur->add_undo_method(*edited_theme, "rename_font", new_name, p_item_name, edited_type);
3195
} break;
3196
case Theme::DATA_TYPE_FONT_SIZE: {
3197
ur->add_do_method(*edited_theme, "rename_font_size", p_item_name, new_name, edited_type);
3198
ur->add_undo_method(*edited_theme, "rename_font_size", new_name, p_item_name, edited_type);
3199
} break;
3200
case Theme::DATA_TYPE_ICON: {
3201
ur->add_do_method(*edited_theme, "rename_icon", p_item_name, new_name, edited_type);
3202
ur->add_undo_method(*edited_theme, "rename_icon", new_name, p_item_name, edited_type);
3203
} break;
3204
case Theme::DATA_TYPE_STYLEBOX: {
3205
ur->add_do_method(*edited_theme, "rename_stylebox", p_item_name, new_name, edited_type);
3206
ur->add_undo_method(*edited_theme, "rename_stylebox", new_name, p_item_name, edited_type);
3207
3208
if (leading_stylebox.pinned && leading_stylebox.item_name == p_item_name) {
3209
leading_stylebox.item_name = new_name;
3210
}
3211
} break;
3212
}
3213
3214
ur->commit_action();
3215
}
3216
3217
void ThemeTypeEditor::_item_rename_entered(String p_value, int p_data_type, String p_item_name, Control *p_control) {
3218
_item_rename_confirmed(p_data_type, p_item_name, p_control);
3219
}
3220
3221
void ThemeTypeEditor::_item_rename_canceled(int p_data_type, String p_item_name, Control *p_control) {
3222
// LineEdit
3223
Object::cast_to<LineEdit>(p_control->get_child(1))->hide();
3224
// LineEdit buttons
3225
Object::cast_to<Button>(p_control->get_child(4))->hide();
3226
Object::cast_to<Button>(p_control->get_child(5))->hide();
3227
3228
// Label
3229
Object::cast_to<Label>(p_control->get_child(0))->show();
3230
// Label buttons
3231
Object::cast_to<Button>(p_control->get_child(2))->show();
3232
Object::cast_to<Button>(p_control->get_child(3))->show();
3233
}
3234
3235
void ThemeTypeEditor::_color_item_changed(Color p_value, String p_item_name) {
3236
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
3237
ur->create_action(TTR("Set Color Item in Theme"), UndoRedo::MERGE_ENDS);
3238
ur->add_do_method(*edited_theme, "set_color", p_item_name, edited_type, p_value);
3239
ur->add_undo_method(*edited_theme, "set_color", p_item_name, edited_type, edited_theme->get_color(p_item_name, edited_type));
3240
ur->commit_action();
3241
}
3242
3243
void ThemeTypeEditor::_constant_item_changed(float p_value, String p_item_name) {
3244
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
3245
ur->create_action(TTR("Set Constant Item in Theme"));
3246
ur->add_do_method(*edited_theme, "set_constant", p_item_name, edited_type, p_value);
3247
ur->add_undo_method(*edited_theme, "set_constant", p_item_name, edited_type, edited_theme->get_constant(p_item_name, edited_type));
3248
ur->commit_action();
3249
}
3250
3251
void ThemeTypeEditor::_font_size_item_changed(float p_value, String p_item_name) {
3252
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
3253
ur->create_action(TTR("Set Font Size Item in Theme"));
3254
ur->add_do_method(*edited_theme, "set_font_size", p_item_name, edited_type, p_value);
3255
ur->add_undo_method(*edited_theme, "set_font_size", p_item_name, edited_type, edited_theme->get_font_size(p_item_name, edited_type));
3256
ur->commit_action();
3257
}
3258
3259
void ThemeTypeEditor::_edit_resource_item(Ref<Resource> p_resource, bool p_edit) {
3260
EditorNode::get_singleton()->edit_resource(p_resource);
3261
}
3262
3263
void ThemeTypeEditor::_font_item_changed(Ref<Font> p_value, String p_item_name) {
3264
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
3265
ur->create_action(TTR("Set Font Item in Theme"));
3266
3267
ur->add_do_method(*edited_theme, "set_font", p_item_name, edited_type, p_value.is_valid() ? p_value : Ref<Font>());
3268
if (edited_theme->has_font(p_item_name, edited_type)) {
3269
ur->add_undo_method(*edited_theme, "set_font", p_item_name, edited_type, edited_theme->get_font(p_item_name, edited_type));
3270
} else {
3271
ur->add_undo_method(*edited_theme, "set_font", p_item_name, edited_type, Ref<Font>());
3272
}
3273
3274
ur->add_do_method(this, CoreStringName(call_deferred), "_update_type_items");
3275
ur->add_undo_method(this, CoreStringName(call_deferred), "_update_type_items");
3276
3277
ur->commit_action();
3278
}
3279
3280
void ThemeTypeEditor::_icon_item_changed(Ref<Texture2D> p_value, String p_item_name) {
3281
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
3282
ur->create_action(TTR("Set Icon Item in Theme"));
3283
3284
ur->add_do_method(*edited_theme, "set_icon", p_item_name, edited_type, p_value.is_valid() ? p_value : Ref<Texture2D>());
3285
if (edited_theme->has_icon(p_item_name, edited_type)) {
3286
ur->add_undo_method(*edited_theme, "set_icon", p_item_name, edited_type, edited_theme->get_icon(p_item_name, edited_type));
3287
} else {
3288
ur->add_undo_method(*edited_theme, "set_icon", p_item_name, edited_type, Ref<Texture2D>());
3289
}
3290
3291
ur->add_do_method(this, CoreStringName(call_deferred), "_update_type_items");
3292
ur->add_undo_method(this, CoreStringName(call_deferred), "_update_type_items");
3293
3294
ur->commit_action();
3295
}
3296
3297
void ThemeTypeEditor::_stylebox_item_changed(Ref<StyleBox> p_value, String p_item_name) {
3298
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
3299
ur->create_action(TTR("Set Stylebox Item in Theme"));
3300
3301
ur->add_do_method(*edited_theme, "set_stylebox", p_item_name, edited_type, p_value.is_valid() ? p_value : Ref<StyleBox>());
3302
if (edited_theme->has_stylebox(p_item_name, edited_type)) {
3303
ur->add_undo_method(*edited_theme, "set_stylebox", p_item_name, edited_type, edited_theme->get_stylebox(p_item_name, edited_type));
3304
} else {
3305
ur->add_undo_method(*edited_theme, "set_stylebox", p_item_name, edited_type, Ref<StyleBox>());
3306
}
3307
3308
ur->add_do_method(this, "_change_pinned_stylebox");
3309
ur->add_undo_method(this, "_change_pinned_stylebox");
3310
3311
ur->add_do_method(this, CoreStringName(call_deferred), "_update_type_items");
3312
ur->add_undo_method(this, CoreStringName(call_deferred), "_update_type_items");
3313
3314
ur->commit_action();
3315
}
3316
3317
void ThemeTypeEditor::_change_pinned_stylebox() {
3318
if (leading_stylebox.pinned) {
3319
if (leading_stylebox.stylebox.is_valid()) {
3320
leading_stylebox.stylebox->disconnect_changed(callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading));
3321
}
3322
3323
Ref<StyleBox> new_stylebox = edited_theme->get_stylebox(leading_stylebox.item_name, edited_type);
3324
leading_stylebox.stylebox = new_stylebox;
3325
leading_stylebox.ref_stylebox = (new_stylebox.is_valid() ? new_stylebox->duplicate() : Ref<Resource>());
3326
3327
if (leading_stylebox.stylebox.is_valid()) {
3328
new_stylebox->connect_changed(callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading));
3329
}
3330
} else if (leading_stylebox.stylebox.is_valid()) {
3331
leading_stylebox.stylebox->disconnect_changed(callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading));
3332
}
3333
}
3334
3335
void ThemeTypeEditor::_on_pin_leader_button_pressed(Control *p_editor, String p_item_name) {
3336
Ref<StyleBox> stylebox;
3337
if (Object::cast_to<EditorResourcePicker>(p_editor)) {
3338
stylebox = Object::cast_to<EditorResourcePicker>(p_editor)->get_edited_resource();
3339
}
3340
3341
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
3342
ur->create_action(TTR("Pin Stylebox"));
3343
ur->add_do_method(this, "_pin_leading_stylebox", p_item_name, stylebox);
3344
3345
if (leading_stylebox.pinned) {
3346
ur->add_undo_method(this, "_pin_leading_stylebox", leading_stylebox.item_name, leading_stylebox.stylebox);
3347
} else {
3348
ur->add_undo_method(this, "_unpin_leading_stylebox");
3349
}
3350
3351
ur->commit_action();
3352
}
3353
3354
void ThemeTypeEditor::_pin_leading_stylebox(String p_item_name, Ref<StyleBox> p_stylebox) {
3355
if (leading_stylebox.stylebox.is_valid()) {
3356
leading_stylebox.stylebox->disconnect_changed(callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading));
3357
}
3358
3359
LeadingStylebox leader;
3360
leader.pinned = true;
3361
leader.item_name = p_item_name;
3362
leader.stylebox = p_stylebox;
3363
leader.ref_stylebox = (p_stylebox.is_valid() ? p_stylebox->duplicate() : Ref<Resource>());
3364
3365
leading_stylebox = leader;
3366
if (p_stylebox.is_valid()) {
3367
p_stylebox->connect_changed(callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading));
3368
}
3369
3370
_update_type_items();
3371
}
3372
3373
void ThemeTypeEditor::_on_unpin_leader_button_pressed() {
3374
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
3375
ur->create_action(TTR("Unpin Stylebox"));
3376
ur->add_do_method(this, "_unpin_leading_stylebox");
3377
ur->add_undo_method(this, "_pin_leading_stylebox", leading_stylebox.item_name, leading_stylebox.stylebox);
3378
ur->commit_action();
3379
}
3380
3381
void ThemeTypeEditor::_unpin_leading_stylebox() {
3382
if (leading_stylebox.stylebox.is_valid()) {
3383
leading_stylebox.stylebox->disconnect_changed(callable_mp(this, &ThemeTypeEditor::_update_stylebox_from_leading));
3384
}
3385
3386
LeadingStylebox leader;
3387
leader.pinned = false;
3388
leading_stylebox = leader;
3389
3390
_update_type_items();
3391
}
3392
3393
void ThemeTypeEditor::_update_stylebox_from_leading() {
3394
if (!leading_stylebox.pinned || leading_stylebox.stylebox.is_null()) {
3395
return;
3396
}
3397
ERR_FAIL_COND_MSG(edited_theme.is_null(), "Leading stylebox does not have an edited theme to update");
3398
3399
// Prevent changes from immediately being reported while the operation is still ongoing.
3400
edited_theme->_freeze_change_propagation();
3401
3402
List<StringName> names;
3403
edited_theme->get_stylebox_list(edited_type, &names);
3404
List<Ref<StyleBox>> styleboxes;
3405
for (const StringName &E : names) {
3406
Ref<StyleBox> sb = edited_theme->get_stylebox(E, edited_type);
3407
3408
// Avoid itself, stylebox can be shared between items.
3409
if (sb == leading_stylebox.stylebox) {
3410
continue;
3411
}
3412
3413
if (sb->get_class() == leading_stylebox.stylebox->get_class()) {
3414
styleboxes.push_back(sb);
3415
}
3416
}
3417
3418
List<PropertyInfo> props;
3419
leading_stylebox.stylebox->get_property_list(&props);
3420
for (const PropertyInfo &E : props) {
3421
if (!(E.usage & PROPERTY_USAGE_STORAGE)) {
3422
continue;
3423
}
3424
3425
Variant value = leading_stylebox.stylebox->get(E.name);
3426
Variant ref_value = leading_stylebox.ref_stylebox->get(E.name);
3427
if (value == ref_value) {
3428
continue;
3429
}
3430
3431
for (const Ref<StyleBox> &F : styleboxes) {
3432
Ref<StyleBox> sb = F;
3433
sb->set(E.name, value);
3434
}
3435
}
3436
3437
leading_stylebox.ref_stylebox = leading_stylebox.stylebox->duplicate();
3438
3439
// Allow changes to be reported now that the operation is finished.
3440
edited_theme->_unfreeze_and_propagate_changes();
3441
}
3442
3443
void ThemeTypeEditor::_type_variation_changed(const String p_value) {
3444
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
3445
ur->create_action(TTR("Set Theme Type Variation"));
3446
3447
if (p_value.is_empty()) {
3448
ur->add_do_method(*edited_theme, "clear_type_variation", edited_type);
3449
} else {
3450
ur->add_do_method(*edited_theme, "set_type_variation", edited_type, StringName(p_value));
3451
}
3452
3453
if (edited_theme->get_type_variation_base(edited_type) == "") {
3454
ur->add_undo_method(*edited_theme, "clear_type_variation", edited_type);
3455
} else {
3456
ur->add_undo_method(*edited_theme, "set_type_variation", edited_type, edited_theme->get_type_variation_base(edited_type));
3457
}
3458
3459
ur->commit_action();
3460
}
3461
3462
void ThemeTypeEditor::_add_type_variation_cbk() {
3463
add_type_mode = ADD_VARIATION_BASE;
3464
add_type_dialog->set_title(TTR("Set Variation Base Type"));
3465
add_type_dialog->set_ok_button_text(TTR("Set Base Type"));
3466
add_type_dialog->set_include_own_types(true);
3467
add_type_dialog->popup_centered(Size2(560, 420) * EDSCALE);
3468
}
3469
3470
void ThemeTypeEditor::_add_type_dialog_selected(const String p_type_name) {
3471
if (add_type_mode == ADD_THEME_TYPE) {
3472
select_type(p_type_name);
3473
} else if (add_type_mode == ADD_VARIATION_BASE) {
3474
_type_variation_changed(p_type_name);
3475
}
3476
}
3477
3478
void ThemeTypeEditor::_notification(int p_what) {
3479
switch (p_what) {
3480
case NOTIFICATION_THEME_CHANGED: {
3481
add_type_button->set_button_icon(get_editor_theme_icon(SNAME("Add")));
3482
rename_type_button->set_button_icon(get_editor_theme_icon(SNAME("Rename")));
3483
remove_type_button->set_button_icon(get_editor_theme_icon(SNAME("Remove")));
3484
3485
data_type_tabs->set_tab_icon(0, get_editor_theme_icon(SNAME("Color")));
3486
data_type_tabs->set_tab_icon(1, get_editor_theme_icon(SNAME("MemberConstant")));
3487
data_type_tabs->set_tab_icon(2, get_editor_theme_icon(SNAME("FontItem")));
3488
data_type_tabs->set_tab_icon(3, get_editor_theme_icon(SNAME("FontSize")));
3489
data_type_tabs->set_tab_icon(4, get_editor_theme_icon(SNAME("ImageTexture")));
3490
data_type_tabs->set_tab_icon(5, get_editor_theme_icon(SNAME("StyleBoxFlat")));
3491
data_type_tabs->set_tab_icon(6, get_editor_theme_icon(SNAME("Tools")));
3492
3493
type_variation_button->set_button_icon(get_editor_theme_icon(SNAME("Add")));
3494
} break;
3495
}
3496
}
3497
3498
void ThemeTypeEditor::_bind_methods() {
3499
ClassDB::bind_method(D_METHOD("_update_type_items"), &ThemeTypeEditor::_update_type_items);
3500
ClassDB::bind_method(D_METHOD("_pin_leading_stylebox"), &ThemeTypeEditor::_pin_leading_stylebox);
3501
ClassDB::bind_method(D_METHOD("_unpin_leading_stylebox"), &ThemeTypeEditor::_unpin_leading_stylebox);
3502
ClassDB::bind_method(D_METHOD("_change_pinned_stylebox"), &ThemeTypeEditor::_change_pinned_stylebox);
3503
ClassDB::bind_method(D_METHOD("select_type", "type_name"), &ThemeTypeEditor::select_type);
3504
}
3505
3506
void ThemeTypeEditor::set_edited_theme(const Ref<Theme> &p_theme) {
3507
if (edited_theme.is_valid()) {
3508
edited_theme->disconnect_changed(callable_mp(this, &ThemeTypeEditor::_update_type_list_debounced));
3509
}
3510
3511
edited_theme = p_theme;
3512
if (edited_theme.is_valid()) {
3513
edited_theme->connect_changed(callable_mp(this, &ThemeTypeEditor::_update_type_list_debounced));
3514
_update_type_list();
3515
}
3516
3517
add_type_dialog->set_edited_theme(edited_theme);
3518
}
3519
3520
void ThemeTypeEditor::select_type(String p_type_name) {
3521
edited_type = p_type_name;
3522
bool type_exists = false;
3523
3524
for (int i = 0; i < theme_type_list->get_item_count(); i++) {
3525
String type_name = theme_type_list->get_item_text(i);
3526
if (type_name == edited_type) {
3527
theme_type_list->select(i);
3528
type_exists = true;
3529
break;
3530
}
3531
}
3532
3533
if (type_exists) {
3534
_update_type_items();
3535
} else {
3536
edited_theme->add_icon_type(edited_type);
3537
edited_theme->add_stylebox_type(edited_type);
3538
edited_theme->add_font_type(edited_type);
3539
edited_theme->add_font_size_type(edited_type);
3540
edited_theme->add_color_type(edited_type);
3541
edited_theme->add_constant_type(edited_type);
3542
3543
_update_type_list();
3544
}
3545
}
3546
3547
bool ThemeTypeEditor::is_stylebox_pinned(Ref<StyleBox> p_stylebox) {
3548
return leading_stylebox.pinned && leading_stylebox.stylebox == p_stylebox;
3549
}
3550
3551
ThemeTypeEditor::ThemeTypeEditor() {
3552
VBoxContainer *main_vb = memnew(VBoxContainer);
3553
add_child(main_vb);
3554
3555
HBoxContainer *type_list_hb = memnew(HBoxContainer);
3556
main_vb->add_child(type_list_hb);
3557
3558
Label *type_list_label = memnew(Label);
3559
type_list_label->set_text(TTR("Type:"));
3560
type_list_hb->add_child(type_list_label);
3561
3562
theme_type_list = memnew(OptionButton);
3563
theme_type_list->set_h_size_flags(SIZE_EXPAND_FILL);
3564
theme_type_list->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS);
3565
theme_type_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
3566
theme_type_list->set_accessibility_name(TTRC("Type"));
3567
type_list_hb->add_child(theme_type_list);
3568
theme_type_list->connect(SceneStringName(item_selected), callable_mp(this, &ThemeTypeEditor::_list_type_selected));
3569
3570
add_type_button = memnew(Button);
3571
add_type_button->set_tooltip_text(TTR("Add a type from a list of available types or create a new one."));
3572
type_list_hb->add_child(add_type_button);
3573
add_type_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeTypeEditor::_add_type_button_cbk));
3574
3575
rename_type_button = memnew(Button);
3576
rename_type_button->set_disabled(true);
3577
rename_type_button->set_tooltip_text(TTRC("Rename current type."));
3578
type_list_hb->add_child(rename_type_button);
3579
rename_type_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeTypeEditor::_rename_type_button_cbk));
3580
3581
theme_type_rename_dialog = memnew(ConfirmationDialog);
3582
theme_type_rename_dialog->set_title(TTRC("Rename Theme Type"));
3583
theme_type_rename_dialog->set_min_size(Size2(256, 64) * EDSCALE);
3584
add_child(theme_type_rename_dialog);
3585
theme_type_rename_dialog->connect(SceneStringName(confirmed), callable_mp(this, &ThemeTypeEditor::_theme_type_rename_dialog_confirmed));
3586
3587
theme_type_rename_line_edit = memnew(LineEdit);
3588
theme_type_rename_line_edit->set_select_all_on_focus(true);
3589
theme_type_rename_dialog->add_child(theme_type_rename_line_edit);
3590
theme_type_rename_dialog->register_text_enter(theme_type_rename_line_edit);
3591
3592
remove_type_button = memnew(Button);
3593
remove_type_button->set_disabled(true);
3594
remove_type_button->set_tooltip_text(TTRC("Remove current type."));
3595
remove_type_button->set_accessibility_name(TTRC("Remove current type."));
3596
type_list_hb->add_child(remove_type_button);
3597
remove_type_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeTypeEditor::_remove_type_button_cbk));
3598
3599
HBoxContainer *type_controls = memnew(HBoxContainer);
3600
main_vb->add_child(type_controls);
3601
3602
show_default_items_button = memnew(CheckButton);
3603
show_default_items_button->set_h_size_flags(SIZE_EXPAND_FILL);
3604
show_default_items_button->set_text(TTR("Show Default"));
3605
show_default_items_button->set_tooltip_text(TTR("Show default type items alongside items that have been overridden."));
3606
show_default_items_button->set_pressed(true);
3607
type_controls->add_child(show_default_items_button);
3608
show_default_items_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeTypeEditor::_update_type_items));
3609
3610
Button *add_default_items_button = memnew(Button);
3611
add_default_items_button->set_h_size_flags(SIZE_EXPAND_FILL);
3612
add_default_items_button->set_text(TTR("Override All"));
3613
add_default_items_button->set_tooltip_text(TTR("Override all default type items."));
3614
type_controls->add_child(add_default_items_button);
3615
add_default_items_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeTypeEditor::_add_default_type_items));
3616
3617
data_type_tabs = memnew(TabContainer);
3618
data_type_tabs->set_tab_alignment(TabBar::ALIGNMENT_CENTER);
3619
main_vb->add_child(data_type_tabs);
3620
data_type_tabs->set_v_size_flags(SIZE_EXPAND_FILL);
3621
data_type_tabs->set_use_hidden_tabs_for_min_size(true);
3622
data_type_tabs->set_theme_type_variation("TabContainerOdd");
3623
3624
color_items_list = _create_item_list(Theme::DATA_TYPE_COLOR);
3625
constant_items_list = _create_item_list(Theme::DATA_TYPE_CONSTANT);
3626
font_items_list = _create_item_list(Theme::DATA_TYPE_FONT);
3627
font_size_items_list = _create_item_list(Theme::DATA_TYPE_FONT_SIZE);
3628
icon_items_list = _create_item_list(Theme::DATA_TYPE_ICON);
3629
stylebox_items_list = _create_item_list(Theme::DATA_TYPE_STYLEBOX);
3630
3631
VBoxContainer *type_settings_tab = memnew(VBoxContainer);
3632
type_settings_tab->set_custom_minimum_size(Size2(0, 160) * EDSCALE);
3633
data_type_tabs->add_child(type_settings_tab);
3634
data_type_tabs->set_tab_title(data_type_tabs->get_tab_count() - 1, "");
3635
3636
ScrollContainer *type_settings_sc = memnew(ScrollContainer);
3637
type_settings_sc->set_v_size_flags(SIZE_EXPAND_FILL);
3638
type_settings_sc->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
3639
type_settings_tab->add_child(type_settings_sc);
3640
VBoxContainer *type_settings_list = memnew(VBoxContainer);
3641
type_settings_list->set_h_size_flags(SIZE_EXPAND_FILL);
3642
type_settings_sc->add_child(type_settings_list);
3643
3644
VBoxContainer *type_variation_vb = memnew(VBoxContainer);
3645
type_settings_list->add_child(type_variation_vb);
3646
3647
HBoxContainer *type_variation_hb = memnew(HBoxContainer);
3648
type_variation_vb->add_child(type_variation_hb);
3649
Label *type_variation_label = memnew(Label);
3650
type_variation_hb->add_child(type_variation_label);
3651
type_variation_label->set_text(TTR("Base Type"));
3652
type_variation_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
3653
type_variation_edit = memnew(LineEdit);
3654
type_variation_hb->add_child(type_variation_edit);
3655
type_variation_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
3656
type_variation_edit->connect(SceneStringName(text_changed), callable_mp(this, &ThemeTypeEditor::_type_variation_changed));
3657
type_variation_edit->connect(SceneStringName(focus_exited), callable_mp(this, &ThemeTypeEditor::_update_type_items));
3658
type_variation_edit->set_accessibility_name(TTRC("Base Type"));
3659
type_variation_button = memnew(Button);
3660
type_variation_hb->add_child(type_variation_button);
3661
type_variation_button->set_tooltip_text(TTR("Select the variation base type from a list of available types."));
3662
type_variation_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeTypeEditor::_add_type_variation_cbk));
3663
3664
type_variation_locked = memnew(Label);
3665
type_variation_vb->add_child(type_variation_locked);
3666
type_variation_locked->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
3667
type_variation_locked->set_autowrap_mode(TextServer::AUTOWRAP_WORD);
3668
type_variation_locked->set_text(TTR("A type associated with a built-in class cannot be marked as a variation of another type."));
3669
type_variation_locked->hide();
3670
3671
add_type_dialog = memnew(ThemeTypeDialog);
3672
add_child(add_type_dialog);
3673
add_type_dialog->connect("type_selected", callable_mp(this, &ThemeTypeEditor::_add_type_dialog_selected));
3674
3675
update_debounce_timer = memnew(Timer);
3676
update_debounce_timer->set_one_shot(true);
3677
update_debounce_timer->set_wait_time(0.5);
3678
update_debounce_timer->connect("timeout", callable_mp(this, &ThemeTypeEditor::_update_type_list));
3679
add_child(update_debounce_timer);
3680
}
3681
3682
///////////////////////
3683
3684
void ThemeEditor::edit(const Ref<Theme> &p_theme) {
3685
if (theme == p_theme) {
3686
return;
3687
}
3688
3689
theme = p_theme;
3690
theme_type_editor->set_edited_theme(p_theme);
3691
theme_edit_dialog->set_edited_theme(p_theme);
3692
3693
for (int i = 0; i < preview_tabs_content->get_child_count(); i++) {
3694
ThemeEditorPreview *preview_tab = Object::cast_to<ThemeEditorPreview>(preview_tabs_content->get_child(i));
3695
if (!preview_tab) {
3696
continue;
3697
}
3698
3699
preview_tab->set_preview_theme(p_theme);
3700
}
3701
3702
if (theme.is_valid()) {
3703
_update_theme_name(theme->get_path().get_file());
3704
}
3705
}
3706
3707
Ref<Theme> ThemeEditor::get_edited_theme() {
3708
return theme;
3709
}
3710
3711
void ThemeEditor::_theme_save_button_cbk(bool p_save_as) {
3712
ERR_FAIL_COND_MSG(theme.is_null(), "Invalid state of the Theme Editor; the Theme resource is missing.");
3713
3714
if (p_save_as) {
3715
EditorNode::get_singleton()->save_resource_as(theme);
3716
} else {
3717
EditorNode::get_singleton()->save_resource(theme);
3718
}
3719
}
3720
3721
void ThemeEditor::_theme_edit_button_cbk() {
3722
theme_edit_dialog->popup_centered(Size2(850, 700) * EDSCALE);
3723
}
3724
3725
void ThemeEditor::_theme_close_button_cbk() {
3726
plugin->make_visible(false); // Enables auto hide.
3727
if (theme.is_valid() && InspectorDock::get_inspector_singleton()->get_edited_object() == theme.ptr()) {
3728
EditorNode::get_singleton()->push_item(nullptr);
3729
} else {
3730
theme = Ref<Theme>();
3731
EditorNode::get_singleton()->hide_unused_editors(plugin);
3732
}
3733
}
3734
3735
void ThemeEditor::_scene_closed(const String &p_path) {
3736
if (theme.is_valid() && theme->is_built_in() && theme->get_path().get_slice("::", 0) == p_path) {
3737
theme = Ref<Theme>();
3738
EditorNode::get_singleton()->hide_unused_editors(plugin);
3739
}
3740
}
3741
3742
void ThemeEditor::_resource_saved(const Ref<Resource> &p_resource) {
3743
if (theme.is_valid() && theme == p_resource) {
3744
_update_theme_name(theme->get_path().get_file());
3745
}
3746
}
3747
3748
void ThemeEditor::_files_moved(const String &p_old_path, const String &p_new_path) {
3749
// Theme's path may not have been updated to new path yet - need to check both old and new.
3750
if (theme.is_valid() && (theme->get_path() == p_old_path || theme->get_path() == p_new_path)) {
3751
_update_theme_name(p_new_path.get_file());
3752
}
3753
}
3754
3755
void ThemeEditor::_update_theme_name(const String &p_name) {
3756
theme_name->set_text(TTR("Theme:") + " " + p_name);
3757
}
3758
3759
void ThemeEditor::_add_preview_button_cbk() {
3760
preview_scene_dialog->popup_file_dialog();
3761
}
3762
3763
void ThemeEditor::_preview_scene_dialog_cbk(const String &p_path) {
3764
SceneThemeEditorPreview *preview_tab = memnew(SceneThemeEditorPreview);
3765
if (!preview_tab->set_preview_scene(p_path)) {
3766
memdelete(preview_tab);
3767
return;
3768
}
3769
3770
_add_preview_tab(preview_tab, p_path.get_file(), get_editor_theme_icon(SNAME("PackedScene")));
3771
preview_tab->connect("scene_invalidated", callable_mp(this, &ThemeEditor::_remove_preview_tab_invalid).bind(preview_tab));
3772
preview_tab->connect("scene_reloaded", callable_mp(this, &ThemeEditor::_update_preview_tab).bind(preview_tab));
3773
}
3774
3775
void ThemeEditor::_add_preview_tab(ThemeEditorPreview *p_preview_tab, const String &p_preview_name, const Ref<Texture2D> &p_icon) {
3776
p_preview_tab->set_preview_theme(theme);
3777
3778
preview_tabs->add_tab(p_preview_name, p_icon);
3779
preview_tabs_content->add_child(p_preview_tab);
3780
preview_tabs->set_tab_button_icon(preview_tabs->get_tab_count() - 1, EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("close"), SNAME("TabBar")));
3781
p_preview_tab->connect("control_picked", callable_mp(this, &ThemeEditor::_preview_control_picked));
3782
3783
preview_tabs->set_current_tab(preview_tabs->get_tab_count() - 1);
3784
}
3785
3786
void ThemeEditor::_change_preview_tab(int p_tab) {
3787
ERR_FAIL_INDEX_MSG(p_tab, preview_tabs_content->get_child_count(), "Attempting to open a preview tab that doesn't exist.");
3788
3789
for (int i = 0; i < preview_tabs_content->get_child_count(); i++) {
3790
Control *c = Object::cast_to<Control>(preview_tabs_content->get_child(i));
3791
if (!c) {
3792
continue;
3793
}
3794
3795
c->set_visible(i == p_tab);
3796
}
3797
}
3798
3799
void ThemeEditor::_remove_preview_tab(int p_tab) {
3800
ERR_FAIL_INDEX_MSG(p_tab, preview_tabs_content->get_child_count(), "Attempting to remove a preview tab that doesn't exist.");
3801
3802
ThemeEditorPreview *preview_tab = Object::cast_to<ThemeEditorPreview>(preview_tabs_content->get_child(p_tab));
3803
ERR_FAIL_COND_MSG(Object::cast_to<DefaultThemeEditorPreview>(preview_tab), "Attemptying to remove the default preview tab.");
3804
3805
if (preview_tab) {
3806
preview_tab->disconnect("control_picked", callable_mp(this, &ThemeEditor::_preview_control_picked));
3807
if (preview_tab->is_connected("scene_invalidated", callable_mp(this, &ThemeEditor::_remove_preview_tab_invalid))) {
3808
preview_tab->disconnect("scene_invalidated", callable_mp(this, &ThemeEditor::_remove_preview_tab_invalid));
3809
}
3810
if (preview_tab->is_connected("scene_reloaded", callable_mp(this, &ThemeEditor::_update_preview_tab))) {
3811
preview_tab->disconnect("scene_reloaded", callable_mp(this, &ThemeEditor::_update_preview_tab));
3812
}
3813
3814
preview_tabs_content->remove_child(preview_tab);
3815
preview_tab->queue_free();
3816
3817
preview_tabs->remove_tab(p_tab);
3818
_change_preview_tab(preview_tabs->get_current_tab());
3819
}
3820
}
3821
3822
void ThemeEditor::_remove_preview_tab_invalid(Node *p_tab_control) {
3823
int tab_index = p_tab_control->get_index();
3824
_remove_preview_tab(tab_index);
3825
}
3826
3827
void ThemeEditor::_update_preview_tab(Node *p_tab_control) {
3828
if (!Object::cast_to<SceneThemeEditorPreview>(p_tab_control)) {
3829
return;
3830
}
3831
3832
int tab_index = p_tab_control->get_index();
3833
SceneThemeEditorPreview *scene_preview = Object::cast_to<SceneThemeEditorPreview>(p_tab_control);
3834
preview_tabs->set_tab_title(tab_index, scene_preview->get_preview_scene_path().get_file());
3835
}
3836
3837
void ThemeEditor::_preview_control_picked(String p_class_name) {
3838
theme_type_editor->select_type(p_class_name);
3839
}
3840
3841
bool ThemeEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
3842
const Dictionary d = p_data;
3843
if (!d.has("type")) {
3844
return false;
3845
}
3846
3847
if (String(d["type"]) == "files") {
3848
const Vector<String> files = d["files"];
3849
3850
if (files.size() != 1) {
3851
return false;
3852
}
3853
3854
const String ftype = EditorFileSystem::get_singleton()->get_file_type(files[0]);
3855
return ftype == "PackedScene";
3856
}
3857
return false;
3858
}
3859
3860
void ThemeEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
3861
Dictionary d = p_data;
3862
Vector<String> files = d["files"];
3863
const String &path = files[0];
3864
3865
SceneThemeEditorPreview *preview_tab = memnew(SceneThemeEditorPreview);
3866
if (!preview_tab->set_preview_scene(path)) {
3867
memdelete(preview_tab);
3868
return;
3869
}
3870
3871
Ref<Texture2D> icon = get_editor_theme_icon(SNAME("PackedScene"));
3872
3873
preview_tab->set_preview_theme(theme);
3874
3875
preview_tabs->add_tab(path.get_file(), icon);
3876
preview_tabs_content->add_child(preview_tab);
3877
preview_tabs->set_tab_button_icon(preview_tabs->get_tab_count() - 1, EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("close"), SNAME("TabBar")));
3878
preview_tab->connect("control_picked", callable_mp(this, &ThemeEditor::_preview_control_picked));
3879
3880
preview_tabs->set_current_tab(preview_tabs->get_tab_count() - 1);
3881
preview_tab->connect("scene_invalidated", callable_mp(this, &ThemeEditor::_remove_preview_tab_invalid).bind(preview_tab));
3882
preview_tab->connect("scene_reloaded", callable_mp(this, &ThemeEditor::_update_preview_tab).bind(preview_tab));
3883
}
3884
3885
void ThemeEditor::_notification(int p_what) {
3886
switch (p_what) {
3887
case NOTIFICATION_READY: {
3888
EditorNode::get_singleton()->connect("scene_closed", callable_mp(this, &ThemeEditor::_scene_closed));
3889
EditorNode::get_singleton()->connect("resource_saved", callable_mp(this, &ThemeEditor::_resource_saved));
3890
FileSystemDock::get_singleton()->connect("files_moved", callable_mp(this, &ThemeEditor::_files_moved));
3891
} break;
3892
3893
case NOTIFICATION_THEME_CHANGED: {
3894
preview_tabs->add_theme_style_override("tab_selected", get_theme_stylebox(SNAME("ThemeEditorPreviewFG"), EditorStringName(EditorStyles)));
3895
preview_tabs->add_theme_style_override("tab_unselected", get_theme_stylebox(SNAME("ThemeEditorPreviewBG"), EditorStringName(EditorStyles)));
3896
preview_tabs_content->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("TabContainerOdd")));
3897
3898
add_preview_button->set_button_icon(get_editor_theme_icon(SNAME("Add")));
3899
} break;
3900
}
3901
}
3902
3903
ThemeEditor::ThemeEditor() {
3904
HBoxContainer *top_menu = memnew(HBoxContainer);
3905
add_child(top_menu);
3906
3907
theme_name = memnew(Label);
3908
theme_name->set_text(TTR("Theme:"));
3909
theme_name->set_theme_type_variation("HeaderSmall");
3910
top_menu->add_child(theme_name);
3911
3912
top_menu->add_spacer(false);
3913
3914
Button *theme_save_button = memnew(Button);
3915
theme_save_button->set_text(TTR("Save"));
3916
theme_save_button->set_flat(true);
3917
theme_save_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeEditor::_theme_save_button_cbk).bind(false));
3918
top_menu->add_child(theme_save_button);
3919
3920
Button *theme_save_as_button = memnew(Button);
3921
theme_save_as_button->set_text(TTR("Save As..."));
3922
theme_save_as_button->set_flat(true);
3923
theme_save_as_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeEditor::_theme_save_button_cbk).bind(true));
3924
top_menu->add_child(theme_save_as_button);
3925
3926
Button *theme_close_button = memnew(Button);
3927
theme_close_button->set_text(TTR("Close"));
3928
theme_close_button->set_flat(true);
3929
theme_close_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeEditor::_theme_close_button_cbk));
3930
top_menu->add_child(theme_close_button);
3931
3932
top_menu->add_child(memnew(VSeparator));
3933
3934
Button *theme_edit_button = memnew(Button);
3935
theme_edit_button->set_text(TTR("Manage Items..."));
3936
theme_edit_button->set_tooltip_text(TTR("Add, remove, organize and import Theme items."));
3937
theme_edit_button->set_flat(true);
3938
theme_edit_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeEditor::_theme_edit_button_cbk));
3939
top_menu->add_child(theme_edit_button);
3940
3941
theme_type_editor = memnew(ThemeTypeEditor);
3942
3943
theme_edit_dialog = memnew(ThemeItemEditorDialog(theme_type_editor));
3944
theme_edit_dialog->hide();
3945
top_menu->add_child(theme_edit_dialog);
3946
3947
HSplitContainer *main_hs = memnew(HSplitContainer);
3948
main_hs->set_v_size_flags(SIZE_EXPAND_FILL);
3949
add_child(main_hs);
3950
3951
main_hs->set_split_offset(520 * EDSCALE);
3952
3953
VBoxContainer *preview_tabs_vb = memnew(VBoxContainer);
3954
preview_tabs_vb->set_h_size_flags(SIZE_EXPAND_FILL);
3955
preview_tabs_vb->add_theme_constant_override("separation", 2 * EDSCALE);
3956
main_hs->add_child(preview_tabs_vb);
3957
HBoxContainer *preview_tabbar_hb = memnew(HBoxContainer);
3958
preview_tabs_vb->add_child(preview_tabbar_hb);
3959
preview_tabs_content = memnew(PanelContainer);
3960
preview_tabs_content->set_v_size_flags(SIZE_EXPAND_FILL);
3961
preview_tabs_content->set_draw_behind_parent(true);
3962
preview_tabs_vb->add_child(preview_tabs_content);
3963
3964
preview_tabs = memnew(TabBar);
3965
preview_tabs->set_h_size_flags(SIZE_EXPAND_FILL);
3966
preview_tabbar_hb->add_child(preview_tabs);
3967
preview_tabs->connect("tab_changed", callable_mp(this, &ThemeEditor::_change_preview_tab));
3968
preview_tabs->connect("tab_button_pressed", callable_mp(this, &ThemeEditor::_remove_preview_tab));
3969
3970
HBoxContainer *add_preview_button_hb = memnew(HBoxContainer);
3971
preview_tabbar_hb->add_child(add_preview_button_hb);
3972
add_preview_button = memnew(Button);
3973
add_preview_button->set_text(TTR("Add Preview"));
3974
add_preview_button_hb->add_child(add_preview_button);
3975
add_preview_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeEditor::_add_preview_button_cbk));
3976
3977
DefaultThemeEditorPreview *default_preview_tab = memnew(DefaultThemeEditorPreview);
3978
preview_tabs_content->add_child(default_preview_tab);
3979
default_preview_tab->connect("control_picked", callable_mp(this, &ThemeEditor::_preview_control_picked));
3980
preview_tabs->add_tab(TTR("Default Preview"));
3981
3982
preview_scene_dialog = memnew(EditorFileDialog);
3983
preview_scene_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
3984
preview_scene_dialog->set_title(TTR("Select UI Scene:"));
3985
List<String> ext;
3986
ResourceLoader::get_recognized_extensions_for_type("PackedScene", &ext);
3987
for (const String &E : ext) {
3988
preview_scene_dialog->add_filter("*." + E, TTR("Scene"));
3989
}
3990
main_hs->add_child(preview_scene_dialog);
3991
preview_scene_dialog->connect("file_selected", callable_mp(this, &ThemeEditor::_preview_scene_dialog_cbk));
3992
3993
main_hs->add_child(theme_type_editor);
3994
theme_type_editor->set_custom_minimum_size(Size2(280, 0) * EDSCALE);
3995
3996
SET_DRAG_FORWARDING_CD(top_menu, ThemeEditor);
3997
SET_DRAG_FORWARDING_CD(preview_tabs, ThemeEditor);
3998
}
3999
4000
///////////////////////
4001
4002
void ThemeEditorPlugin::edit(Object *p_object) {
4003
theme_editor->edit(Ref<Theme>(p_object));
4004
}
4005
4006
bool ThemeEditorPlugin::handles(Object *p_object) const {
4007
return Object::cast_to<Theme>(p_object) != nullptr;
4008
}
4009
4010
void ThemeEditorPlugin::make_visible(bool p_visible) {
4011
if (p_visible) {
4012
button->show();
4013
EditorNode::get_bottom_panel()->make_item_visible(theme_editor);
4014
} else {
4015
if (theme_editor->is_visible_in_tree()) {
4016
EditorNode::get_bottom_panel()->hide_bottom_panel();
4017
}
4018
4019
button->hide();
4020
}
4021
}
4022
4023
bool ThemeEditorPlugin::can_auto_hide() const {
4024
return theme_editor->theme.is_null();
4025
}
4026
4027
ThemeEditorPlugin::ThemeEditorPlugin() {
4028
theme_editor = memnew(ThemeEditor);
4029
theme_editor->plugin = this;
4030
theme_editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
4031
4032
button = EditorNode::get_bottom_panel()->add_item(TTRC("Theme"), theme_editor, ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_theme_bottom_panel", TTRC("Toggle Theme Bottom Panel")));
4033
button->hide();
4034
}
4035
4036