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