1/**************************************************************************/
2/* theme.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.h"
32
33#include "core/string/print_string.h"
34#include "scene/theme/theme_db.h"
35
36// Dynamic properties.
37bool Theme::_set(const StringName &p_name, const Variant &p_value) {
38 String sname = p_name;
39
40 if (sname.contains("/")) {
41 String type = sname.get_slicec('/', 1);
42 String theme_type = sname.get_slicec('/', 0);
43 String prop_name = sname.get_slicec('/', 2);
44
45 if (type == "icons") {
46 set_icon(prop_name, theme_type, p_value);
47 } else if (type == "styles") {
48 set_stylebox(prop_name, theme_type, p_value);
49 } else if (type == "fonts") {
50 set_font(prop_name, theme_type, p_value);
51 } else if (type == "font_sizes") {
52 set_font_size(prop_name, theme_type, p_value);
53 } else if (type == "colors") {
54 set_color(prop_name, theme_type, p_value);
55 } else if (type == "constants") {
56 set_constant(prop_name, theme_type, p_value);
57 } else if (type == "base_type") {
58 set_type_variation(theme_type, p_value);
59 } else {
60 return false;
61 }
62
63 return true;
64 }
65
66 return false;
67}
68
69bool Theme::_get(const StringName &p_name, Variant &r_ret) const {
70 String sname = p_name;
71
72 if (sname.contains("/")) {
73 String type = sname.get_slicec('/', 1);
74 String theme_type = sname.get_slicec('/', 0);
75 String prop_name = sname.get_slicec('/', 2);
76
77 if (type == "icons") {
78 if (!has_icon(prop_name, theme_type)) {
79 r_ret = Ref<Texture2D>();
80 } else {
81 r_ret = get_icon(prop_name, theme_type);
82 }
83 } else if (type == "styles") {
84 if (!has_stylebox(prop_name, theme_type)) {
85 r_ret = Ref<StyleBox>();
86 } else {
87 r_ret = get_stylebox(prop_name, theme_type);
88 }
89 } else if (type == "fonts") {
90 if (!has_font(prop_name, theme_type)) {
91 r_ret = Ref<Font>();
92 } else {
93 r_ret = get_font(prop_name, theme_type);
94 }
95 } else if (type == "font_sizes") {
96 r_ret = get_font_size(prop_name, theme_type);
97 } else if (type == "colors") {
98 r_ret = get_color(prop_name, theme_type);
99 } else if (type == "constants") {
100 r_ret = get_constant(prop_name, theme_type);
101 } else if (type == "base_type") {
102 r_ret = get_type_variation_base(theme_type);
103 } else {
104 return false;
105 }
106
107 return true;
108 }
109
110 return false;
111}
112
113void Theme::_get_property_list(List<PropertyInfo> *p_list) const {
114 List<PropertyInfo> list;
115
116 // Type variations.
117 for (const KeyValue<StringName, StringName> &E : variation_map) {
118 list.push_back(PropertyInfo(Variant::STRING_NAME, String() + E.key + "/base_type"));
119 }
120
121 // Icons.
122 for (const KeyValue<StringName, ThemeIconMap> &E : icon_map) {
123 for (const KeyValue<StringName, Ref<Texture2D>> &F : E.value) {
124 list.push_back(PropertyInfo(Variant::OBJECT, String() + E.key + "/icons/" + F.key, PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL));
125 }
126 }
127
128 // Styles.
129 for (const KeyValue<StringName, ThemeStyleMap> &E : style_map) {
130 for (const KeyValue<StringName, Ref<StyleBox>> &F : E.value) {
131 list.push_back(PropertyInfo(Variant::OBJECT, String() + E.key + "/styles/" + F.key, PROPERTY_HINT_RESOURCE_TYPE, "StyleBox", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL));
132 }
133 }
134
135 // Fonts.
136 for (const KeyValue<StringName, ThemeFontMap> &E : font_map) {
137 for (const KeyValue<StringName, Ref<Font>> &F : E.value) {
138 list.push_back(PropertyInfo(Variant::OBJECT, String() + E.key + "/fonts/" + F.key, PROPERTY_HINT_RESOURCE_TYPE, "Font", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL));
139 }
140 }
141
142 // Font sizes.
143 for (const KeyValue<StringName, ThemeFontSizeMap> &E : font_size_map) {
144 for (const KeyValue<StringName, int> &F : E.value) {
145 list.push_back(PropertyInfo(Variant::INT, String() + E.key + "/font_sizes/" + F.key, PROPERTY_HINT_RANGE, "0,256,1,or_greater,suffix:px"));
146 }
147 }
148
149 // Colors.
150 for (const KeyValue<StringName, ThemeColorMap> &E : color_map) {
151 for (const KeyValue<StringName, Color> &F : E.value) {
152 list.push_back(PropertyInfo(Variant::COLOR, String() + E.key + "/colors/" + F.key));
153 }
154 }
155
156 // Constants.
157 for (const KeyValue<StringName, ThemeConstantMap> &E : constant_map) {
158 for (const KeyValue<StringName, int> &F : E.value) {
159 list.push_back(PropertyInfo(Variant::INT, String() + E.key + "/constants/" + F.key));
160 }
161 }
162
163 // Sort and store properties.
164 list.sort();
165 String prev_type;
166 for (const PropertyInfo &E : list) {
167 // Add groups for types so that their names are left unchanged in the inspector.
168 String current_type = E.name.get_slice("/", 0);
169 if (prev_type != current_type) {
170 p_list->push_back(PropertyInfo(Variant::NIL, current_type, PROPERTY_HINT_NONE, current_type + "/", PROPERTY_USAGE_GROUP));
171 prev_type = current_type;
172 }
173
174 p_list->push_back(E);
175 }
176}
177
178// Static helpers.
179bool Theme::is_valid_type_name(const String &p_name) {
180 for (int i = 0; i < p_name.length(); i++) {
181 if (!is_ascii_identifier_char(p_name[i])) {
182 return false;
183 }
184 }
185 return true;
186}
187
188bool Theme::is_valid_item_name(const String &p_name) {
189 if (p_name.is_empty()) {
190 return false;
191 }
192 for (int i = 0; i < p_name.length(); i++) {
193 if (!is_ascii_identifier_char(p_name[i])) {
194 return false;
195 }
196 }
197 return true;
198}
199
200// Fallback values for theme item types, configurable per theme.
201void Theme::set_default_base_scale(float p_base_scale) {
202 if (default_base_scale == p_base_scale) {
203 return;
204 }
205
206 default_base_scale = p_base_scale;
207
208 _emit_theme_changed();
209}
210
211float Theme::get_default_base_scale() const {
212 return default_base_scale;
213}
214
215bool Theme::has_default_base_scale() const {
216 return default_base_scale > 0.0;
217}
218
219void Theme::set_default_font(const Ref<Font> &p_default_font) {
220 if (default_font == p_default_font) {
221 return;
222 }
223
224 if (default_font.is_valid()) {
225 default_font->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed));
226 }
227
228 default_font = p_default_font;
229
230 if (default_font.is_valid()) {
231 default_font->connect_changed(callable_mp(this, &Theme::_emit_theme_changed).bind(false), CONNECT_REFERENCE_COUNTED);
232 }
233
234 _emit_theme_changed();
235}
236
237Ref<Font> Theme::get_default_font() const {
238 return default_font;
239}
240
241bool Theme::has_default_font() const {
242 return default_font.is_valid();
243}
244
245void Theme::set_default_font_size(int p_font_size) {
246 if (default_font_size == p_font_size) {
247 return;
248 }
249
250 default_font_size = p_font_size;
251
252 _emit_theme_changed();
253}
254
255int Theme::get_default_font_size() const {
256 return default_font_size;
257}
258
259bool Theme::has_default_font_size() const {
260 return default_font_size > 0;
261}
262
263// Icons.
264void Theme::set_icon(const StringName &p_name, const StringName &p_theme_type, const Ref<Texture2D> &p_icon) {
265 ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name));
266 ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
267
268 bool existing = false;
269 if (icon_map[p_theme_type].has(p_name) && icon_map[p_theme_type][p_name].is_valid()) {
270 existing = true;
271 icon_map[p_theme_type][p_name]->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed));
272 }
273
274 icon_map[p_theme_type][p_name] = p_icon;
275
276 if (p_icon.is_valid()) {
277 icon_map[p_theme_type][p_name]->connect_changed(callable_mp(this, &Theme::_emit_theme_changed).bind(false), CONNECT_REFERENCE_COUNTED);
278 }
279
280 _emit_theme_changed(!existing);
281}
282
283Ref<Texture2D> Theme::get_icon(const StringName &p_name, const StringName &p_theme_type) const {
284 if (icon_map.has(p_theme_type) && icon_map[p_theme_type].has(p_name) && icon_map[p_theme_type][p_name].is_valid()) {
285 return icon_map[p_theme_type][p_name];
286 } else {
287 return ThemeDB::get_singleton()->get_fallback_icon();
288 }
289}
290
291bool Theme::has_icon(const StringName &p_name, const StringName &p_theme_type) const {
292 return (icon_map.has(p_theme_type) && icon_map[p_theme_type].has(p_name) && icon_map[p_theme_type][p_name].is_valid());
293}
294
295bool Theme::has_icon_nocheck(const StringName &p_name, const StringName &p_theme_type) const {
296 return (icon_map.has(p_theme_type) && icon_map[p_theme_type].has(p_name));
297}
298
299void Theme::rename_icon(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
300 ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name));
301 ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
302 ERR_FAIL_COND_MSG(!icon_map.has(p_theme_type), "Cannot rename the icon '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist.");
303 ERR_FAIL_COND_MSG(icon_map[p_theme_type].has(p_name), "Cannot rename the icon '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists.");
304 ERR_FAIL_COND_MSG(!icon_map[p_theme_type].has(p_old_name), "Cannot rename the icon '" + String(p_old_name) + "' because it does not exist.");
305
306 icon_map[p_theme_type][p_name] = icon_map[p_theme_type][p_old_name];
307 icon_map[p_theme_type].erase(p_old_name);
308
309 _emit_theme_changed(true);
310}
311
312void Theme::clear_icon(const StringName &p_name, const StringName &p_theme_type) {
313 ERR_FAIL_COND_MSG(!icon_map.has(p_theme_type), "Cannot clear the icon '" + String(p_name) + "' because the node type '" + String(p_theme_type) + "' does not exist.");
314 ERR_FAIL_COND_MSG(!icon_map[p_theme_type].has(p_name), "Cannot clear the icon '" + String(p_name) + "' because it does not exist.");
315
316 if (icon_map[p_theme_type][p_name].is_valid()) {
317 icon_map[p_theme_type][p_name]->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed));
318 }
319
320 icon_map[p_theme_type].erase(p_name);
321
322 _emit_theme_changed(true);
323}
324
325void Theme::get_icon_list(StringName p_theme_type, List<StringName> *p_list) const {
326 ERR_FAIL_NULL(p_list);
327
328 if (!icon_map.has(p_theme_type)) {
329 return;
330 }
331
332 for (const KeyValue<StringName, Ref<Texture2D>> &E : icon_map[p_theme_type]) {
333 p_list->push_back(E.key);
334 }
335}
336
337void Theme::add_icon_type(const StringName &p_theme_type) {
338 ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
339
340 if (icon_map.has(p_theme_type)) {
341 return;
342 }
343 icon_map[p_theme_type] = ThemeIconMap();
344}
345
346void Theme::remove_icon_type(const StringName &p_theme_type) {
347 if (!icon_map.has(p_theme_type)) {
348 return;
349 }
350
351 _freeze_change_propagation();
352
353 for (const KeyValue<StringName, Ref<Texture2D>> &E : icon_map[p_theme_type]) {
354 Ref<Texture2D> icon = E.value;
355 if (icon.is_valid()) {
356 icon->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed));
357 }
358 }
359
360 icon_map.erase(p_theme_type);
361
362 _unfreeze_and_propagate_changes();
363}
364
365void Theme::get_icon_type_list(List<StringName> *p_list) const {
366 ERR_FAIL_NULL(p_list);
367
368 for (const KeyValue<StringName, ThemeIconMap> &E : icon_map) {
369 p_list->push_back(E.key);
370 }
371}
372
373// Styleboxes.
374void Theme::set_stylebox(const StringName &p_name, const StringName &p_theme_type, const Ref<StyleBox> &p_style) {
375 ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name));
376 ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
377
378 bool existing = false;
379 if (style_map[p_theme_type].has(p_name) && style_map[p_theme_type][p_name].is_valid()) {
380 existing = true;
381 style_map[p_theme_type][p_name]->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed));
382 }
383
384 style_map[p_theme_type][p_name] = p_style;
385
386 if (p_style.is_valid()) {
387 style_map[p_theme_type][p_name]->connect_changed(callable_mp(this, &Theme::_emit_theme_changed).bind(false), CONNECT_REFERENCE_COUNTED);
388 }
389
390 _emit_theme_changed(!existing);
391}
392
393Ref<StyleBox> Theme::get_stylebox(const StringName &p_name, const StringName &p_theme_type) const {
394 if (style_map.has(p_theme_type) && style_map[p_theme_type].has(p_name) && style_map[p_theme_type][p_name].is_valid()) {
395 return style_map[p_theme_type][p_name];
396 } else {
397 return ThemeDB::get_singleton()->get_fallback_stylebox();
398 }
399}
400
401bool Theme::has_stylebox(const StringName &p_name, const StringName &p_theme_type) const {
402 return (style_map.has(p_theme_type) && style_map[p_theme_type].has(p_name) && style_map[p_theme_type][p_name].is_valid());
403}
404
405bool Theme::has_stylebox_nocheck(const StringName &p_name, const StringName &p_theme_type) const {
406 return (style_map.has(p_theme_type) && style_map[p_theme_type].has(p_name));
407}
408
409void Theme::rename_stylebox(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
410 ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name));
411 ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
412 ERR_FAIL_COND_MSG(!style_map.has(p_theme_type), "Cannot rename the stylebox '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist.");
413 ERR_FAIL_COND_MSG(style_map[p_theme_type].has(p_name), "Cannot rename the stylebox '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists.");
414 ERR_FAIL_COND_MSG(!style_map[p_theme_type].has(p_old_name), "Cannot rename the stylebox '" + String(p_old_name) + "' because it does not exist.");
415
416 style_map[p_theme_type][p_name] = style_map[p_theme_type][p_old_name];
417 style_map[p_theme_type].erase(p_old_name);
418
419 _emit_theme_changed(true);
420}
421
422void Theme::clear_stylebox(const StringName &p_name, const StringName &p_theme_type) {
423 ERR_FAIL_COND_MSG(!style_map.has(p_theme_type), "Cannot clear the stylebox '" + String(p_name) + "' because the node type '" + String(p_theme_type) + "' does not exist.");
424 ERR_FAIL_COND_MSG(!style_map[p_theme_type].has(p_name), "Cannot clear the stylebox '" + String(p_name) + "' because it does not exist.");
425
426 if (style_map[p_theme_type][p_name].is_valid()) {
427 style_map[p_theme_type][p_name]->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed));
428 }
429
430 style_map[p_theme_type].erase(p_name);
431
432 _emit_theme_changed(true);
433}
434
435void Theme::get_stylebox_list(StringName p_theme_type, List<StringName> *p_list) const {
436 ERR_FAIL_NULL(p_list);
437
438 if (!style_map.has(p_theme_type)) {
439 return;
440 }
441
442 for (const KeyValue<StringName, Ref<StyleBox>> &E : style_map[p_theme_type]) {
443 p_list->push_back(E.key);
444 }
445}
446
447void Theme::add_stylebox_type(const StringName &p_theme_type) {
448 ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
449
450 if (style_map.has(p_theme_type)) {
451 return;
452 }
453 style_map[p_theme_type] = ThemeStyleMap();
454}
455
456void Theme::remove_stylebox_type(const StringName &p_theme_type) {
457 if (!style_map.has(p_theme_type)) {
458 return;
459 }
460
461 _freeze_change_propagation();
462
463 for (const KeyValue<StringName, Ref<StyleBox>> &E : style_map[p_theme_type]) {
464 Ref<StyleBox> style = E.value;
465 if (style.is_valid()) {
466 style->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed));
467 }
468 }
469
470 style_map.erase(p_theme_type);
471
472 _unfreeze_and_propagate_changes();
473}
474
475void Theme::get_stylebox_type_list(List<StringName> *p_list) const {
476 ERR_FAIL_NULL(p_list);
477
478 for (const KeyValue<StringName, ThemeStyleMap> &E : style_map) {
479 p_list->push_back(E.key);
480 }
481}
482
483// Fonts.
484void Theme::set_font(const StringName &p_name, const StringName &p_theme_type, const Ref<Font> &p_font) {
485 ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name));
486 ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
487
488 bool existing = false;
489 if (font_map[p_theme_type][p_name].is_valid()) {
490 existing = true;
491 font_map[p_theme_type][p_name]->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed));
492 }
493
494 font_map[p_theme_type][p_name] = p_font;
495
496 if (p_font.is_valid()) {
497 font_map[p_theme_type][p_name]->connect_changed(callable_mp(this, &Theme::_emit_theme_changed).bind(false), CONNECT_REFERENCE_COUNTED);
498 }
499
500 _emit_theme_changed(!existing);
501}
502
503Ref<Font> Theme::get_font(const StringName &p_name, const StringName &p_theme_type) const {
504 if (font_map.has(p_theme_type) && font_map[p_theme_type].has(p_name) && font_map[p_theme_type][p_name].is_valid()) {
505 return font_map[p_theme_type][p_name];
506 } else if (has_default_font()) {
507 return default_font;
508 } else {
509 return ThemeDB::get_singleton()->get_fallback_font();
510 }
511}
512
513bool Theme::has_font(const StringName &p_name, const StringName &p_theme_type) const {
514 return ((font_map.has(p_theme_type) && font_map[p_theme_type].has(p_name) && font_map[p_theme_type][p_name].is_valid()) || has_default_font());
515}
516
517bool Theme::has_font_nocheck(const StringName &p_name, const StringName &p_theme_type) const {
518 return (font_map.has(p_theme_type) && font_map[p_theme_type].has(p_name));
519}
520
521void Theme::rename_font(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
522 ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name));
523 ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
524 ERR_FAIL_COND_MSG(!font_map.has(p_theme_type), "Cannot rename the font '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist.");
525 ERR_FAIL_COND_MSG(font_map[p_theme_type].has(p_name), "Cannot rename the font '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists.");
526 ERR_FAIL_COND_MSG(!font_map[p_theme_type].has(p_old_name), "Cannot rename the font '" + String(p_old_name) + "' because it does not exist.");
527
528 font_map[p_theme_type][p_name] = font_map[p_theme_type][p_old_name];
529 font_map[p_theme_type].erase(p_old_name);
530
531 _emit_theme_changed(true);
532}
533
534void Theme::clear_font(const StringName &p_name, const StringName &p_theme_type) {
535 ERR_FAIL_COND_MSG(!font_map.has(p_theme_type), "Cannot clear the font '" + String(p_name) + "' because the node type '" + String(p_theme_type) + "' does not exist.");
536 ERR_FAIL_COND_MSG(!font_map[p_theme_type].has(p_name), "Cannot clear the font '" + String(p_name) + "' because it does not exist.");
537
538 if (font_map[p_theme_type][p_name].is_valid()) {
539 font_map[p_theme_type][p_name]->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed));
540 }
541
542 font_map[p_theme_type].erase(p_name);
543
544 _emit_theme_changed(true);
545}
546
547void Theme::get_font_list(StringName p_theme_type, List<StringName> *p_list) const {
548 ERR_FAIL_NULL(p_list);
549
550 if (!font_map.has(p_theme_type)) {
551 return;
552 }
553
554 for (const KeyValue<StringName, Ref<Font>> &E : font_map[p_theme_type]) {
555 p_list->push_back(E.key);
556 }
557}
558
559void Theme::add_font_type(const StringName &p_theme_type) {
560 ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
561
562 if (font_map.has(p_theme_type)) {
563 return;
564 }
565 font_map[p_theme_type] = ThemeFontMap();
566}
567
568void Theme::remove_font_type(const StringName &p_theme_type) {
569 if (!font_map.has(p_theme_type)) {
570 return;
571 }
572
573 _freeze_change_propagation();
574
575 for (const KeyValue<StringName, Ref<Font>> &E : font_map[p_theme_type]) {
576 Ref<Font> font = E.value;
577 if (font.is_valid()) {
578 font->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed));
579 }
580 }
581
582 font_map.erase(p_theme_type);
583
584 _unfreeze_and_propagate_changes();
585}
586
587void Theme::get_font_type_list(List<StringName> *p_list) const {
588 ERR_FAIL_NULL(p_list);
589
590 for (const KeyValue<StringName, ThemeFontMap> &E : font_map) {
591 p_list->push_back(E.key);
592 }
593}
594
595// Font sizes.
596void Theme::set_font_size(const StringName &p_name, const StringName &p_theme_type, int p_font_size) {
597 ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name));
598 ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
599
600 bool existing = has_font_size_nocheck(p_name, p_theme_type);
601 font_size_map[p_theme_type][p_name] = p_font_size;
602
603 _emit_theme_changed(!existing);
604}
605
606int Theme::get_font_size(const StringName &p_name, const StringName &p_theme_type) const {
607 if (font_size_map.has(p_theme_type) && font_size_map[p_theme_type].has(p_name) && (font_size_map[p_theme_type][p_name] > 0)) {
608 return font_size_map[p_theme_type][p_name];
609 } else if (has_default_font_size()) {
610 return default_font_size;
611 } else {
612 return ThemeDB::get_singleton()->get_fallback_font_size();
613 }
614}
615
616bool Theme::has_font_size(const StringName &p_name, const StringName &p_theme_type) const {
617 return ((font_size_map.has(p_theme_type) && font_size_map[p_theme_type].has(p_name) && (font_size_map[p_theme_type][p_name] > 0)) || has_default_font_size());
618}
619
620bool Theme::has_font_size_nocheck(const StringName &p_name, const StringName &p_theme_type) const {
621 return (font_size_map.has(p_theme_type) && font_size_map[p_theme_type].has(p_name));
622}
623
624void Theme::rename_font_size(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
625 ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name));
626 ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
627 ERR_FAIL_COND_MSG(!font_size_map.has(p_theme_type), "Cannot rename the font size '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist.");
628 ERR_FAIL_COND_MSG(font_size_map[p_theme_type].has(p_name), "Cannot rename the font size '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists.");
629 ERR_FAIL_COND_MSG(!font_size_map[p_theme_type].has(p_old_name), "Cannot rename the font size '" + String(p_old_name) + "' because it does not exist.");
630
631 font_size_map[p_theme_type][p_name] = font_size_map[p_theme_type][p_old_name];
632 font_size_map[p_theme_type].erase(p_old_name);
633
634 _emit_theme_changed(true);
635}
636
637void Theme::clear_font_size(const StringName &p_name, const StringName &p_theme_type) {
638 ERR_FAIL_COND_MSG(!font_size_map.has(p_theme_type), "Cannot clear the font size '" + String(p_name) + "' because the node type '" + String(p_theme_type) + "' does not exist.");
639 ERR_FAIL_COND_MSG(!font_size_map[p_theme_type].has(p_name), "Cannot clear the font size '" + String(p_name) + "' because it does not exist.");
640
641 font_size_map[p_theme_type].erase(p_name);
642
643 _emit_theme_changed(true);
644}
645
646void Theme::get_font_size_list(StringName p_theme_type, List<StringName> *p_list) const {
647 ERR_FAIL_NULL(p_list);
648
649 if (!font_size_map.has(p_theme_type)) {
650 return;
651 }
652
653 for (const KeyValue<StringName, int> &E : font_size_map[p_theme_type]) {
654 p_list->push_back(E.key);
655 }
656}
657
658void Theme::add_font_size_type(const StringName &p_theme_type) {
659 ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
660
661 if (font_size_map.has(p_theme_type)) {
662 return;
663 }
664 font_size_map[p_theme_type] = ThemeFontSizeMap();
665}
666
667void Theme::remove_font_size_type(const StringName &p_theme_type) {
668 if (!font_size_map.has(p_theme_type)) {
669 return;
670 }
671
672 font_size_map.erase(p_theme_type);
673}
674
675void Theme::get_font_size_type_list(List<StringName> *p_list) const {
676 ERR_FAIL_NULL(p_list);
677
678 for (const KeyValue<StringName, ThemeFontSizeMap> &E : font_size_map) {
679 p_list->push_back(E.key);
680 }
681}
682
683// Colors.
684void Theme::set_color(const StringName &p_name, const StringName &p_theme_type, const Color &p_color) {
685 ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name));
686 ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
687
688 bool existing = has_color_nocheck(p_name, p_theme_type);
689 color_map[p_theme_type][p_name] = p_color;
690
691 _emit_theme_changed(!existing);
692}
693
694Color Theme::get_color(const StringName &p_name, const StringName &p_theme_type) const {
695 if (color_map.has(p_theme_type) && color_map[p_theme_type].has(p_name)) {
696 return color_map[p_theme_type][p_name];
697 } else {
698 return Color();
699 }
700}
701
702bool Theme::has_color(const StringName &p_name, const StringName &p_theme_type) const {
703 return (color_map.has(p_theme_type) && color_map[p_theme_type].has(p_name));
704}
705
706bool Theme::has_color_nocheck(const StringName &p_name, const StringName &p_theme_type) const {
707 return (color_map.has(p_theme_type) && color_map[p_theme_type].has(p_name));
708}
709
710void Theme::rename_color(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
711 ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name));
712 ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
713 ERR_FAIL_COND_MSG(!color_map.has(p_theme_type), "Cannot rename the color '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist.");
714 ERR_FAIL_COND_MSG(color_map[p_theme_type].has(p_name), "Cannot rename the color '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists.");
715 ERR_FAIL_COND_MSG(!color_map[p_theme_type].has(p_old_name), "Cannot rename the color '" + String(p_old_name) + "' because it does not exist.");
716
717 color_map[p_theme_type][p_name] = color_map[p_theme_type][p_old_name];
718 color_map[p_theme_type].erase(p_old_name);
719
720 _emit_theme_changed(true);
721}
722
723void Theme::clear_color(const StringName &p_name, const StringName &p_theme_type) {
724 ERR_FAIL_COND_MSG(!color_map.has(p_theme_type), "Cannot clear the color '" + String(p_name) + "' because the node type '" + String(p_theme_type) + "' does not exist.");
725 ERR_FAIL_COND_MSG(!color_map[p_theme_type].has(p_name), "Cannot clear the color '" + String(p_name) + "' because it does not exist.");
726
727 color_map[p_theme_type].erase(p_name);
728
729 _emit_theme_changed(true);
730}
731
732void Theme::get_color_list(StringName p_theme_type, List<StringName> *p_list) const {
733 ERR_FAIL_NULL(p_list);
734
735 if (!color_map.has(p_theme_type)) {
736 return;
737 }
738
739 for (const KeyValue<StringName, Color> &E : color_map[p_theme_type]) {
740 p_list->push_back(E.key);
741 }
742}
743
744void Theme::add_color_type(const StringName &p_theme_type) {
745 ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
746
747 if (color_map.has(p_theme_type)) {
748 return;
749 }
750 color_map[p_theme_type] = ThemeColorMap();
751}
752
753void Theme::remove_color_type(const StringName &p_theme_type) {
754 if (!color_map.has(p_theme_type)) {
755 return;
756 }
757
758 color_map.erase(p_theme_type);
759}
760
761void Theme::get_color_type_list(List<StringName> *p_list) const {
762 ERR_FAIL_NULL(p_list);
763
764 for (const KeyValue<StringName, ThemeColorMap> &E : color_map) {
765 p_list->push_back(E.key);
766 }
767}
768
769// Theme constants.
770void Theme::set_constant(const StringName &p_name, const StringName &p_theme_type, int p_constant) {
771 ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name));
772 ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
773
774 bool existing = has_constant_nocheck(p_name, p_theme_type);
775 constant_map[p_theme_type][p_name] = p_constant;
776
777 _emit_theme_changed(!existing);
778}
779
780int Theme::get_constant(const StringName &p_name, const StringName &p_theme_type) const {
781 if (constant_map.has(p_theme_type) && constant_map[p_theme_type].has(p_name)) {
782 return constant_map[p_theme_type][p_name];
783 } else {
784 return 0;
785 }
786}
787
788bool Theme::has_constant(const StringName &p_name, const StringName &p_theme_type) const {
789 return (constant_map.has(p_theme_type) && constant_map[p_theme_type].has(p_name));
790}
791
792bool Theme::has_constant_nocheck(const StringName &p_name, const StringName &p_theme_type) const {
793 return (constant_map.has(p_theme_type) && constant_map[p_theme_type].has(p_name));
794}
795
796void Theme::rename_constant(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
797 ERR_FAIL_COND_MSG(!is_valid_item_name(p_name), vformat("Invalid item name: '%s'", p_name));
798 ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
799 ERR_FAIL_COND_MSG(!constant_map.has(p_theme_type), "Cannot rename the constant '" + String(p_old_name) + "' because the node type '" + String(p_theme_type) + "' does not exist.");
800 ERR_FAIL_COND_MSG(constant_map[p_theme_type].has(p_name), "Cannot rename the constant '" + String(p_old_name) + "' because the new name '" + String(p_name) + "' already exists.");
801 ERR_FAIL_COND_MSG(!constant_map[p_theme_type].has(p_old_name), "Cannot rename the constant '" + String(p_old_name) + "' because it does not exist.");
802
803 constant_map[p_theme_type][p_name] = constant_map[p_theme_type][p_old_name];
804 constant_map[p_theme_type].erase(p_old_name);
805
806 _emit_theme_changed(true);
807}
808
809void Theme::clear_constant(const StringName &p_name, const StringName &p_theme_type) {
810 ERR_FAIL_COND_MSG(!constant_map.has(p_theme_type), "Cannot clear the constant '" + String(p_name) + "' because the node type '" + String(p_theme_type) + "' does not exist.");
811 ERR_FAIL_COND_MSG(!constant_map[p_theme_type].has(p_name), "Cannot clear the constant '" + String(p_name) + "' because it does not exist.");
812
813 constant_map[p_theme_type].erase(p_name);
814
815 _emit_theme_changed(true);
816}
817
818void Theme::get_constant_list(StringName p_theme_type, List<StringName> *p_list) const {
819 ERR_FAIL_NULL(p_list);
820
821 if (!constant_map.has(p_theme_type)) {
822 return;
823 }
824
825 for (const KeyValue<StringName, int> &E : constant_map[p_theme_type]) {
826 p_list->push_back(E.key);
827 }
828}
829
830void Theme::add_constant_type(const StringName &p_theme_type) {
831 ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
832
833 if (constant_map.has(p_theme_type)) {
834 return;
835 }
836 constant_map[p_theme_type] = ThemeConstantMap();
837}
838
839void Theme::remove_constant_type(const StringName &p_theme_type) {
840 if (!constant_map.has(p_theme_type)) {
841 return;
842 }
843
844 constant_map.erase(p_theme_type);
845}
846
847void Theme::get_constant_type_list(List<StringName> *p_list) const {
848 ERR_FAIL_NULL(p_list);
849
850 for (const KeyValue<StringName, ThemeConstantMap> &E : constant_map) {
851 p_list->push_back(E.key);
852 }
853}
854
855// Generic methods for managing theme items.
856void Theme::set_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type, const Variant &p_value) {
857 switch (p_data_type) {
858 case DATA_TYPE_COLOR: {
859 ERR_FAIL_COND_MSG(p_value.get_type() != Variant::COLOR, "Theme item's data type (Color) does not match Variant's type (" + Variant::get_type_name(p_value.get_type()) + ").");
860
861 Color color_value = p_value;
862 set_color(p_name, p_theme_type, color_value);
863 } break;
864 case DATA_TYPE_CONSTANT: {
865 ERR_FAIL_COND_MSG(p_value.get_type() != Variant::INT, "Theme item's data type (int) does not match Variant's type (" + Variant::get_type_name(p_value.get_type()) + ").");
866
867 int constant_value = p_value;
868 set_constant(p_name, p_theme_type, constant_value);
869 } break;
870 case DATA_TYPE_FONT: {
871 ERR_FAIL_COND_MSG(p_value.get_type() != Variant::OBJECT, "Theme item's data type (Object) does not match Variant's type (" + Variant::get_type_name(p_value.get_type()) + ").");
872
873 Ref<Font> font_value = Object::cast_to<Font>(p_value.get_validated_object());
874 set_font(p_name, p_theme_type, font_value);
875 } break;
876 case DATA_TYPE_FONT_SIZE: {
877 ERR_FAIL_COND_MSG(p_value.get_type() != Variant::INT, "Theme item's data type (int) does not match Variant's type (" + Variant::get_type_name(p_value.get_type()) + ").");
878
879 int font_size_value = p_value;
880 set_font_size(p_name, p_theme_type, font_size_value);
881 } break;
882 case DATA_TYPE_ICON: {
883 ERR_FAIL_COND_MSG(p_value.get_type() != Variant::OBJECT, "Theme item's data type (Object) does not match Variant's type (" + Variant::get_type_name(p_value.get_type()) + ").");
884
885 Ref<Texture2D> icon_value = Object::cast_to<Texture2D>(p_value.get_validated_object());
886 set_icon(p_name, p_theme_type, icon_value);
887 } break;
888 case DATA_TYPE_STYLEBOX: {
889 ERR_FAIL_COND_MSG(p_value.get_type() != Variant::OBJECT, "Theme item's data type (Object) does not match Variant's type (" + Variant::get_type_name(p_value.get_type()) + ").");
890
891 Ref<StyleBox> stylebox_value = Object::cast_to<StyleBox>(p_value.get_validated_object());
892 set_stylebox(p_name, p_theme_type, stylebox_value);
893 } break;
894 case DATA_TYPE_MAX:
895 break; // Can't happen, but silences warning.
896 }
897}
898
899Variant Theme::get_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type) const {
900 switch (p_data_type) {
901 case DATA_TYPE_COLOR:
902 return get_color(p_name, p_theme_type);
903 case DATA_TYPE_CONSTANT:
904 return get_constant(p_name, p_theme_type);
905 case DATA_TYPE_FONT:
906 return get_font(p_name, p_theme_type);
907 case DATA_TYPE_FONT_SIZE:
908 return get_font_size(p_name, p_theme_type);
909 case DATA_TYPE_ICON:
910 return get_icon(p_name, p_theme_type);
911 case DATA_TYPE_STYLEBOX:
912 return get_stylebox(p_name, p_theme_type);
913 case DATA_TYPE_MAX:
914 break; // Can't happen, but silences warning.
915 }
916
917 return Variant();
918}
919
920bool Theme::has_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type) const {
921 switch (p_data_type) {
922 case DATA_TYPE_COLOR:
923 return has_color(p_name, p_theme_type);
924 case DATA_TYPE_CONSTANT:
925 return has_constant(p_name, p_theme_type);
926 case DATA_TYPE_FONT:
927 return has_font(p_name, p_theme_type);
928 case DATA_TYPE_FONT_SIZE:
929 return has_font_size(p_name, p_theme_type);
930 case DATA_TYPE_ICON:
931 return has_icon(p_name, p_theme_type);
932 case DATA_TYPE_STYLEBOX:
933 return has_stylebox(p_name, p_theme_type);
934 case DATA_TYPE_MAX:
935 break; // Can't happen, but silences warning.
936 }
937
938 return false;
939}
940
941bool Theme::has_theme_item_nocheck(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type) const {
942 switch (p_data_type) {
943 case DATA_TYPE_COLOR:
944 return has_color_nocheck(p_name, p_theme_type);
945 case DATA_TYPE_CONSTANT:
946 return has_constant_nocheck(p_name, p_theme_type);
947 case DATA_TYPE_FONT:
948 return has_font_nocheck(p_name, p_theme_type);
949 case DATA_TYPE_FONT_SIZE:
950 return has_font_size_nocheck(p_name, p_theme_type);
951 case DATA_TYPE_ICON:
952 return has_icon_nocheck(p_name, p_theme_type);
953 case DATA_TYPE_STYLEBOX:
954 return has_stylebox_nocheck(p_name, p_theme_type);
955 case DATA_TYPE_MAX:
956 break; // Can't happen, but silences warning.
957 }
958
959 return false;
960}
961
962void Theme::rename_theme_item(DataType p_data_type, const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
963 switch (p_data_type) {
964 case DATA_TYPE_COLOR:
965 rename_color(p_old_name, p_name, p_theme_type);
966 break;
967 case DATA_TYPE_CONSTANT:
968 rename_constant(p_old_name, p_name, p_theme_type);
969 break;
970 case DATA_TYPE_FONT:
971 rename_font(p_old_name, p_name, p_theme_type);
972 break;
973 case DATA_TYPE_FONT_SIZE:
974 rename_font_size(p_old_name, p_name, p_theme_type);
975 break;
976 case DATA_TYPE_ICON:
977 rename_icon(p_old_name, p_name, p_theme_type);
978 break;
979 case DATA_TYPE_STYLEBOX:
980 rename_stylebox(p_old_name, p_name, p_theme_type);
981 break;
982 case DATA_TYPE_MAX:
983 break; // Can't happen, but silences warning.
984 }
985}
986
987void Theme::clear_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type) {
988 switch (p_data_type) {
989 case DATA_TYPE_COLOR:
990 clear_color(p_name, p_theme_type);
991 break;
992 case DATA_TYPE_CONSTANT:
993 clear_constant(p_name, p_theme_type);
994 break;
995 case DATA_TYPE_FONT:
996 clear_font(p_name, p_theme_type);
997 break;
998 case DATA_TYPE_FONT_SIZE:
999 clear_font_size(p_name, p_theme_type);
1000 break;
1001 case DATA_TYPE_ICON:
1002 clear_icon(p_name, p_theme_type);
1003 break;
1004 case DATA_TYPE_STYLEBOX:
1005 clear_stylebox(p_name, p_theme_type);
1006 break;
1007 case DATA_TYPE_MAX:
1008 break; // Can't happen, but silences warning.
1009 }
1010}
1011
1012void Theme::get_theme_item_list(DataType p_data_type, StringName p_theme_type, List<StringName> *p_list) const {
1013 switch (p_data_type) {
1014 case DATA_TYPE_COLOR:
1015 get_color_list(p_theme_type, p_list);
1016 break;
1017 case DATA_TYPE_CONSTANT:
1018 get_constant_list(p_theme_type, p_list);
1019 break;
1020 case DATA_TYPE_FONT:
1021 get_font_list(p_theme_type, p_list);
1022 break;
1023 case DATA_TYPE_FONT_SIZE:
1024 get_font_size_list(p_theme_type, p_list);
1025 break;
1026 case DATA_TYPE_ICON:
1027 get_icon_list(p_theme_type, p_list);
1028 break;
1029 case DATA_TYPE_STYLEBOX:
1030 get_stylebox_list(p_theme_type, p_list);
1031 break;
1032 case DATA_TYPE_MAX:
1033 break; // Can't happen, but silences warning.
1034 }
1035}
1036
1037void Theme::add_theme_item_type(DataType p_data_type, const StringName &p_theme_type) {
1038 switch (p_data_type) {
1039 case DATA_TYPE_COLOR:
1040 add_color_type(p_theme_type);
1041 break;
1042 case DATA_TYPE_CONSTANT:
1043 add_constant_type(p_theme_type);
1044 break;
1045 case DATA_TYPE_FONT:
1046 add_font_type(p_theme_type);
1047 break;
1048 case DATA_TYPE_FONT_SIZE:
1049 add_font_size_type(p_theme_type);
1050 break;
1051 case DATA_TYPE_ICON:
1052 add_icon_type(p_theme_type);
1053 break;
1054 case DATA_TYPE_STYLEBOX:
1055 add_stylebox_type(p_theme_type);
1056 break;
1057 case DATA_TYPE_MAX:
1058 break; // Can't happen, but silences warning.
1059 }
1060}
1061
1062void Theme::remove_theme_item_type(DataType p_data_type, const StringName &p_theme_type) {
1063 switch (p_data_type) {
1064 case DATA_TYPE_COLOR:
1065 remove_color_type(p_theme_type);
1066 break;
1067 case DATA_TYPE_CONSTANT:
1068 remove_constant_type(p_theme_type);
1069 break;
1070 case DATA_TYPE_FONT:
1071 remove_font_type(p_theme_type);
1072 break;
1073 case DATA_TYPE_FONT_SIZE:
1074 remove_font_size_type(p_theme_type);
1075 break;
1076 case DATA_TYPE_ICON:
1077 remove_icon_type(p_theme_type);
1078 break;
1079 case DATA_TYPE_STYLEBOX:
1080 remove_stylebox_type(p_theme_type);
1081 break;
1082 case DATA_TYPE_MAX:
1083 break; // Can't happen, but silences warning.
1084 }
1085}
1086
1087void Theme::get_theme_item_type_list(DataType p_data_type, List<StringName> *p_list) const {
1088 switch (p_data_type) {
1089 case DATA_TYPE_COLOR:
1090 get_color_type_list(p_list);
1091 break;
1092 case DATA_TYPE_CONSTANT:
1093 get_constant_type_list(p_list);
1094 break;
1095 case DATA_TYPE_FONT:
1096 get_font_type_list(p_list);
1097 break;
1098 case DATA_TYPE_FONT_SIZE:
1099 get_font_size_type_list(p_list);
1100 break;
1101 case DATA_TYPE_ICON:
1102 get_icon_type_list(p_list);
1103 break;
1104 case DATA_TYPE_STYLEBOX:
1105 get_stylebox_type_list(p_list);
1106 break;
1107 case DATA_TYPE_MAX:
1108 break; // Can't happen, but silences warning.
1109 }
1110}
1111
1112// Theme type variations.
1113void Theme::set_type_variation(const StringName &p_theme_type, const StringName &p_base_type) {
1114 ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type));
1115 ERR_FAIL_COND_MSG(!is_valid_type_name(p_base_type), vformat("Invalid type name: '%s'", p_base_type));
1116 ERR_FAIL_COND_MSG(p_theme_type == StringName(), "An empty theme type cannot be marked as a variation of another type.");
1117 ERR_FAIL_COND_MSG(ClassDB::class_exists(p_theme_type), "A type associated with a built-in class cannot be marked as a variation of another type.");
1118 ERR_FAIL_COND_MSG(p_base_type == StringName(), "An empty theme type cannot be the base type of a variation. Use clear_type_variation() instead if you want to unmark '" + String(p_theme_type) + "' as a variation.");
1119
1120 if (variation_map.has(p_theme_type)) {
1121 StringName old_base = variation_map[p_theme_type];
1122 variation_base_map[old_base].erase(p_theme_type);
1123 }
1124
1125 variation_map[p_theme_type] = p_base_type;
1126 variation_base_map[p_base_type].push_back(p_theme_type);
1127
1128 _emit_theme_changed(true);
1129}
1130
1131bool Theme::is_type_variation(const StringName &p_theme_type, const StringName &p_base_type) const {
1132 return (variation_map.has(p_theme_type) && variation_map[p_theme_type] == p_base_type);
1133}
1134
1135void Theme::clear_type_variation(const StringName &p_theme_type) {
1136 ERR_FAIL_COND_MSG(!variation_map.has(p_theme_type), "Cannot clear the type variation '" + String(p_theme_type) + "' because it does not exist.");
1137
1138 StringName base_type = variation_map[p_theme_type];
1139 variation_base_map[base_type].erase(p_theme_type);
1140 variation_map.erase(p_theme_type);
1141
1142 _emit_theme_changed(true);
1143}
1144
1145StringName Theme::get_type_variation_base(const StringName &p_theme_type) const {
1146 if (!variation_map.has(p_theme_type)) {
1147 return StringName();
1148 }
1149
1150 return variation_map[p_theme_type];
1151}
1152
1153void Theme::get_type_variation_list(const StringName &p_base_type, List<StringName> *p_list) const {
1154 ERR_FAIL_NULL(p_list);
1155
1156 if (!variation_base_map.has(p_base_type)) {
1157 return;
1158 }
1159
1160 for (const StringName &E : variation_base_map[p_base_type]) {
1161 // Prevent infinite loops if variants were set to be cross-dependent (that's still invalid usage, but handling for stability sake).
1162 if (p_list->find(E)) {
1163 continue;
1164 }
1165
1166 p_list->push_back(E);
1167 // Continue looking for sub-variations.
1168 get_type_variation_list(E, p_list);
1169 }
1170}
1171
1172// Theme types.
1173void Theme::add_type(const StringName &p_theme_type) {
1174 // Add a record to every data type map.
1175 for (int i = 0; i < Theme::DATA_TYPE_MAX; i++) {
1176 Theme::DataType dt = (Theme::DataType)i;
1177 add_theme_item_type(dt, p_theme_type);
1178 }
1179
1180 _emit_theme_changed(true);
1181}
1182
1183void Theme::remove_type(const StringName &p_theme_type) {
1184 // Gracefully remove the record from every data type map.
1185 for (int i = 0; i < Theme::DATA_TYPE_MAX; i++) {
1186 Theme::DataType dt = (Theme::DataType)i;
1187 remove_theme_item_type(dt, p_theme_type);
1188 }
1189
1190 // If type is a variation, remove that connection.
1191 if (get_type_variation_base(p_theme_type) != StringName()) {
1192 clear_type_variation(p_theme_type);
1193 }
1194
1195 // If type is a variation base, remove all those connections.
1196 List<StringName> names;
1197 get_type_variation_list(p_theme_type, &names);
1198 for (const StringName &E : names) {
1199 clear_type_variation(E);
1200 }
1201
1202 _emit_theme_changed(true);
1203}
1204
1205void Theme::get_type_list(List<StringName> *p_list) const {
1206 ERR_FAIL_NULL(p_list);
1207
1208 // This Set guarantees uniqueness.
1209 // Because each map can have the same type defined, but for this method
1210 // we only want one occurrence of each type.
1211 HashSet<StringName> types;
1212
1213 // Icons.
1214 for (const KeyValue<StringName, ThemeIconMap> &E : icon_map) {
1215 types.insert(E.key);
1216 }
1217
1218 // Styles.
1219 for (const KeyValue<StringName, ThemeStyleMap> &E : style_map) {
1220 types.insert(E.key);
1221 }
1222
1223 // Fonts.
1224 for (const KeyValue<StringName, ThemeFontMap> &E : font_map) {
1225 types.insert(E.key);
1226 }
1227
1228 // Font sizes.
1229 for (const KeyValue<StringName, ThemeFontSizeMap> &E : font_size_map) {
1230 types.insert(E.key);
1231 }
1232
1233 // Colors.
1234 for (const KeyValue<StringName, ThemeColorMap> &E : color_map) {
1235 types.insert(E.key);
1236 }
1237
1238 // Constants.
1239 for (const KeyValue<StringName, ThemeConstantMap> &E : constant_map) {
1240 types.insert(E.key);
1241 }
1242
1243 for (const StringName &E : types) {
1244 p_list->push_back(E);
1245 }
1246}
1247
1248void Theme::get_type_dependencies(const StringName &p_base_type, const StringName &p_type_variation, List<StringName> *p_list) {
1249 ERR_FAIL_NULL(p_list);
1250
1251 // Build the dependency chain for type variations.
1252 if (p_type_variation != StringName()) {
1253 StringName variation_name = p_type_variation;
1254 while (variation_name != StringName()) {
1255 p_list->push_back(variation_name);
1256 variation_name = get_type_variation_base(variation_name);
1257
1258 // If we have reached the base type dependency, it's safe to stop (assuming no funny business was done to the Theme).
1259 if (variation_name == p_base_type) {
1260 break;
1261 }
1262 }
1263 }
1264
1265 // Continue building the chain using native class hierarchy.
1266 ThemeDB::get_singleton()->get_native_type_dependencies(p_base_type, p_list);
1267}
1268
1269// Internal methods for getting lists as a Vector of String (compatible with public API).
1270Vector<String> Theme::_get_icon_list(const String &p_theme_type) const {
1271 Vector<String> ilret;
1272 List<StringName> il;
1273
1274 get_icon_list(p_theme_type, &il);
1275 ilret.resize(il.size());
1276
1277 int i = 0;
1278 String *w = ilret.ptrw();
1279 for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
1280 w[i] = E->get();
1281 }
1282 return ilret;
1283}
1284
1285Vector<String> Theme::_get_icon_type_list() const {
1286 Vector<String> ilret;
1287 List<StringName> il;
1288
1289 get_icon_type_list(&il);
1290 ilret.resize(il.size());
1291
1292 int i = 0;
1293 String *w = ilret.ptrw();
1294 for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
1295 w[i] = E->get();
1296 }
1297 return ilret;
1298}
1299
1300Vector<String> Theme::_get_stylebox_list(const String &p_theme_type) const {
1301 Vector<String> ilret;
1302 List<StringName> il;
1303
1304 get_stylebox_list(p_theme_type, &il);
1305 ilret.resize(il.size());
1306
1307 int i = 0;
1308 String *w = ilret.ptrw();
1309 for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
1310 w[i] = E->get();
1311 }
1312 return ilret;
1313}
1314
1315Vector<String> Theme::_get_stylebox_type_list() const {
1316 Vector<String> ilret;
1317 List<StringName> il;
1318
1319 get_stylebox_type_list(&il);
1320 ilret.resize(il.size());
1321
1322 int i = 0;
1323 String *w = ilret.ptrw();
1324 for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
1325 w[i] = E->get();
1326 }
1327 return ilret;
1328}
1329
1330Vector<String> Theme::_get_font_list(const String &p_theme_type) const {
1331 Vector<String> ilret;
1332 List<StringName> il;
1333
1334 get_font_list(p_theme_type, &il);
1335 ilret.resize(il.size());
1336
1337 int i = 0;
1338 String *w = ilret.ptrw();
1339 for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
1340 w[i] = E->get();
1341 }
1342 return ilret;
1343}
1344
1345Vector<String> Theme::_get_font_type_list() const {
1346 Vector<String> ilret;
1347 List<StringName> il;
1348
1349 get_font_type_list(&il);
1350 ilret.resize(il.size());
1351
1352 int i = 0;
1353 String *w = ilret.ptrw();
1354 for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
1355 w[i] = E->get();
1356 }
1357 return ilret;
1358}
1359
1360Vector<String> Theme::_get_font_size_list(const String &p_theme_type) const {
1361 Vector<String> ilret;
1362 List<StringName> il;
1363
1364 get_font_size_list(p_theme_type, &il);
1365 ilret.resize(il.size());
1366
1367 int i = 0;
1368 String *w = ilret.ptrw();
1369 for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
1370 w[i] = E->get();
1371 }
1372 return ilret;
1373}
1374
1375Vector<String> Theme::_get_font_size_type_list() const {
1376 Vector<String> ilret;
1377 List<StringName> il;
1378
1379 get_font_size_type_list(&il);
1380 ilret.resize(il.size());
1381
1382 int i = 0;
1383 String *w = ilret.ptrw();
1384 for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
1385 w[i] = E->get();
1386 }
1387 return ilret;
1388}
1389
1390Vector<String> Theme::_get_color_list(const String &p_theme_type) const {
1391 Vector<String> ilret;
1392 List<StringName> il;
1393
1394 get_color_list(p_theme_type, &il);
1395 ilret.resize(il.size());
1396
1397 int i = 0;
1398 String *w = ilret.ptrw();
1399 for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
1400 w[i] = E->get();
1401 }
1402 return ilret;
1403}
1404
1405Vector<String> Theme::_get_color_type_list() const {
1406 Vector<String> ilret;
1407 List<StringName> il;
1408
1409 get_color_type_list(&il);
1410 ilret.resize(il.size());
1411
1412 int i = 0;
1413 String *w = ilret.ptrw();
1414 for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
1415 w[i] = E->get();
1416 }
1417 return ilret;
1418}
1419
1420Vector<String> Theme::_get_constant_list(const String &p_theme_type) const {
1421 Vector<String> ilret;
1422 List<StringName> il;
1423
1424 get_constant_list(p_theme_type, &il);
1425 ilret.resize(il.size());
1426
1427 int i = 0;
1428 String *w = ilret.ptrw();
1429 for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
1430 w[i] = E->get();
1431 }
1432 return ilret;
1433}
1434
1435Vector<String> Theme::_get_constant_type_list() const {
1436 Vector<String> ilret;
1437 List<StringName> il;
1438
1439 get_constant_type_list(&il);
1440 ilret.resize(il.size());
1441
1442 int i = 0;
1443 String *w = ilret.ptrw();
1444 for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
1445 w[i] = E->get();
1446 }
1447 return ilret;
1448}
1449
1450Vector<String> Theme::_get_theme_item_list(DataType p_data_type, const String &p_theme_type) const {
1451 switch (p_data_type) {
1452 case DATA_TYPE_COLOR:
1453 return _get_color_list(p_theme_type);
1454 case DATA_TYPE_CONSTANT:
1455 return _get_constant_list(p_theme_type);
1456 case DATA_TYPE_FONT:
1457 return _get_font_list(p_theme_type);
1458 case DATA_TYPE_FONT_SIZE:
1459 return _get_font_size_list(p_theme_type);
1460 case DATA_TYPE_ICON:
1461 return _get_icon_list(p_theme_type);
1462 case DATA_TYPE_STYLEBOX:
1463 return _get_stylebox_list(p_theme_type);
1464 case DATA_TYPE_MAX:
1465 break; // Can't happen, but silences warning.
1466 }
1467
1468 return Vector<String>();
1469}
1470
1471Vector<String> Theme::_get_theme_item_type_list(DataType p_data_type) const {
1472 switch (p_data_type) {
1473 case DATA_TYPE_COLOR:
1474 return _get_color_type_list();
1475 case DATA_TYPE_CONSTANT:
1476 return _get_constant_type_list();
1477 case DATA_TYPE_FONT:
1478 return _get_font_type_list();
1479 case DATA_TYPE_FONT_SIZE:
1480 return _get_font_size_type_list();
1481 case DATA_TYPE_ICON:
1482 return _get_icon_type_list();
1483 case DATA_TYPE_STYLEBOX:
1484 return _get_stylebox_type_list();
1485 case DATA_TYPE_MAX:
1486 break; // Can't happen, but silences warning.
1487 }
1488
1489 return Vector<String>();
1490}
1491
1492Vector<String> Theme::_get_type_variation_list(const StringName &p_theme_type) const {
1493 Vector<String> ilret;
1494 List<StringName> il;
1495
1496 get_type_variation_list(p_theme_type, &il);
1497 ilret.resize(il.size());
1498
1499 int i = 0;
1500 String *w = ilret.ptrw();
1501 for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
1502 w[i] = E->get();
1503 }
1504 return ilret;
1505}
1506
1507Vector<String> Theme::_get_type_list() const {
1508 Vector<String> ilret;
1509 List<StringName> il;
1510
1511 get_type_list(&il);
1512 ilret.resize(il.size());
1513
1514 int i = 0;
1515 String *w = ilret.ptrw();
1516 for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
1517 w[i] = E->get();
1518 }
1519 return ilret;
1520}
1521
1522// Theme bulk manipulations.
1523void Theme::_emit_theme_changed(bool p_notify_list_changed) {
1524 if (no_change_propagation) {
1525 return;
1526 }
1527
1528 if (p_notify_list_changed) {
1529 notify_property_list_changed();
1530 }
1531 emit_changed();
1532}
1533
1534void Theme::_freeze_change_propagation() {
1535 no_change_propagation = true;
1536}
1537
1538void Theme::_unfreeze_and_propagate_changes() {
1539 no_change_propagation = false;
1540 _emit_theme_changed(true);
1541}
1542
1543void Theme::merge_with(const Ref<Theme> &p_other) {
1544 if (p_other.is_null()) {
1545 return;
1546 }
1547
1548 _freeze_change_propagation();
1549
1550 // Colors.
1551 {
1552 for (const KeyValue<StringName, ThemeColorMap> &E : p_other->color_map) {
1553 for (const KeyValue<StringName, Color> &F : E.value) {
1554 set_color(F.key, E.key, F.value);
1555 }
1556 }
1557 }
1558
1559 // Constants.
1560 {
1561 for (const KeyValue<StringName, ThemeConstantMap> &E : p_other->constant_map) {
1562 for (const KeyValue<StringName, int> &F : E.value) {
1563 set_constant(F.key, E.key, F.value);
1564 }
1565 }
1566 }
1567
1568 // Fonts.
1569 {
1570 for (const KeyValue<StringName, ThemeFontMap> &E : p_other->font_map) {
1571 for (const KeyValue<StringName, Ref<Font>> &F : E.value) {
1572 set_font(F.key, E.key, F.value);
1573 }
1574 }
1575 }
1576
1577 // Font sizes.
1578 {
1579 for (const KeyValue<StringName, ThemeFontSizeMap> &E : p_other->font_size_map) {
1580 for (const KeyValue<StringName, int> &F : E.value) {
1581 set_font_size(F.key, E.key, F.value);
1582 }
1583 }
1584 }
1585
1586 // Icons.
1587 {
1588 for (const KeyValue<StringName, ThemeIconMap> &E : p_other->icon_map) {
1589 for (const KeyValue<StringName, Ref<Texture2D>> &F : E.value) {
1590 set_icon(F.key, E.key, F.value);
1591 }
1592 }
1593 }
1594
1595 // Styleboxes.
1596 {
1597 for (const KeyValue<StringName, ThemeStyleMap> &E : p_other->style_map) {
1598 for (const KeyValue<StringName, Ref<StyleBox>> &F : E.value) {
1599 set_stylebox(F.key, E.key, F.value);
1600 }
1601 }
1602 }
1603
1604 // Type variations.
1605 {
1606 for (const KeyValue<StringName, StringName> &E : p_other->variation_map) {
1607 set_type_variation(E.key, E.value);
1608 }
1609 }
1610
1611 _unfreeze_and_propagate_changes();
1612}
1613
1614void Theme::clear() {
1615 // These items need disconnecting.
1616 {
1617 for (const KeyValue<StringName, ThemeIconMap> &E : icon_map) {
1618 for (const KeyValue<StringName, Ref<Texture2D>> &F : E.value) {
1619 if (F.value.is_valid()) {
1620 Ref<Texture2D> icon = F.value;
1621 icon->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed));
1622 }
1623 }
1624 }
1625 }
1626
1627 {
1628 for (const KeyValue<StringName, ThemeStyleMap> &E : style_map) {
1629 for (const KeyValue<StringName, Ref<StyleBox>> &F : E.value) {
1630 if (F.value.is_valid()) {
1631 Ref<StyleBox> style = F.value;
1632 style->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed));
1633 }
1634 }
1635 }
1636 }
1637
1638 {
1639 for (const KeyValue<StringName, ThemeFontMap> &E : font_map) {
1640 for (const KeyValue<StringName, Ref<Font>> &F : E.value) {
1641 if (F.value.is_valid()) {
1642 Ref<Font> font = F.value;
1643 font->disconnect_changed(callable_mp(this, &Theme::_emit_theme_changed));
1644 }
1645 }
1646 }
1647 }
1648
1649 icon_map.clear();
1650 style_map.clear();
1651 font_map.clear();
1652 font_size_map.clear();
1653 color_map.clear();
1654 constant_map.clear();
1655
1656 variation_map.clear();
1657 variation_base_map.clear();
1658
1659 _emit_theme_changed(true);
1660}
1661
1662void Theme::reset_state() {
1663 clear();
1664}
1665
1666void Theme::_bind_methods() {
1667 ClassDB::bind_method(D_METHOD("set_icon", "name", "theme_type", "texture"), &Theme::set_icon);
1668 ClassDB::bind_method(D_METHOD("get_icon", "name", "theme_type"), &Theme::get_icon);
1669 ClassDB::bind_method(D_METHOD("has_icon", "name", "theme_type"), &Theme::has_icon);
1670 ClassDB::bind_method(D_METHOD("rename_icon", "old_name", "name", "theme_type"), &Theme::rename_icon);
1671 ClassDB::bind_method(D_METHOD("clear_icon", "name", "theme_type"), &Theme::clear_icon);
1672 ClassDB::bind_method(D_METHOD("get_icon_list", "theme_type"), &Theme::_get_icon_list);
1673 ClassDB::bind_method(D_METHOD("get_icon_type_list"), &Theme::_get_icon_type_list);
1674
1675 ClassDB::bind_method(D_METHOD("set_stylebox", "name", "theme_type", "texture"), &Theme::set_stylebox);
1676 ClassDB::bind_method(D_METHOD("get_stylebox", "name", "theme_type"), &Theme::get_stylebox);
1677 ClassDB::bind_method(D_METHOD("has_stylebox", "name", "theme_type"), &Theme::has_stylebox);
1678 ClassDB::bind_method(D_METHOD("rename_stylebox", "old_name", "name", "theme_type"), &Theme::rename_stylebox);
1679 ClassDB::bind_method(D_METHOD("clear_stylebox", "name", "theme_type"), &Theme::clear_stylebox);
1680 ClassDB::bind_method(D_METHOD("get_stylebox_list", "theme_type"), &Theme::_get_stylebox_list);
1681 ClassDB::bind_method(D_METHOD("get_stylebox_type_list"), &Theme::_get_stylebox_type_list);
1682
1683 ClassDB::bind_method(D_METHOD("set_font", "name", "theme_type", "font"), &Theme::set_font);
1684 ClassDB::bind_method(D_METHOD("get_font", "name", "theme_type"), &Theme::get_font);
1685 ClassDB::bind_method(D_METHOD("has_font", "name", "theme_type"), &Theme::has_font);
1686 ClassDB::bind_method(D_METHOD("rename_font", "old_name", "name", "theme_type"), &Theme::rename_font);
1687 ClassDB::bind_method(D_METHOD("clear_font", "name", "theme_type"), &Theme::clear_font);
1688 ClassDB::bind_method(D_METHOD("get_font_list", "theme_type"), &Theme::_get_font_list);
1689 ClassDB::bind_method(D_METHOD("get_font_type_list"), &Theme::_get_font_type_list);
1690
1691 ClassDB::bind_method(D_METHOD("set_font_size", "name", "theme_type", "font_size"), &Theme::set_font_size);
1692 ClassDB::bind_method(D_METHOD("get_font_size", "name", "theme_type"), &Theme::get_font_size);
1693 ClassDB::bind_method(D_METHOD("has_font_size", "name", "theme_type"), &Theme::has_font_size);
1694 ClassDB::bind_method(D_METHOD("rename_font_size", "old_name", "name", "theme_type"), &Theme::rename_font_size);
1695 ClassDB::bind_method(D_METHOD("clear_font_size", "name", "theme_type"), &Theme::clear_font_size);
1696 ClassDB::bind_method(D_METHOD("get_font_size_list", "theme_type"), &Theme::_get_font_size_list);
1697 ClassDB::bind_method(D_METHOD("get_font_size_type_list"), &Theme::_get_font_size_type_list);
1698
1699 ClassDB::bind_method(D_METHOD("set_color", "name", "theme_type", "color"), &Theme::set_color);
1700 ClassDB::bind_method(D_METHOD("get_color", "name", "theme_type"), &Theme::get_color);
1701 ClassDB::bind_method(D_METHOD("has_color", "name", "theme_type"), &Theme::has_color);
1702 ClassDB::bind_method(D_METHOD("rename_color", "old_name", "name", "theme_type"), &Theme::rename_color);
1703 ClassDB::bind_method(D_METHOD("clear_color", "name", "theme_type"), &Theme::clear_color);
1704 ClassDB::bind_method(D_METHOD("get_color_list", "theme_type"), &Theme::_get_color_list);
1705 ClassDB::bind_method(D_METHOD("get_color_type_list"), &Theme::_get_color_type_list);
1706
1707 ClassDB::bind_method(D_METHOD("set_constant", "name", "theme_type", "constant"), &Theme::set_constant);
1708 ClassDB::bind_method(D_METHOD("get_constant", "name", "theme_type"), &Theme::get_constant);
1709 ClassDB::bind_method(D_METHOD("has_constant", "name", "theme_type"), &Theme::has_constant);
1710 ClassDB::bind_method(D_METHOD("rename_constant", "old_name", "name", "theme_type"), &Theme::rename_constant);
1711 ClassDB::bind_method(D_METHOD("clear_constant", "name", "theme_type"), &Theme::clear_constant);
1712 ClassDB::bind_method(D_METHOD("get_constant_list", "theme_type"), &Theme::_get_constant_list);
1713 ClassDB::bind_method(D_METHOD("get_constant_type_list"), &Theme::_get_constant_type_list);
1714
1715 ClassDB::bind_method(D_METHOD("set_default_base_scale", "base_scale"), &Theme::set_default_base_scale);
1716 ClassDB::bind_method(D_METHOD("get_default_base_scale"), &Theme::get_default_base_scale);
1717 ClassDB::bind_method(D_METHOD("has_default_base_scale"), &Theme::has_default_base_scale);
1718
1719 ClassDB::bind_method(D_METHOD("set_default_font", "font"), &Theme::set_default_font);
1720 ClassDB::bind_method(D_METHOD("get_default_font"), &Theme::get_default_font);
1721 ClassDB::bind_method(D_METHOD("has_default_font"), &Theme::has_default_font);
1722
1723 ClassDB::bind_method(D_METHOD("set_default_font_size", "font_size"), &Theme::set_default_font_size);
1724 ClassDB::bind_method(D_METHOD("get_default_font_size"), &Theme::get_default_font_size);
1725 ClassDB::bind_method(D_METHOD("has_default_font_size"), &Theme::has_default_font_size);
1726
1727 ClassDB::bind_method(D_METHOD("set_theme_item", "data_type", "name", "theme_type", "value"), &Theme::set_theme_item);
1728 ClassDB::bind_method(D_METHOD("get_theme_item", "data_type", "name", "theme_type"), &Theme::get_theme_item);
1729 ClassDB::bind_method(D_METHOD("has_theme_item", "data_type", "name", "theme_type"), &Theme::has_theme_item);
1730 ClassDB::bind_method(D_METHOD("rename_theme_item", "data_type", "old_name", "name", "theme_type"), &Theme::rename_theme_item);
1731 ClassDB::bind_method(D_METHOD("clear_theme_item", "data_type", "name", "theme_type"), &Theme::clear_theme_item);
1732 ClassDB::bind_method(D_METHOD("get_theme_item_list", "data_type", "theme_type"), &Theme::_get_theme_item_list);
1733 ClassDB::bind_method(D_METHOD("get_theme_item_type_list", "data_type"), &Theme::_get_theme_item_type_list);
1734
1735 ClassDB::bind_method(D_METHOD("set_type_variation", "theme_type", "base_type"), &Theme::set_type_variation);
1736 ClassDB::bind_method(D_METHOD("is_type_variation", "theme_type", "base_type"), &Theme::is_type_variation);
1737 ClassDB::bind_method(D_METHOD("clear_type_variation", "theme_type"), &Theme::clear_type_variation);
1738 ClassDB::bind_method(D_METHOD("get_type_variation_base", "theme_type"), &Theme::get_type_variation_base);
1739 ClassDB::bind_method(D_METHOD("get_type_variation_list", "base_type"), &Theme::_get_type_variation_list);
1740
1741 ClassDB::bind_method(D_METHOD("add_type", "theme_type"), &Theme::add_type);
1742 ClassDB::bind_method(D_METHOD("remove_type", "theme_type"), &Theme::remove_type);
1743 ClassDB::bind_method(D_METHOD("get_type_list"), &Theme::_get_type_list);
1744
1745 ClassDB::bind_method(D_METHOD("merge_with", "other"), &Theme::merge_with);
1746 ClassDB::bind_method(D_METHOD("clear"), &Theme::clear);
1747
1748 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "default_base_scale", PROPERTY_HINT_RANGE, "0.0,2.0,0.01,or_greater"), "set_default_base_scale", "get_default_base_scale");
1749 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "default_font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_default_font", "get_default_font");
1750 ADD_PROPERTY(PropertyInfo(Variant::INT, "default_font_size", PROPERTY_HINT_RANGE, "0,256,1,or_greater,suffix:px"), "set_default_font_size", "get_default_font_size");
1751
1752 BIND_ENUM_CONSTANT(DATA_TYPE_COLOR);
1753 BIND_ENUM_CONSTANT(DATA_TYPE_CONSTANT);
1754 BIND_ENUM_CONSTANT(DATA_TYPE_FONT);
1755 BIND_ENUM_CONSTANT(DATA_TYPE_FONT_SIZE);
1756 BIND_ENUM_CONSTANT(DATA_TYPE_ICON);
1757 BIND_ENUM_CONSTANT(DATA_TYPE_STYLEBOX);
1758 BIND_ENUM_CONSTANT(DATA_TYPE_MAX);
1759}
1760
1761Theme::Theme() {
1762}
1763
1764Theme::~Theme() {
1765}
1766