1 /*************************************************************************/
2 /* theme.cpp */
3 /*************************************************************************/
4 /* This file is part of: */
5 /* GODOT ENGINE */
6 /* https://godotengine.org */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
9 /* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
10 /* */
11 /* Permission is hereby granted, free of charge, to any person obtaining */
12 /* a copy of this software and associated documentation files (the */
13 /* "Software"), to deal in the Software without restriction, including */
14 /* without limitation the rights to use, copy, modify, merge, publish, */
15 /* distribute, sublicense, and/or sell copies of the Software, and to */
16 /* permit persons to whom the Software is furnished to do so, subject to */
17 /* the following conditions: */
18 /* */
19 /* The above copyright notice and this permission notice shall be */
20 /* included in all copies or substantial portions of the Software. */
21 /* */
22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29 /*************************************************************************/
30 #include "theme.h"
31 #include "os/file_access.h"
32 #include "print_string.h"
33
34 Ref<Theme> Theme::default_theme;
35
_emit_theme_changed()36 void Theme::_emit_theme_changed() {
37
38 emit_changed();
39 }
40
_ref_font(Ref<Font> p_sc)41 void Theme::_ref_font(Ref<Font> p_sc) {
42
43 if (!font_refcount.has(p_sc)) {
44 font_refcount[p_sc] = 1;
45 p_sc->connect("changed", this, "_emit_theme_changed");
46 } else {
47 font_refcount[p_sc] += 1;
48 }
49 }
50
_unref_font(Ref<Font> p_sc)51 void Theme::_unref_font(Ref<Font> p_sc) {
52
53 ERR_FAIL_COND(!font_refcount.has(p_sc));
54 font_refcount[p_sc]--;
55 if (font_refcount[p_sc] == 0) {
56 p_sc->disconnect("changed", this, "_emit_theme_changed");
57 font_refcount.erase(p_sc);
58 }
59 }
60
_set(const StringName & p_name,const Variant & p_value)61 bool Theme::_set(const StringName &p_name, const Variant &p_value) {
62
63 String sname = p_name;
64
65 if (sname.find("/") != -1) {
66
67 String type = sname.get_slicec('/', 1);
68 String node_type = sname.get_slicec('/', 0);
69 String name = sname.get_slicec('/', 2);
70
71 if (type == "icons") {
72
73 set_icon(name, node_type, p_value);
74 } else if (type == "styles") {
75
76 set_stylebox(name, node_type, p_value);
77 } else if (type == "fonts") {
78
79 set_font(name, node_type, p_value);
80 } else if (type == "colors") {
81
82 set_color(name, node_type, p_value);
83 } else if (type == "constants") {
84
85 set_constant(name, node_type, p_value);
86 } else
87 return false;
88
89 return true;
90 }
91
92 return false;
93 }
94
_get(const StringName & p_name,Variant & r_ret) const95 bool Theme::_get(const StringName &p_name, Variant &r_ret) const {
96
97 String sname = p_name;
98
99 if (sname.find("/") != -1) {
100
101 String type = sname.get_slicec('/', 1);
102 String node_type = sname.get_slicec('/', 0);
103 String name = sname.get_slicec('/', 2);
104
105 if (type == "icons") {
106
107 if (!has_icon(name, node_type))
108 r_ret = Ref<Texture>();
109 else
110 r_ret = get_icon(name, node_type);
111 } else if (type == "styles") {
112
113 if (!has_stylebox(name, node_type))
114 r_ret = Ref<StyleBox>();
115 else
116 r_ret = get_stylebox(name, node_type);
117 } else if (type == "fonts") {
118
119 if (!has_font(name, node_type))
120 r_ret = Ref<Font>();
121 else
122 r_ret = get_font(name, node_type);
123 } else if (type == "colors") {
124
125 r_ret = get_color(name, node_type);
126 } else if (type == "constants") {
127
128 r_ret = get_constant(name, node_type);
129 } else
130 return false;
131
132 return true;
133 }
134
135 return false;
136 }
137
_get_property_list(List<PropertyInfo> * p_list) const138 void Theme::_get_property_list(List<PropertyInfo> *p_list) const {
139
140 List<PropertyInfo> list;
141
142 const StringName *key = NULL;
143
144 while ((key = icon_map.next(key))) {
145
146 const StringName *key2 = NULL;
147
148 while ((key2 = icon_map[*key].next(key2))) {
149
150 list.push_back(PropertyInfo(Variant::OBJECT, String() + *key + "/icons/" + *key2, PROPERTY_HINT_RESOURCE_TYPE, "Texture", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL));
151 }
152 }
153
154 key = NULL;
155
156 while ((key = style_map.next(key))) {
157
158 const StringName *key2 = NULL;
159
160 while ((key2 = style_map[*key].next(key2))) {
161
162 list.push_back(PropertyInfo(Variant::OBJECT, String() + *key + "/styles/" + *key2, PROPERTY_HINT_RESOURCE_TYPE, "StyleBox", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL));
163 }
164 }
165
166 key = NULL;
167
168 while ((key = font_map.next(key))) {
169
170 const StringName *key2 = NULL;
171
172 while ((key2 = font_map[*key].next(key2))) {
173
174 list.push_back(PropertyInfo(Variant::OBJECT, String() + *key + "/fonts/" + *key2, PROPERTY_HINT_RESOURCE_TYPE, "Font", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL));
175 }
176 }
177
178 key = NULL;
179
180 while ((key = color_map.next(key))) {
181
182 const StringName *key2 = NULL;
183
184 while ((key2 = color_map[*key].next(key2))) {
185
186 list.push_back(PropertyInfo(Variant::COLOR, String() + *key + "/colors/" + *key2));
187 }
188 }
189
190 key = NULL;
191
192 while ((key = constant_map.next(key))) {
193
194 const StringName *key2 = NULL;
195
196 while ((key2 = constant_map[*key].next(key2))) {
197
198 list.push_back(PropertyInfo(Variant::INT, String() + *key + "/constants/" + *key2));
199 }
200 }
201
202 list.sort();
203 for (List<PropertyInfo>::Element *E = list.front(); E; E = E->next()) {
204 p_list->push_back(E->get());
205 }
206 }
207
get_default()208 Ref<Theme> Theme::get_default() {
209
210 return default_theme;
211 }
212
set_default_theme_font(const Ref<Font> & p_default_font)213 void Theme::set_default_theme_font(const Ref<Font> &p_default_font) {
214
215 if (default_theme_font == p_default_font)
216 return;
217
218 if (default_theme_font.is_valid()) {
219 _unref_font(default_theme_font);
220 }
221
222 default_theme_font = p_default_font;
223
224 if (default_theme_font.is_valid()) {
225 _ref_font(default_theme_font);
226 }
227
228 _change_notify();
229 emit_changed();
230 }
231
get_default_theme_font() const232 Ref<Font> Theme::get_default_theme_font() const {
233
234 return default_theme_font;
235 }
236
set_default(const Ref<Theme> & p_default)237 void Theme::set_default(const Ref<Theme> &p_default) {
238
239 default_theme = p_default;
240 }
241
242 Ref<Texture> Theme::default_icon;
243 Ref<StyleBox> Theme::default_style;
244 Ref<Font> Theme::default_font;
245
set_default_icon(const Ref<Texture> & p_icon)246 void Theme::set_default_icon(const Ref<Texture> &p_icon) {
247
248 default_icon = p_icon;
249 }
set_default_style(const Ref<StyleBox> & p_style)250 void Theme::set_default_style(const Ref<StyleBox> &p_style) {
251
252 default_style = p_style;
253 }
set_default_font(const Ref<Font> & p_font)254 void Theme::set_default_font(const Ref<Font> &p_font) {
255
256 default_font = p_font;
257 }
258
set_icon(const StringName & p_name,const StringName & p_type,const Ref<Texture> & p_icon)259 void Theme::set_icon(const StringName &p_name, const StringName &p_type, const Ref<Texture> &p_icon) {
260
261 // ERR_FAIL_COND(p_icon.is_null());
262
263 bool new_value = !icon_map.has(p_type) || !icon_map[p_type].has(p_name);
264
265 icon_map[p_type][p_name] = p_icon;
266
267 if (new_value) {
268 _change_notify();
269 emit_changed();
270 }
271 }
get_icon(const StringName & p_name,const StringName & p_type) const272 Ref<Texture> Theme::get_icon(const StringName &p_name, const StringName &p_type) const {
273
274 if (icon_map.has(p_type) && icon_map[p_type].has(p_name) && icon_map[p_type][p_name].is_valid()) {
275
276 return icon_map[p_type][p_name];
277 } else {
278 return default_icon;
279 }
280 }
281
has_icon(const StringName & p_name,const StringName & p_type) const282 bool Theme::has_icon(const StringName &p_name, const StringName &p_type) const {
283
284 return (icon_map.has(p_type) && icon_map[p_type].has(p_name) && icon_map[p_type][p_name].is_valid());
285 }
286
clear_icon(const StringName & p_name,const StringName & p_type)287 void Theme::clear_icon(const StringName &p_name, const StringName &p_type) {
288
289 ERR_FAIL_COND(!icon_map.has(p_type));
290 ERR_FAIL_COND(!icon_map[p_type].has(p_name));
291
292 icon_map[p_type].erase(p_name);
293 _change_notify();
294 emit_changed();
295 }
296
get_icon_list(StringName p_type,List<StringName> * p_list) const297 void Theme::get_icon_list(StringName p_type, List<StringName> *p_list) const {
298
299 if (!icon_map.has(p_type))
300 return;
301
302 const StringName *key = NULL;
303
304 while ((key = icon_map[p_type].next(key))) {
305
306 p_list->push_back(*key);
307 }
308 }
309
set_shader(const StringName & p_name,const StringName & p_type,const Ref<Shader> & p_shader)310 void Theme::set_shader(const StringName &p_name, const StringName &p_type, const Ref<Shader> &p_shader) {
311 bool new_value = !shader_map.has(p_type) || !shader_map[p_type].has(p_name);
312
313 shader_map[p_type][p_name] = p_shader;
314
315 if (new_value) {
316 _change_notify();
317 emit_changed();
318 }
319 }
320
get_shader(const StringName & p_name,const StringName & p_type) const321 Ref<Shader> Theme::get_shader(const StringName &p_name, const StringName &p_type) const {
322 if (shader_map.has(p_type) && shader_map[p_type].has(p_name) && shader_map[p_type][p_name].is_valid()) {
323 return shader_map[p_type][p_name];
324 } else {
325 return NULL;
326 }
327 }
328
has_shader(const StringName & p_name,const StringName & p_type) const329 bool Theme::has_shader(const StringName &p_name, const StringName &p_type) const {
330 return (shader_map.has(p_type) && shader_map[p_type].has(p_name) && shader_map[p_type][p_name].is_valid());
331 }
332
clear_shader(const StringName & p_name,const StringName & p_type)333 void Theme::clear_shader(const StringName &p_name, const StringName &p_type) {
334 ERR_FAIL_COND(!shader_map.has(p_type));
335 ERR_FAIL_COND(!shader_map[p_type].has(p_name));
336
337 shader_map[p_type].erase(p_name);
338 _change_notify();
339 emit_changed();
340 }
341
get_shader_list(const StringName & p_type,List<StringName> * p_list) const342 void Theme::get_shader_list(const StringName &p_type, List<StringName> *p_list) const {
343 if (!shader_map.has(p_type))
344 return;
345
346 const StringName *key = NULL;
347
348 while ((key = shader_map[p_type].next(key))) {
349
350 p_list->push_back(*key);
351 }
352 }
353
set_stylebox(const StringName & p_name,const StringName & p_type,const Ref<StyleBox> & p_style)354 void Theme::set_stylebox(const StringName &p_name, const StringName &p_type, const Ref<StyleBox> &p_style) {
355
356 // ERR_FAIL_COND(p_style.is_null());
357
358 bool new_value = !style_map.has(p_type) || !style_map[p_type].has(p_name);
359
360 style_map[p_type][p_name] = p_style;
361
362 if (new_value)
363 _change_notify();
364 emit_changed();
365 }
366
get_stylebox(const StringName & p_name,const StringName & p_type) const367 Ref<StyleBox> Theme::get_stylebox(const StringName &p_name, const StringName &p_type) const {
368
369 if (style_map.has(p_type) && style_map[p_type].has(p_name) && style_map[p_type][p_name].is_valid()) {
370
371 return style_map[p_type][p_name];
372 } else {
373 return default_style;
374 }
375 }
376
has_stylebox(const StringName & p_name,const StringName & p_type) const377 bool Theme::has_stylebox(const StringName &p_name, const StringName &p_type) const {
378
379 return (style_map.has(p_type) && style_map[p_type].has(p_name) && style_map[p_type][p_name].is_valid());
380 }
381
clear_stylebox(const StringName & p_name,const StringName & p_type)382 void Theme::clear_stylebox(const StringName &p_name, const StringName &p_type) {
383
384 ERR_FAIL_COND(!style_map.has(p_type));
385 ERR_FAIL_COND(!style_map[p_type].has(p_name));
386
387 style_map[p_type].erase(p_name);
388 _change_notify();
389 emit_changed();
390 }
391
get_stylebox_list(StringName p_type,List<StringName> * p_list) const392 void Theme::get_stylebox_list(StringName p_type, List<StringName> *p_list) const {
393
394 if (!style_map.has(p_type))
395 return;
396
397 const StringName *key = NULL;
398
399 while ((key = style_map[p_type].next(key))) {
400
401 p_list->push_back(*key);
402 }
403 }
404
get_stylebox_types(List<StringName> * p_list) const405 void Theme::get_stylebox_types(List<StringName> *p_list) const {
406 const StringName *key = NULL;
407 while ((key = style_map.next(key))) {
408 p_list->push_back(*key);
409 }
410 }
411
set_font(const StringName & p_name,const StringName & p_type,const Ref<Font> & p_font)412 void Theme::set_font(const StringName &p_name, const StringName &p_type, const Ref<Font> &p_font) {
413
414 // ERR_FAIL_COND(p_font.is_null());
415
416 bool new_value = !font_map.has(p_type) || !font_map[p_type].has(p_name);
417
418 if (!new_value) {
419 if (font_map[p_type][p_name].is_valid()) {
420 _unref_font(font_map[p_type][p_name]);
421 }
422 }
423 font_map[p_type][p_name] = p_font;
424
425 if (p_font.is_valid()) {
426 _ref_font(p_font);
427 }
428
429 if (new_value) {
430 _change_notify();
431 emit_changed();
432 }
433 }
get_font(const StringName & p_name,const StringName & p_type) const434 Ref<Font> Theme::get_font(const StringName &p_name, const StringName &p_type) const {
435
436 if (font_map.has(p_type) && font_map[p_type].has(p_name) && font_map[p_type][p_name].is_valid())
437 return font_map[p_type][p_name];
438 else if (default_theme_font.is_valid())
439 return default_theme_font;
440 else
441 return default_font;
442 }
443
has_font(const StringName & p_name,const StringName & p_type) const444 bool Theme::has_font(const StringName &p_name, const StringName &p_type) const {
445
446 return (font_map.has(p_type) && font_map[p_type].has(p_name) && font_map[p_type][p_name].is_valid());
447 }
448
clear_font(const StringName & p_name,const StringName & p_type)449 void Theme::clear_font(const StringName &p_name, const StringName &p_type) {
450
451 ERR_FAIL_COND(!font_map.has(p_type));
452 ERR_FAIL_COND(!font_map[p_type].has(p_name));
453
454 if (font_map.has(p_type) && font_map[p_type].has(p_name) && font_map[p_type][p_name].is_valid()) {
455 _unref_font(font_map[p_type][p_name]);
456 }
457
458 font_map[p_type].erase(p_name);
459 _change_notify();
460 emit_changed();
461 }
462
get_font_list(StringName p_type,List<StringName> * p_list) const463 void Theme::get_font_list(StringName p_type, List<StringName> *p_list) const {
464
465 if (!font_map.has(p_type))
466 return;
467
468 const StringName *key = NULL;
469
470 while ((key = font_map[p_type].next(key))) {
471
472 p_list->push_back(*key);
473 }
474 }
475
set_color(const StringName & p_name,const StringName & p_type,const Color & p_color)476 void Theme::set_color(const StringName &p_name, const StringName &p_type, const Color &p_color) {
477
478 bool new_value = !color_map.has(p_type) || !color_map[p_type].has(p_name);
479
480 color_map[p_type][p_name] = p_color;
481
482 if (new_value) {
483 _change_notify();
484 emit_changed();
485 }
486 }
487
get_color(const StringName & p_name,const StringName & p_type) const488 Color Theme::get_color(const StringName &p_name, const StringName &p_type) const {
489
490 if (color_map.has(p_type) && color_map[p_type].has(p_name))
491 return color_map[p_type][p_name];
492 else
493 return Color();
494 }
495
has_color(const StringName & p_name,const StringName & p_type) const496 bool Theme::has_color(const StringName &p_name, const StringName &p_type) const {
497
498 return (color_map.has(p_type) && color_map[p_type].has(p_name));
499 }
500
clear_color(const StringName & p_name,const StringName & p_type)501 void Theme::clear_color(const StringName &p_name, const StringName &p_type) {
502
503 ERR_FAIL_COND(!color_map.has(p_type));
504 ERR_FAIL_COND(!color_map[p_type].has(p_name));
505
506 color_map[p_type].erase(p_name);
507 _change_notify();
508 emit_changed();
509 }
510
get_color_list(StringName p_type,List<StringName> * p_list) const511 void Theme::get_color_list(StringName p_type, List<StringName> *p_list) const {
512
513 if (!color_map.has(p_type))
514 return;
515
516 const StringName *key = NULL;
517
518 while ((key = color_map[p_type].next(key))) {
519
520 p_list->push_back(*key);
521 }
522 }
523
set_constant(const StringName & p_name,const StringName & p_type,int p_constant)524 void Theme::set_constant(const StringName &p_name, const StringName &p_type, int p_constant) {
525
526 bool new_value = !constant_map.has(p_type) || !constant_map[p_type].has(p_name);
527 constant_map[p_type][p_name] = p_constant;
528
529 if (new_value) {
530 _change_notify();
531 emit_changed();
532 }
533 }
534
get_constant(const StringName & p_name,const StringName & p_type) const535 int Theme::get_constant(const StringName &p_name, const StringName &p_type) const {
536
537 if (constant_map.has(p_type) && constant_map[p_type].has(p_name))
538 return constant_map[p_type][p_name];
539 else {
540 return 0;
541 }
542 }
543
has_constant(const StringName & p_name,const StringName & p_type) const544 bool Theme::has_constant(const StringName &p_name, const StringName &p_type) const {
545
546 return (constant_map.has(p_type) && constant_map[p_type].has(p_name));
547 }
548
clear_constant(const StringName & p_name,const StringName & p_type)549 void Theme::clear_constant(const StringName &p_name, const StringName &p_type) {
550
551 ERR_FAIL_COND(!constant_map.has(p_type));
552 ERR_FAIL_COND(!constant_map[p_type].has(p_name));
553
554 constant_map[p_type].erase(p_name);
555 _change_notify();
556 emit_changed();
557 }
558
get_constant_list(StringName p_type,List<StringName> * p_list) const559 void Theme::get_constant_list(StringName p_type, List<StringName> *p_list) const {
560
561 if (!constant_map.has(p_type))
562 return;
563
564 const StringName *key = NULL;
565
566 while ((key = constant_map[p_type].next(key))) {
567
568 p_list->push_back(*key);
569 }
570 }
571
copy_default_theme()572 void Theme::copy_default_theme() {
573
574 Ref<Theme> default_theme = get_default();
575
576 icon_map = default_theme->icon_map;
577 style_map = default_theme->style_map;
578 font_map = default_theme->font_map;
579 color_map = default_theme->color_map;
580 constant_map = default_theme->constant_map;
581 _change_notify();
582 emit_changed();
583 }
584
get_type_list(List<StringName> * p_list) const585 void Theme::get_type_list(List<StringName> *p_list) const {
586
587 Set<StringName> types;
588
589 const StringName *key = NULL;
590
591 while ((key = icon_map.next(key))) {
592
593 types.insert(*key);
594 }
595
596 key = NULL;
597
598 while ((key = style_map.next(key))) {
599
600 types.insert(*key);
601 }
602
603 key = NULL;
604
605 while ((key = font_map.next(key))) {
606
607 types.insert(*key);
608 }
609
610 key = NULL;
611
612 while ((key = color_map.next(key))) {
613
614 types.insert(*key);
615 }
616
617 key = NULL;
618
619 while ((key = constant_map.next(key))) {
620
621 types.insert(*key);
622 }
623
624 for (Set<StringName>::Element *E = types.front(); E; E = E->next()) {
625
626 p_list->push_back(E->get());
627 }
628 }
629
_bind_methods()630 void Theme::_bind_methods() {
631
632 ObjectTypeDB::bind_method(_MD("set_icon", "name", "type", "texture:Texture"), &Theme::set_icon);
633 ObjectTypeDB::bind_method(_MD("get_icon:Texture", "name", "type"), &Theme::get_icon);
634 ObjectTypeDB::bind_method(_MD("has_icon", "name", "type"), &Theme::has_icon);
635 ObjectTypeDB::bind_method(_MD("clear_icon", "name", "type"), &Theme::clear_icon);
636 ObjectTypeDB::bind_method(_MD("get_icon_list", "type"), &Theme::_get_icon_list);
637
638 ObjectTypeDB::bind_method(_MD("set_stylebox", "name", "type", "texture:StyleBox"), &Theme::set_stylebox);
639 ObjectTypeDB::bind_method(_MD("get_stylebox:StyleBox", "name", "type"), &Theme::get_stylebox);
640 ObjectTypeDB::bind_method(_MD("has_stylebox", "name", "type"), &Theme::has_stylebox);
641 ObjectTypeDB::bind_method(_MD("clear_stylebox", "name", "type"), &Theme::clear_stylebox);
642 ObjectTypeDB::bind_method(_MD("get_stylebox_list", "type"), &Theme::_get_stylebox_list);
643 ObjectTypeDB::bind_method(_MD("get_stylebox_types"), &Theme::_get_stylebox_types);
644
645 ObjectTypeDB::bind_method(_MD("set_font", "name", "type", "font:Font"), &Theme::set_font);
646 ObjectTypeDB::bind_method(_MD("get_font:Font", "name", "type"), &Theme::get_font);
647 ObjectTypeDB::bind_method(_MD("has_font", "name", "type"), &Theme::has_font);
648 ObjectTypeDB::bind_method(_MD("clear_font", "name", "type"), &Theme::clear_font);
649 ObjectTypeDB::bind_method(_MD("get_font_list", "type"), &Theme::_get_font_list);
650
651 ObjectTypeDB::bind_method(_MD("set_color", "name", "type", "color"), &Theme::set_color);
652 ObjectTypeDB::bind_method(_MD("get_color", "name", "type"), &Theme::get_color);
653 ObjectTypeDB::bind_method(_MD("has_color", "name", "type"), &Theme::has_color);
654 ObjectTypeDB::bind_method(_MD("clear_color", "name", "type"), &Theme::clear_color);
655 ObjectTypeDB::bind_method(_MD("get_color_list", "type"), &Theme::_get_color_list);
656
657 ObjectTypeDB::bind_method(_MD("set_constant", "name", "type", "constant"), &Theme::set_constant);
658 ObjectTypeDB::bind_method(_MD("get_constant", "name", "type"), &Theme::get_constant);
659 ObjectTypeDB::bind_method(_MD("has_constant", "name", "type"), &Theme::has_constant);
660 ObjectTypeDB::bind_method(_MD("clear_constant", "name", "type"), &Theme::clear_constant);
661 ObjectTypeDB::bind_method(_MD("get_constant_list", "type"), &Theme::_get_constant_list);
662
663 ObjectTypeDB::bind_method(_MD("set_default_font", "font"), &Theme::set_default_theme_font);
664 ObjectTypeDB::bind_method(_MD("get_default_font"), &Theme::get_default_theme_font);
665
666 ObjectTypeDB::bind_method(_MD("get_type_list", "type"), &Theme::_get_type_list);
667
668 ObjectTypeDB::bind_method(_MD("_emit_theme_changed"), &Theme::_emit_theme_changed);
669
670 ObjectTypeDB::bind_method("copy_default_theme", &Theme::copy_default_theme);
671
672 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "default_font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), _SCS("set_default_font"), _SCS("get_default_font"));
673 }
674
Theme()675 Theme::Theme() {
676 }
677
~Theme()678 Theme::~Theme() {
679 }
680
load(const String & p_path,const String & p_original_path,Error * r_error)681 RES ResourceFormatLoaderTheme::load(const String &p_path, const String &p_original_path, Error *r_error) {
682 if (r_error)
683 *r_error = ERR_CANT_OPEN;
684
685 Error err;
686 FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err);
687
688 ERR_EXPLAIN("Unable to open theme file: " + p_path);
689 ERR_FAIL_COND_V(err, RES());
690 String base_path = p_path.get_base_dir();
691 Ref<Theme> theme(memnew(Theme));
692 Map<StringName, Variant> library;
693 if (r_error)
694 *r_error = ERR_FILE_CORRUPT;
695
696 bool reading_library = false;
697 int line = 0;
698
699 while (!f->eof_reached()) {
700
701 String l = f->get_line().strip_edges();
702 line++;
703
704 int comment = l.find(";");
705 if (comment != -1)
706 l = l.substr(0, comment);
707 if (l == "")
708 continue;
709
710 if (l.begins_with("[")) {
711 if (l == "[library]") {
712 reading_library = true;
713 } else if (l == "[theme]") {
714 reading_library = false;
715 } else {
716 memdelete(f);
717 ERR_EXPLAIN(p_path + ":" + itos(line) + ": Unknown section type: '" + l + "'.");
718 ERR_FAIL_V(RES());
719 }
720 continue;
721 }
722
723 int eqpos = l.find("=");
724 if (eqpos == -1) {
725 memdelete(f);
726 ERR_EXPLAIN(p_path + ":" + itos(line) + ": Expected '='.");
727 ERR_FAIL_V(RES());
728 }
729
730 String right = l.substr(eqpos + 1, l.length()).strip_edges();
731 if (right == "") {
732 memdelete(f);
733 ERR_EXPLAIN(p_path + ":" + itos(line) + ": Expected value after '='.");
734 ERR_FAIL_V(RES());
735 }
736
737 Variant value;
738
739 if (right.is_valid_integer()) {
740 //is number
741 value = right.to_int();
742 } else if (right.is_valid_html_color()) {
743 //is html color
744 value = Color::html(right);
745 } else if (right.begins_with("@")) { //reference
746
747 String reference = right.substr(1, right.length());
748 if (!library.has(reference)) {
749 memdelete(f);
750 ERR_EXPLAIN(p_path + ":" + itos(line) + ": Invalid reference to '" + reference + "'.");
751 ERR_FAIL_V(RES());
752 }
753
754 value = library[reference];
755
756 } else if (right.begins_with("default")) { //use default
757 //do none
758 } else {
759 //attempt to parse a constructor
760 int popenpos = right.find("(");
761
762 if (popenpos == -1) {
763 memdelete(f);
764 ERR_EXPLAIN(p_path + ":" + itos(line) + ": Invalid constructor syntax: " + right);
765 ERR_FAIL_V(RES());
766 }
767
768 int pclosepos = right.find_last(")");
769
770 if (pclosepos == -1) {
771 ERR_EXPLAIN(p_path + ":" + itos(line) + ": Invalid constructor parameter syntax: " + right);
772 ERR_FAIL_V(RES());
773 }
774
775 String type = right.substr(0, popenpos);
776 String param = right.substr(popenpos + 1, pclosepos - popenpos - 1);
777
778 if (type == "icon") {
779
780 String path;
781
782 if (param.is_abs_path())
783 path = param;
784 else
785 path = base_path + "/" + param;
786
787 Ref<Texture> texture = ResourceLoader::load(path);
788 if (!texture.is_valid()) {
789 memdelete(f);
790 ERR_EXPLAIN(p_path + ":" + itos(line) + ": Couldn't find icon at path: " + path);
791 ERR_FAIL_V(RES());
792 }
793
794 value = texture;
795
796 } else if (type == "sbox") {
797
798 String path;
799
800 if (param.is_abs_path())
801 path = param;
802 else
803 path = base_path + "/" + param;
804
805 Ref<StyleBox> stylebox = ResourceLoader::load(path);
806 if (!stylebox.is_valid()) {
807 memdelete(f);
808 ERR_EXPLAIN(p_path + ":" + itos(line) + ": Couldn't find stylebox at path: " + path);
809 ERR_FAIL_V(RES());
810 }
811
812 value = stylebox;
813
814 } else if (type == "sboxt") {
815
816 Vector<String> params = param.split(",");
817 if (params.size() != 5 && params.size() != 9) {
818 memdelete(f);
819 ERR_EXPLAIN(p_path + ":" + itos(line) + ": Invalid param count for sboxt(): '" + right + "'.");
820 ERR_FAIL_V(RES());
821 }
822
823 String path = params[0];
824
825 if (!param.is_abs_path())
826 path = base_path + "/" + path;
827
828 Ref<Texture> tex = ResourceLoader::load(path);
829 if (tex.is_null()) {
830 memdelete(f);
831 ERR_EXPLAIN(p_path + ":" + itos(line) + ": Could not open texture for sboxt at path: '" + params[0] + "'.");
832 ERR_FAIL_V(RES());
833 }
834
835 Ref<StyleBoxTexture> sbtex(memnew(StyleBoxTexture));
836
837 sbtex->set_texture(tex);
838
839 for (int i = 0; i < 4; i++) {
840 if (!params[i + 1].is_valid_integer()) {
841
842 memdelete(f);
843 ERR_EXPLAIN(p_path + ":" + itos(line) + ": Invalid expand margin parameter for sboxt #" + itos(i + 1) + ", expected integer constant, got: '" + params[i + 1] + "'.");
844 ERR_FAIL_V(RES());
845 }
846
847 int margin = params[i + 1].to_int();
848 sbtex->set_expand_margin_size(Margin(i), margin);
849 }
850
851 if (params.size() == 9) {
852
853 for (int i = 0; i < 4; i++) {
854
855 if (!params[i + 5].is_valid_integer()) {
856 memdelete(f);
857 ERR_EXPLAIN(p_path + ":" + itos(line) + ": Invalid expand margin parameter for sboxt #" + itos(i + 5) + ", expected integer constant, got: '" + params[i + 5] + "'.");
858 ERR_FAIL_V(RES());
859 }
860
861 int margin = params[i + 5].to_int();
862 sbtex->set_margin_size(Margin(i), margin);
863 }
864 }
865
866 value = sbtex;
867 } else if (type == "sboxf") {
868
869 Vector<String> params = param.split(",");
870 if (params.size() < 2) {
871
872 memdelete(f);
873 ERR_EXPLAIN(p_path + ":" + itos(line) + ": Invalid param count for sboxf(): '" + right + "'.");
874 ERR_FAIL_V(RES());
875 }
876
877 Ref<StyleBoxFlat> sbflat(memnew(StyleBoxFlat));
878
879 if (!params[0].is_valid_integer()) {
880
881 memdelete(f);
882 ERR_EXPLAIN(p_path + ":" + itos(line) + ": Expected integer numeric constant for parameter 0 (border size).");
883 ERR_FAIL_V(RES());
884 }
885
886 sbflat->set_border_size(params[0].to_int());
887
888 if (!params[0].is_valid_integer()) {
889
890 memdelete(f);
891 ERR_EXPLAIN(p_path + ":" + itos(line) + ": Expected integer numeric constant for parameter 0 (border size).");
892 ERR_FAIL_V(RES());
893 }
894
895 int left = MIN(params.size() - 1, 3);
896
897 int ccodes = 0;
898
899 for (int i = 0; i < left; i++) {
900
901 if (params[i + 1].is_valid_html_color())
902 ccodes++;
903 else
904 break;
905 }
906
907 Color normal;
908 Color bright;
909 Color dark;
910
911 if (ccodes < 1) {
912 memdelete(f);
913 ERR_EXPLAIN(p_path + ":" + itos(line) + ": Expected at least 1, 2 or 3 html color codes.");
914 ERR_FAIL_V(RES());
915 } else if (ccodes == 1) {
916
917 normal = Color::html(params[1]);
918 bright = Color::html(params[1]);
919 dark = Color::html(params[1]);
920 } else if (ccodes == 2) {
921
922 normal = Color::html(params[1]);
923 bright = Color::html(params[2]);
924 dark = Color::html(params[2]);
925 } else {
926
927 normal = Color::html(params[1]);
928 bright = Color::html(params[2]);
929 dark = Color::html(params[3]);
930 }
931
932 sbflat->set_dark_color(dark);
933 sbflat->set_light_color(bright);
934 sbflat->set_bg_color(normal);
935
936 if (params.size() == ccodes + 5) {
937 //margins
938 for (int i = 0; i < 4; i++) {
939
940 if (!params[i + ccodes + 1].is_valid_integer()) {
941 memdelete(f);
942 ERR_EXPLAIN(p_path + ":" + itos(line) + ": Invalid expand margin parameter for sboxf #" + itos(i + ccodes + 1) + ", expected integer constant, got: '" + params[i + ccodes + 1] + "'.");
943 ERR_FAIL_V(RES());
944 }
945
946 // int margin = params[i+ccodes+1].to_int();
947 //sbflat->set_margin_size(Margin(i),margin);
948 }
949 } else if (params.size() != ccodes + 1) {
950 memdelete(f);
951 ERR_EXPLAIN(p_path + ":" + itos(line) + ": Invalid amount of margin parameters for sboxt.");
952 ERR_FAIL_V(RES());
953 }
954
955 value = sbflat;
956
957 } else {
958 memdelete(f);
959 ERR_EXPLAIN(p_path + ":" + itos(line) + ": Invalid constructor type: '" + type + "'.");
960 ERR_FAIL_V(RES());
961 }
962 }
963
964 //parse left and do something with it
965 String left = l.substr(0, eqpos);
966
967 if (reading_library) {
968
969 left = left.strip_edges();
970 if (!left.is_valid_identifier()) {
971 memdelete(f);
972 ERR_EXPLAIN(p_path + ":" + itos(line) + ": <LibraryItem> is not a valid identifier.");
973 ERR_FAIL_V(RES());
974 }
975 if (library.has(left)) {
976 memdelete(f);
977 ERR_EXPLAIN(p_path + ":" + itos(line) + ": Already in library: '" + left + "'.");
978 ERR_FAIL_V(RES());
979 }
980
981 library[left] = value;
982 } else {
983
984 int pointpos = left.find(".");
985 if (pointpos == -1) {
986 memdelete(f);
987 ERR_EXPLAIN(p_path + ":" + itos(line) + ": Expected 'control.item=..' assign syntax.");
988 ERR_FAIL_V(RES());
989 }
990
991 String control = left.substr(0, pointpos).strip_edges();
992 if (!control.is_valid_identifier()) {
993 memdelete(f);
994 ERR_EXPLAIN(p_path + ":" + itos(line) + ": <Control> is not a valid identifier.");
995 ERR_FAIL_V(RES());
996 }
997 String item = left.substr(pointpos + 1, left.size()).strip_edges();
998 if (!item.is_valid_identifier()) {
999 memdelete(f);
1000 ERR_EXPLAIN(p_path + ":" + itos(line) + ": <Item> is not a valid identifier.");
1001 ERR_FAIL_V(RES());
1002 }
1003
1004 if (value.get_type() == Variant::NIL) {
1005 //try to use exiting
1006 if (Theme::get_default()->has_stylebox(item, control))
1007 value = Theme::get_default()->get_stylebox(item, control);
1008 else if (Theme::get_default()->has_font(item, control))
1009 value = Theme::get_default()->get_font(item, control);
1010 else if (Theme::get_default()->has_icon(item, control))
1011 value = Theme::get_default()->get_icon(item, control);
1012 else if (Theme::get_default()->has_color(item, control))
1013 value = Theme::get_default()->get_color(item, control);
1014 else if (Theme::get_default()->has_constant(item, control))
1015 value = Theme::get_default()->get_constant(item, control);
1016 else {
1017 memdelete(f);
1018 ERR_EXPLAIN(p_path + ":" + itos(line) + ": Default not present for: '" + control + "." + item + "'.");
1019 ERR_FAIL_V(RES());
1020 }
1021 }
1022
1023 if (value.get_type() == Variant::OBJECT) {
1024
1025 Ref<Resource> res = value;
1026 if (!res.is_valid()) {
1027
1028 memdelete(f);
1029 ERR_EXPLAIN(p_path + ":" + itos(line) + ": Invalid resource (NULL).");
1030 ERR_FAIL_V(RES());
1031 }
1032
1033 if (res->cast_to<StyleBox>()) {
1034
1035 theme->set_stylebox(item, control, res);
1036 } else if (res->cast_to<Font>()) {
1037 theme->set_font(item, control, res);
1038 } else if (res->cast_to<Font>()) {
1039 theme->set_font(item, control, res);
1040 } else if (res->cast_to<Texture>()) {
1041 theme->set_icon(item, control, res);
1042 } else {
1043 memdelete(f);
1044 ERR_EXPLAIN(p_path + ":" + itos(line) + ": Invalid resource type.");
1045 ERR_FAIL_V(RES());
1046 }
1047 } else if (value.get_type() == Variant::COLOR) {
1048
1049 theme->set_color(item, control, value);
1050
1051 } else if (value.get_type() == Variant::INT) {
1052
1053 theme->set_constant(item, control, value);
1054
1055 } else {
1056
1057 memdelete(f);
1058 ERR_EXPLAIN(p_path + ":" + itos(line) + ": Couldn't even determine what this setting is! what did you do!?");
1059 ERR_FAIL_V(RES());
1060 }
1061 }
1062 }
1063
1064 f->close();
1065 memdelete(f);
1066
1067 if (r_error)
1068 *r_error = OK;
1069
1070 return theme;
1071 }
1072
get_recognized_extensions(List<String> * p_extensions) const1073 void ResourceFormatLoaderTheme::get_recognized_extensions(List<String> *p_extensions) const {
1074
1075 p_extensions->push_back("theme");
1076 }
1077
handles_type(const String & p_type) const1078 bool ResourceFormatLoaderTheme::handles_type(const String &p_type) const {
1079
1080 return p_type == "Theme";
1081 }
1082
get_resource_type(const String & p_path) const1083 String ResourceFormatLoaderTheme::get_resource_type(const String &p_path) const {
1084
1085 if (p_path.extension().to_lower() == "theme")
1086 return "Theme";
1087 return "";
1088 }
1089