Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/editor/themes/editor_theme_manager.cpp
20850 views
1
/**************************************************************************/
2
/* editor_theme_manager.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 "editor_theme_manager.h"
32
33
#include "core/error/error_macros.h"
34
#include "core/io/resource_loader.h"
35
#include "editor/editor_string_names.h"
36
#include "editor/file_system/editor_paths.h"
37
#include "editor/settings/editor_settings.h"
38
#include "editor/themes/editor_color_map.h"
39
#include "editor/themes/editor_fonts.h"
40
#include "editor/themes/editor_icons.h"
41
#include "editor/themes/editor_scale.h"
42
#include "editor/themes/editor_theme.h"
43
#include "editor/themes/theme_classic.h"
44
#include "editor/themes/theme_modern.h"
45
#include "scene/resources/style_box_flat.h"
46
#include "scene/resources/style_box_line.h"
47
#include "scene/resources/style_box_texture.h"
48
#include "scene/resources/texture.h"
49
#include "scene/scene_string_names.h"
50
#include "servers/display/display_server.h"
51
52
// Theme configuration.
53
54
uint32_t EditorThemeManager::ThemeConfiguration::hash() {
55
uint32_t hash = hash_murmur3_one_float(EDSCALE);
56
57
// Basic properties.
58
59
hash = hash_murmur3_one_32(style.hash(), hash);
60
hash = hash_murmur3_one_32(preset.hash(), hash);
61
hash = hash_murmur3_one_32(spacing_preset.hash(), hash);
62
63
hash = hash_murmur3_one_32(base_color.to_rgba32(), hash);
64
hash = hash_murmur3_one_32(accent_color.to_rgba32(), hash);
65
hash = hash_murmur3_one_float(contrast, hash);
66
hash = hash_murmur3_one_float(icon_saturation, hash);
67
68
// Extra properties.
69
70
hash = hash_murmur3_one_32(base_spacing, hash);
71
hash = hash_murmur3_one_32(extra_spacing, hash);
72
hash = hash_murmur3_one_32(border_width, hash);
73
hash = hash_murmur3_one_32(corner_radius, hash);
74
75
hash = hash_murmur3_one_32((int)draw_extra_borders, hash);
76
hash = hash_murmur3_one_float(relationship_line_opacity, hash);
77
hash = hash_murmur3_one_32(thumb_size, hash);
78
hash = hash_murmur3_one_32(class_icon_size, hash);
79
hash = hash_murmur3_one_32((int)enable_touch_optimizations, hash);
80
hash = hash_murmur3_one_float(gizmo_handle_scale, hash);
81
hash = hash_murmur3_one_32(inspector_property_height, hash);
82
hash = hash_murmur3_one_float(subresource_hue_tint, hash);
83
84
hash = hash_murmur3_one_float(default_contrast, hash);
85
86
// Generated properties.
87
88
hash = hash_murmur3_one_32((int)dark_theme, hash);
89
hash = hash_murmur3_one_32((int)dark_icon_and_font, hash);
90
91
return hash;
92
}
93
94
uint32_t EditorThemeManager::ThemeConfiguration::hash_fonts() {
95
uint32_t hash = hash_murmur3_one_float(EDSCALE);
96
97
// TODO: Implement the hash based on what editor_register_fonts() uses.
98
99
return hash;
100
}
101
102
uint32_t EditorThemeManager::ThemeConfiguration::hash_icons() {
103
uint32_t hash = hash_murmur3_one_float(EDSCALE);
104
105
hash = hash_murmur3_one_32(accent_color.to_rgba32(), hash);
106
hash = hash_murmur3_one_float(icon_saturation, hash);
107
108
hash = hash_murmur3_one_32(thumb_size, hash);
109
hash = hash_murmur3_one_float(gizmo_handle_scale, hash);
110
111
hash = hash_murmur3_one_32((int)dark_icon_and_font, hash);
112
113
return hash;
114
}
115
116
// Benchmarks.
117
118
int EditorThemeManager::benchmark_run = 0;
119
120
String EditorThemeManager::get_benchmark_key() {
121
if (benchmark_run == 0) {
122
return "EditorTheme (Startup)";
123
}
124
125
return vformat("EditorTheme (Run %d)", benchmark_run);
126
}
127
128
// Generation helper methods.
129
130
Ref<StyleBoxTexture> EditorThemeManager::make_stylebox(Ref<Texture2D> p_texture, float p_left, float p_top, float p_right, float p_bottom, float p_margin_left, float p_margin_top, float p_margin_right, float p_margin_bottom, bool p_draw_center) {
131
Ref<StyleBoxTexture> style(memnew(StyleBoxTexture));
132
style->set_texture(p_texture);
133
style->set_texture_margin_individual(p_left * EDSCALE, p_top * EDSCALE, p_right * EDSCALE, p_bottom * EDSCALE);
134
style->set_content_margin_individual((p_left + p_margin_left) * EDSCALE, (p_top + p_margin_top) * EDSCALE, (p_right + p_margin_right) * EDSCALE, (p_bottom + p_margin_bottom) * EDSCALE);
135
style->set_draw_center(p_draw_center);
136
return style;
137
}
138
139
Ref<StyleBoxEmpty> EditorThemeManager::make_empty_stylebox(float p_margin_left, float p_margin_top, float p_margin_right, float p_margin_bottom) {
140
Ref<StyleBoxEmpty> style(memnew(StyleBoxEmpty));
141
style->set_content_margin_individual(p_margin_left * EDSCALE, p_margin_top * EDSCALE, p_margin_right * EDSCALE, p_margin_bottom * EDSCALE);
142
return style;
143
}
144
145
Ref<StyleBoxFlat> EditorThemeManager::make_flat_stylebox(Color p_color, float p_margin_left, float p_margin_top, float p_margin_right, float p_margin_bottom, int p_corner_width) {
146
Ref<StyleBoxFlat> style(memnew(StyleBoxFlat));
147
style->set_bg_color(p_color);
148
// Adjust level of detail based on the corners' effective sizes.
149
style->set_corner_detail(Math::ceil(0.8 * p_corner_width * EDSCALE));
150
style->set_corner_radius_all(p_corner_width * EDSCALE);
151
style->set_content_margin_individual(p_margin_left * EDSCALE, p_margin_top * EDSCALE, p_margin_right * EDSCALE, p_margin_bottom * EDSCALE);
152
return style;
153
}
154
155
Ref<StyleBoxLine> EditorThemeManager::make_line_stylebox(Color p_color, int p_thickness, float p_grow_begin, float p_grow_end, bool p_vertical) {
156
Ref<StyleBoxLine> style(memnew(StyleBoxLine));
157
style->set_color(p_color);
158
style->set_grow_begin(p_grow_begin);
159
style->set_grow_end(p_grow_end);
160
style->set_thickness(p_thickness);
161
style->set_vertical(p_vertical);
162
return style;
163
}
164
165
// Theme generation and population routines.
166
167
Ref<EditorTheme> EditorThemeManager::_create_base_theme(const Ref<EditorTheme> &p_old_theme) {
168
OS::get_singleton()->benchmark_begin_measure(get_benchmark_key(), "Create Base Theme");
169
170
Ref<EditorTheme> theme = memnew(EditorTheme);
171
ThemeConfiguration config = _create_theme_config();
172
theme->set_generated_hash(config.hash());
173
theme->set_generated_fonts_hash(config.hash_fonts());
174
theme->set_generated_icons_hash(config.hash_icons());
175
176
print_verbose(vformat("EditorTheme: Generating new theme for the config '%d'.", theme->get_generated_hash()));
177
178
bool is_default_style = config.style == "Modern";
179
if (is_default_style) {
180
ThemeModern::populate_shared_styles(theme, config);
181
} else {
182
ThemeClassic::populate_shared_styles(theme, config);
183
}
184
185
// Register icons.
186
{
187
OS::get_singleton()->benchmark_begin_measure(get_benchmark_key(), "Register Icons");
188
189
// External functions, see editor_icons.cpp.
190
editor_configure_icons(config.dark_icon_and_font);
191
192
// If settings are comparable to the old theme, then just copy existing icons over.
193
// Otherwise, regenerate them.
194
bool keep_old_icons = (p_old_theme.is_valid() && theme->get_generated_icons_hash() == p_old_theme->get_generated_icons_hash());
195
if (keep_old_icons) {
196
print_verbose("EditorTheme: Can keep old icons, copying.");
197
editor_copy_icons(theme, p_old_theme);
198
} else {
199
print_verbose("EditorTheme: Generating new icons.");
200
editor_register_icons(theme, config.dark_icon_and_font, config.icon_saturation, config.thumb_size, config.gizmo_handle_scale);
201
}
202
203
OS::get_singleton()->benchmark_end_measure(get_benchmark_key(), "Register Icons");
204
}
205
206
// Register fonts.
207
{
208
OS::get_singleton()->benchmark_begin_measure(get_benchmark_key(), "Register Fonts");
209
210
// TODO: Check if existing font definitions from the old theme are usable and copy them.
211
212
// External function, see editor_fonts.cpp.
213
print_verbose("EditorTheme: Generating new fonts.");
214
editor_register_fonts(theme);
215
216
OS::get_singleton()->benchmark_end_measure(get_benchmark_key(), "Register Fonts");
217
}
218
219
// TODO: Check if existing style definitions from the old theme are usable and copy them.
220
221
print_verbose("EditorTheme: Generating new styles.");
222
223
if (is_default_style) {
224
ThemeModern::populate_standard_styles(theme, config);
225
ThemeModern::populate_editor_styles(theme, config);
226
} else {
227
ThemeClassic::populate_standard_styles(theme, config);
228
ThemeClassic::populate_editor_styles(theme, config);
229
}
230
231
_populate_text_editor_styles(theme, config);
232
_populate_visual_shader_styles(theme, config);
233
234
OS::get_singleton()->benchmark_end_measure(get_benchmark_key(), "Create Base Theme");
235
return theme;
236
}
237
238
EditorThemeManager::ThemeConfiguration EditorThemeManager::_create_theme_config() {
239
ThemeConfiguration config;
240
241
// Basic properties.
242
243
config.style = EDITOR_GET("interface/theme/style");
244
config.preset = EDITOR_GET("interface/theme/color_preset");
245
config.spacing_preset = EDITOR_GET("interface/theme/spacing_preset");
246
247
config.base_color = EDITOR_GET("interface/theme/base_color");
248
config.accent_color = EDITOR_GET("interface/theme/accent_color");
249
config.contrast = EDITOR_GET("interface/theme/contrast");
250
config.icon_saturation = EDITOR_GET("interface/theme/icon_saturation");
251
config.corner_radius = EDITOR_GET("interface/theme/corner_radius");
252
253
// Extra properties.
254
255
config.base_spacing = EDITOR_GET("interface/theme/base_spacing");
256
config.extra_spacing = EDITOR_GET("interface/theme/additional_spacing");
257
// Ensure borders are visible when using an editor scale below 100%.
258
config.border_width = CLAMP((int)EDITOR_GET("interface/theme/border_size"), 0, 2) * MAX(1, EDSCALE);
259
260
config.draw_extra_borders = EDITOR_GET("interface/theme/draw_extra_borders");
261
config.draw_relationship_lines = EDITOR_GET("interface/theme/draw_relationship_lines");
262
config.relationship_line_opacity = EDITOR_GET("interface/theme/relationship_line_opacity");
263
config.thumb_size = EDITOR_GET("filesystem/file_dialog/thumbnail_size");
264
config.class_icon_size = 16 * EDSCALE;
265
config.enable_touch_optimizations = EDITOR_GET("interface/touchscreen/enable_touch_optimizations");
266
config.gizmo_handle_scale = EDITOR_GET("interface/touchscreen/scale_gizmo_handles");
267
config.subresource_hue_tint = EDITOR_GET("docks/property_editor/subresource_hue_tint");
268
config.dragging_hover_wait_msec = (float)EDITOR_GET("interface/editor/dragging_hover_wait_seconds") * 1000;
269
270
// Handle theme style.
271
if (config.preset != "Custom") {
272
if (config.style == "Classic") {
273
config.draw_relationship_lines = RELATIONSHIP_ALL;
274
config.corner_radius = 3;
275
} else { // Default
276
config.draw_relationship_lines = config.default_relationship_lines;
277
config.corner_radius = config.default_corner_radius;
278
}
279
280
EditorSettings::get_singleton()->set_initial_value("interface/theme/draw_relationship_lines", config.draw_relationship_lines);
281
EditorSettings::get_singleton()->set_initial_value("interface/theme/corner_radius", config.corner_radius);
282
283
// Enforce values in case they were adjusted or overridden.
284
EditorSettings::get_singleton()->set_manually("interface/theme/draw_relationship_lines", config.draw_relationship_lines);
285
EditorSettings::get_singleton()->set_manually("interface/theme/corner_radius", config.corner_radius);
286
}
287
288
// Handle color preset.
289
{
290
const bool follow_system_theme = EDITOR_GET("interface/theme/follow_system_theme");
291
const bool use_system_accent_color = EDITOR_GET("interface/theme/use_system_accent_color");
292
DisplayServer *display_server = DisplayServer::get_singleton();
293
Color system_base_color = display_server->get_base_color();
294
Color system_accent_color = display_server->get_accent_color();
295
296
if (follow_system_theme) {
297
String dark_theme = "Default";
298
String light_theme = "Light";
299
300
config.preset = light_theme; // Assume light theme if we can't detect system theme attributes.
301
302
if (system_base_color == Color(0, 0, 0, 0)) {
303
if (display_server->is_dark_mode_supported() && display_server->is_dark_mode()) {
304
config.preset = dark_theme;
305
}
306
} else {
307
if (system_base_color.get_luminance() < 0.5) {
308
config.preset = dark_theme;
309
}
310
}
311
}
312
313
if (config.preset != "Custom") {
314
Color preset_accent_color;
315
Color preset_base_color;
316
float preset_contrast = config.default_contrast;
317
bool preset_draw_extra_borders = false;
318
float preset_icon_saturation = config.default_icon_saturation;
319
320
// A negative contrast rate looks better for light themes, since it better follows the natural order of UI "elevation".
321
const float light_contrast = -0.06;
322
323
// Please use alphabetical order if you're adding a new color preset here.
324
if (config.preset == "Black (OLED)") {
325
preset_accent_color = Color(0.45, 0.75, 1.0);
326
preset_base_color = Color(0, 0, 0);
327
// The contrast rate value is irrelevant on a fully black theme.
328
preset_contrast = 0.0;
329
preset_draw_extra_borders = true;
330
} else if (config.preset == "Breeze Dark") {
331
preset_accent_color = Color(0.239, 0.682, 0.914);
332
preset_base_color = Color(0.1255, 0.1373, 0.149);
333
} else if (config.preset == "Godot 2") {
334
preset_accent_color = Color(0.53, 0.67, 0.89);
335
preset_base_color = Color(0.24, 0.23, 0.27);
336
preset_icon_saturation = 1;
337
} else if (config.preset == "Godot 3") {
338
preset_accent_color = Color(0.44, 0.73, 0.98);
339
preset_base_color = Color(0.21, 0.24, 0.29);
340
preset_icon_saturation = 1;
341
} else if (config.preset == "Gray") {
342
preset_accent_color = Color(0.44, 0.73, 0.98);
343
preset_base_color = Color(0.24, 0.24, 0.24);
344
} else if (config.preset == "Light") {
345
preset_accent_color = Color(0.18, 0.50, 1.00);
346
preset_base_color = Color(0.9, 0.9, 0.9);
347
preset_contrast = light_contrast;
348
preset_icon_saturation = 1;
349
} else if (config.preset == "Solarized (Dark)") {
350
preset_accent_color = Color(0.15, 0.55, 0.82);
351
preset_base_color = Color(0.03, 0.21, 0.26);
352
preset_contrast = 0.23;
353
} else if (config.preset == "Solarized (Light)") {
354
preset_accent_color = Color(0.15, 0.55, 0.82);
355
preset_base_color = Color(0.89, 0.86, 0.79);
356
preset_contrast = light_contrast;
357
} else { // Default
358
preset_accent_color = Color(0.337, 0.62, 1.0);
359
preset_base_color = Color(0.161, 0.161, 0.161);
360
}
361
362
config.accent_color = preset_accent_color;
363
config.base_color = preset_base_color;
364
config.contrast = preset_contrast;
365
config.draw_extra_borders = preset_draw_extra_borders;
366
config.icon_saturation = preset_icon_saturation;
367
368
EditorSettings::get_singleton()->set_initial_value("interface/theme/accent_color", config.accent_color);
369
EditorSettings::get_singleton()->set_initial_value("interface/theme/base_color", config.base_color);
370
EditorSettings::get_singleton()->set_initial_value("interface/theme/contrast", config.contrast);
371
EditorSettings::get_singleton()->set_initial_value("interface/theme/draw_extra_borders", config.draw_extra_borders);
372
EditorSettings::get_singleton()->set_initial_value("interface/theme/icon_saturation", config.icon_saturation);
373
}
374
375
if (follow_system_theme && system_base_color != Color(0, 0, 0, 0)) {
376
config.base_color = system_base_color;
377
config.preset = "Custom";
378
}
379
380
if (use_system_accent_color && system_accent_color != Color(0, 0, 0, 0)) {
381
config.accent_color = system_accent_color;
382
config.preset = "Custom";
383
}
384
385
// Enforce values in case they were adjusted or overridden.
386
EditorSettings::get_singleton()->set_manually("interface/theme/color_preset", config.preset);
387
EditorSettings::get_singleton()->set_manually("interface/theme/accent_color", config.accent_color);
388
EditorSettings::get_singleton()->set_manually("interface/theme/base_color", config.base_color);
389
EditorSettings::get_singleton()->set_manually("interface/theme/contrast", config.contrast);
390
EditorSettings::get_singleton()->set_manually("interface/theme/draw_extra_borders", config.draw_extra_borders);
391
EditorSettings::get_singleton()->set_manually("interface/theme/icon_saturation", config.icon_saturation);
392
}
393
394
// Handle theme spacing preset.
395
{
396
if (config.spacing_preset != "Custom") {
397
int preset_base_spacing = 0;
398
int preset_extra_spacing = 0;
399
Size2 preset_dialogs_buttons_min_size;
400
401
if (config.spacing_preset == "Compact") {
402
preset_base_spacing = 2;
403
preset_extra_spacing = 2;
404
preset_dialogs_buttons_min_size = Size2(90, 26);
405
} else if (config.spacing_preset == "Spacious") {
406
preset_base_spacing = 6;
407
preset_extra_spacing = 2;
408
preset_dialogs_buttons_min_size = Size2(112, 36);
409
} else { // Default
410
preset_base_spacing = 4;
411
preset_extra_spacing = 0;
412
preset_dialogs_buttons_min_size = Size2(105, 34);
413
}
414
415
config.base_spacing = preset_base_spacing;
416
config.extra_spacing = preset_extra_spacing;
417
config.dialogs_buttons_min_size = preset_dialogs_buttons_min_size;
418
419
EditorSettings::get_singleton()->set_initial_value("interface/theme/base_spacing", config.base_spacing);
420
EditorSettings::get_singleton()->set_initial_value("interface/theme/additional_spacing", config.extra_spacing);
421
}
422
423
// Enforce values in case they were adjusted or overridden.
424
EditorSettings::get_singleton()->set_manually("interface/theme/spacing_preset", config.spacing_preset);
425
EditorSettings::get_singleton()->set_manually("interface/theme/base_spacing", config.base_spacing);
426
EditorSettings::get_singleton()->set_manually("interface/theme/additional_spacing", config.extra_spacing);
427
}
428
429
// Generated properties.
430
431
config.dark_theme = is_dark_theme();
432
config.dark_icon_and_font = is_dark_icon_and_font();
433
434
config.base_margin = config.base_spacing;
435
config.increased_margin = config.base_spacing + config.extra_spacing * 0.75;
436
config.separation_margin = (config.base_spacing + config.extra_spacing / 2) * EDSCALE;
437
config.popup_margin = config.base_margin * 2.4 * EDSCALE;
438
// Make sure content doesn't stick to window decorations; this can be fixed in future with layout changes.
439
config.window_border_margin = MAX(1, config.base_margin * EDSCALE);
440
config.top_bar_separation = MAX(1, config.base_margin * EDSCALE);
441
442
// Force the v_separation to be even so that the spacing on top and bottom is even.
443
// If the vsep is odd and cannot be split into 2 even groups (of pixels), then it will be lopsided.
444
// We add 2 to the vsep to give it some extra spacing which looks a bit more modern (see Windows, for example).
445
const int separation_base = config.increased_margin + 6;
446
config.forced_even_separation = separation_base + (separation_base % 2);
447
448
return config;
449
}
450
451
void _load_text_editor_theme() {
452
EditorSettings *settings = EditorSettings::get_singleton();
453
const String theme_name = settings->get_setting("text_editor/theme/color_theme");
454
455
ERR_FAIL_COND(EditorSettings::is_default_text_editor_theme(theme_name.get_file().to_lower()));
456
457
const String theme_path = EditorPaths::get_singleton()->get_text_editor_themes_dir().path_join(theme_name + ".tet");
458
459
Ref<ConfigFile> cf;
460
cf.instantiate();
461
Error err = cf->load(theme_path);
462
ERR_FAIL_COND_MSG(err != OK, vformat("Failed to load text editor theme file '%s': %s", theme_name, error_names[err]));
463
464
const PackedStringArray keys = cf->get_section_keys("color_theme");
465
466
for (const String &key : keys) {
467
const String setting_key = "text_editor/theme/highlighting/" + key;
468
// Don't load if it's not an actual setting, or if it isn't a color setting.
469
if (!settings->has_setting(setting_key) || !key.contains("color")) {
470
continue;
471
}
472
const String val = cf->get_value("color_theme", key);
473
// Make sure it is actually a color.
474
if (val.is_valid_html_color()) {
475
const Color color_value = Color::html(val);
476
// Change manually to prevent settings_changed spam.
477
settings->set_initial_value(setting_key, color_value);
478
settings->set_manually(setting_key, color_value);
479
}
480
}
481
// If it doesn't load a setting just use what is currently loaded.
482
}
483
484
void EditorThemeManager::_populate_text_editor_styles(const Ref<EditorTheme> &p_theme, ThemeConfiguration &p_config) {
485
const String text_editor_color_theme = EDITOR_GET("text_editor/theme/color_theme");
486
const bool is_default_theme = text_editor_color_theme == "Default";
487
const bool is_godot2_theme = text_editor_color_theme == "Godot 2";
488
const bool is_custom_theme = text_editor_color_theme == "Custom";
489
if (is_default_theme || is_godot2_theme || is_custom_theme) {
490
HashMap<StringName, Color> colors;
491
if (is_default_theme || is_custom_theme) {
492
// Adaptive colors for comments and elements with lower relevance.
493
const Color dim_color = Color(p_config.font_color, 0.5);
494
const float mono_value = p_config.mono_color.r;
495
const Color alpha1 = Color(mono_value, mono_value, mono_value, 0.07);
496
const Color alpha2 = Color(mono_value, mono_value, mono_value, 0.14);
497
const Color alpha3 = Color(mono_value, mono_value, mono_value, 0.27);
498
499
// Syntax highlight token colors.
500
colors["text_editor/theme/highlighting/symbol_color"] = p_config.dark_icon_and_font ? Color(0.67, 0.79, 1) : Color(0, 0, 0.61);
501
colors["text_editor/theme/highlighting/keyword_color"] = p_config.dark_icon_and_font ? Color(1.0, 0.44, 0.52) : Color(0.9, 0.135, 0.51);
502
colors["text_editor/theme/highlighting/control_flow_keyword_color"] = p_config.dark_icon_and_font ? Color(1.0, 0.55, 0.8) : Color(0.743, 0.12, 0.8);
503
colors["text_editor/theme/highlighting/base_type_color"] = p_config.dark_icon_and_font ? Color(0.26, 1.0, 0.76) : Color(0, 0.6, 0.2);
504
colors["text_editor/theme/highlighting/engine_type_color"] = p_config.dark_icon_and_font ? Color(0.56, 1, 0.86) : Color(0.11, 0.55, 0.4);
505
colors["text_editor/theme/highlighting/user_type_color"] = p_config.dark_icon_and_font ? Color(0.78, 1, 0.93) : Color(0.18, 0.45, 0.4);
506
colors["text_editor/theme/highlighting/comment_color"] = p_config.dark_icon_and_font ? dim_color : Color(0.08, 0.08, 0.08, 0.5);
507
colors["text_editor/theme/highlighting/doc_comment_color"] = p_config.dark_icon_and_font ? Color(0.6, 0.7, 0.8, 0.8) : Color(0.15, 0.15, 0.4, 0.7);
508
colors["text_editor/theme/highlighting/string_color"] = p_config.dark_icon_and_font ? Color(1, 0.93, 0.63) : Color(0.6, 0.42, 0);
509
colors["text_editor/theme/highlighting/string_placeholder_color"] = p_config.dark_icon_and_font ? Color(1, 0.75, 0.4) : Color(0.93, 0.6, 0.33);
510
511
// Use the brightest background color on a light theme (which generally uses a negative contrast rate).
512
colors["text_editor/theme/highlighting/background_color"] = p_config.base_color.lerp(Color(0, 0, 0), p_config.contrast * (p_config.dark_icon_and_font ? 1.2 : 1.8)).clamp();
513
colors["text_editor/theme/highlighting/completion_background_color"] = p_config.base_color.lerp(Color(0, 0, 0), p_config.contrast * 0.3).clamp();
514
colors["text_editor/theme/highlighting/completion_selected_color"] = alpha1;
515
colors["text_editor/theme/highlighting/completion_existing_color"] = alpha2;
516
// Same opacity as the scroll grabber editor icon.
517
colors["text_editor/theme/highlighting/completion_scroll_color"] = Color(mono_value, mono_value, mono_value, 0.29);
518
colors["text_editor/theme/highlighting/completion_scroll_hovered_color"] = Color(mono_value, mono_value, mono_value, 0.4);
519
colors["text_editor/theme/highlighting/completion_font_color"] = p_config.font_color;
520
colors["text_editor/theme/highlighting/text_color"] = p_config.font_color;
521
colors["text_editor/theme/highlighting/line_number_color"] = dim_color;
522
colors["text_editor/theme/highlighting/safe_line_number_color"] = p_config.dark_icon_and_font ? (dim_color * Color(1, 1.2, 1, 1.5)) : Color(0, 0.4, 0, 0.75);
523
colors["text_editor/theme/highlighting/caret_color"] = p_config.mono_color;
524
colors["text_editor/theme/highlighting/caret_background_color"] = p_config.mono_color.inverted();
525
colors["text_editor/theme/highlighting/text_selected_color"] = Color(0, 0, 0, 0);
526
colors["text_editor/theme/highlighting/selection_color"] = p_config.selection_color;
527
colors["text_editor/theme/highlighting/brace_mismatch_color"] = p_config.dark_icon_and_font ? p_config.error_color : Color(1, 0.08, 0, 1);
528
colors["text_editor/theme/highlighting/current_line_color"] = alpha1;
529
colors["text_editor/theme/highlighting/line_length_guideline_color"] = p_config.dark_icon_and_font ? p_config.base_color : p_config.dark_color_2;
530
colors["text_editor/theme/highlighting/word_highlighted_color"] = alpha1;
531
colors["text_editor/theme/highlighting/number_color"] = p_config.dark_icon_and_font ? Color(0.63, 1, 0.88) : Color(0, 0.55, 0.28, 1);
532
colors["text_editor/theme/highlighting/function_color"] = p_config.dark_icon_and_font ? Color(0.34, 0.7, 1.0) : Color(0, 0.225, 0.9, 1);
533
colors["text_editor/theme/highlighting/member_variable_color"] = p_config.dark_icon_and_font ? Color(0.34, 0.7, 1.0).lerp(p_config.mono_color, 0.6) : Color(0, 0.4, 0.68, 1);
534
colors["text_editor/theme/highlighting/mark_color"] = Color(p_config.error_color.r, p_config.error_color.g, p_config.error_color.b, 0.3);
535
colors["text_editor/theme/highlighting/warning_color"] = Color(p_config.warning_color.r, p_config.warning_color.g, p_config.warning_color.b, 0.15);
536
colors["text_editor/theme/highlighting/bookmark_color"] = Color(0.08, 0.49, 0.98);
537
colors["text_editor/theme/highlighting/breakpoint_color"] = p_config.dark_icon_and_font ? p_config.error_color : Color(1, 0.27, 0.2, 1);
538
colors["text_editor/theme/highlighting/executing_line_color"] = Color(0.98, 0.89, 0.27);
539
colors["text_editor/theme/highlighting/code_folding_color"] = alpha3;
540
colors["text_editor/theme/highlighting/folded_code_region_color"] = Color(0.68, 0.46, 0.77, 0.2);
541
colors["text_editor/theme/highlighting/search_result_color"] = alpha1;
542
colors["text_editor/theme/highlighting/search_result_border_color"] = p_config.dark_icon_and_font ? Color(0.41, 0.61, 0.91, 0.38) : Color(0, 0.4, 1, 0.38);
543
544
if (p_config.dark_icon_and_font) {
545
colors["text_editor/theme/highlighting/gdscript/function_definition_color"] = Color(0.4, 0.9, 1.0);
546
colors["text_editor/theme/highlighting/gdscript/global_function_color"] = Color(0.64, 0.64, 0.96);
547
colors["text_editor/theme/highlighting/gdscript/node_path_color"] = Color(0.72, 0.77, 0.49);
548
colors["text_editor/theme/highlighting/gdscript/node_reference_color"] = Color(0.39, 0.76, 0.35);
549
colors["text_editor/theme/highlighting/gdscript/annotation_color"] = Color(1.0, 0.7, 0.45);
550
colors["text_editor/theme/highlighting/gdscript/string_name_color"] = Color(1.0, 0.76, 0.65);
551
colors["text_editor/theme/highlighting/comment_markers/critical_color"] = Color(0.77, 0.35, 0.35);
552
colors["text_editor/theme/highlighting/comment_markers/warning_color"] = Color(0.72, 0.61, 0.48);
553
colors["text_editor/theme/highlighting/comment_markers/notice_color"] = Color(0.56, 0.67, 0.51);
554
} else {
555
colors["text_editor/theme/highlighting/gdscript/function_definition_color"] = Color(0, 0.6, 0.6);
556
colors["text_editor/theme/highlighting/gdscript/global_function_color"] = Color(0.36, 0.18, 0.72);
557
colors["text_editor/theme/highlighting/gdscript/node_path_color"] = Color(0.18, 0.55, 0);
558
colors["text_editor/theme/highlighting/gdscript/node_reference_color"] = Color(0.0, 0.5, 0);
559
colors["text_editor/theme/highlighting/gdscript/annotation_color"] = Color(0.8, 0.37, 0);
560
colors["text_editor/theme/highlighting/gdscript/string_name_color"] = Color(0.8, 0.56, 0.45);
561
colors["text_editor/theme/highlighting/comment_markers/critical_color"] = Color(0.8, 0.14, 0.14);
562
colors["text_editor/theme/highlighting/comment_markers/warning_color"] = Color(0.75, 0.39, 0.03);
563
colors["text_editor/theme/highlighting/comment_markers/notice_color"] = Color(0.24, 0.54, 0.09);
564
}
565
} else if (is_godot2_theme) {
566
colors = EditorSettings::get_godot2_text_editor_theme();
567
}
568
EditorSettings *settings = EditorSettings::get_singleton();
569
for (const KeyValue<StringName, Color> &setting : colors) {
570
settings->set_initial_value(setting.key, setting.value);
571
if (is_default_theme || is_godot2_theme) {
572
settings->set_manually(setting.key, setting.value);
573
}
574
}
575
} else {
576
// Custom user theme.
577
_load_text_editor_theme();
578
}
579
580
// Now theme is loaded, apply it to CodeEdit.
581
p_theme->set_font(SceneStringName(font), "CodeEdit", p_theme->get_font(SNAME("source"), EditorStringName(EditorFonts)));
582
p_theme->set_font_size(SceneStringName(font_size), "CodeEdit", p_theme->get_font_size(SNAME("source_size"), EditorStringName(EditorFonts)));
583
584
/* clang-format off */
585
p_theme->set_icon("tab", "CodeEdit", p_theme->get_icon(SNAME("GuiTab"), EditorStringName(EditorIcons)));
586
p_theme->set_icon("space", "CodeEdit", p_theme->get_icon(SNAME("GuiSpace"), EditorStringName(EditorIcons)));
587
p_theme->set_icon("folded", "CodeEdit", p_theme->get_icon(SNAME("CodeFoldedRightArrow"), EditorStringName(EditorIcons)));
588
p_theme->set_icon("can_fold", "CodeEdit", p_theme->get_icon(SNAME("CodeFoldDownArrow"), EditorStringName(EditorIcons)));
589
p_theme->set_icon("folded_code_region", "CodeEdit", p_theme->get_icon(SNAME("CodeRegionFoldedRightArrow"), EditorStringName(EditorIcons)));
590
p_theme->set_icon("can_fold_code_region", "CodeEdit", p_theme->get_icon(SNAME("CodeRegionFoldDownArrow"), EditorStringName(EditorIcons)));
591
p_theme->set_icon("executing_line", "CodeEdit", p_theme->get_icon(SNAME("TextEditorPlay"), EditorStringName(EditorIcons)));
592
p_theme->set_icon("breakpoint", "CodeEdit", p_theme->get_icon(SNAME("Breakpoint"), EditorStringName(EditorIcons)));
593
/* clang-format on */
594
595
p_theme->set_constant("line_spacing", "CodeEdit", EDITOR_GET("text_editor/appearance/whitespace/line_spacing"));
596
597
const Color background_color = EDITOR_GET("text_editor/theme/highlighting/background_color");
598
Ref<StyleBoxFlat> code_edit_stylebox = make_flat_stylebox(background_color, p_config.widget_margin.x, p_config.widget_margin.y, p_config.widget_margin.x, p_config.widget_margin.y, p_config.corner_radius);
599
p_theme->set_stylebox(CoreStringName(normal), "CodeEdit", code_edit_stylebox);
600
p_theme->set_stylebox("read_only", "CodeEdit", code_edit_stylebox);
601
p_theme->set_stylebox("focus", "CodeEdit", memnew(StyleBoxEmpty));
602
603
/* clang-format off */
604
p_theme->set_color("completion_background_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/completion_background_color"));
605
p_theme->set_color("completion_selected_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/completion_selected_color"));
606
p_theme->set_color("completion_existing_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/completion_existing_color"));
607
p_theme->set_color("completion_scroll_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/completion_scroll_color"));
608
p_theme->set_color("completion_scroll_hovered_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/completion_scroll_hovered_color"));
609
p_theme->set_color(SceneStringName(font_color), "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/text_color"));
610
p_theme->set_color("line_number_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/line_number_color"));
611
p_theme->set_color("caret_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/caret_color"));
612
p_theme->set_color("font_selected_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/text_selected_color"));
613
p_theme->set_color("selection_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/selection_color"));
614
p_theme->set_color("brace_mismatch_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/brace_mismatch_color"));
615
p_theme->set_color("current_line_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/current_line_color"));
616
p_theme->set_color("line_length_guideline_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/line_length_guideline_color"));
617
p_theme->set_color("word_highlighted_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/word_highlighted_color"));
618
p_theme->set_color("bookmark_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/bookmark_color"));
619
p_theme->set_color("breakpoint_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/breakpoint_color"));
620
p_theme->set_color("executing_line_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/executing_line_color"));
621
p_theme->set_color("code_folding_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/code_folding_color"));
622
p_theme->set_color("folded_code_region_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/folded_code_region_color"));
623
p_theme->set_color("search_result_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/search_result_color"));
624
p_theme->set_color("search_result_border_color", "CodeEdit", EDITOR_GET("text_editor/theme/highlighting/search_result_border_color"));
625
/* clang-format on */
626
}
627
628
void EditorThemeManager::_populate_visual_shader_styles(const Ref<EditorTheme> &p_theme, ThemeConfiguration &p_config) {
629
EditorSettings *ed_settings = EditorSettings::get_singleton();
630
String visual_shader_color_theme = ed_settings->get("editors/visual_editors/color_theme");
631
if (visual_shader_color_theme == "Default") {
632
// Connection type colors
633
ed_settings->set_initial_value("editors/visual_editors/connection_colors/scalar_color", Color(0.55, 0.55, 0.55), true);
634
ed_settings->set_initial_value("editors/visual_editors/connection_colors/vector2_color", Color(0.44, 0.43, 0.64), true);
635
ed_settings->set_initial_value("editors/visual_editors/connection_colors/vector3_color", Color(0.337, 0.314, 0.71), true);
636
ed_settings->set_initial_value("editors/visual_editors/connection_colors/vector4_color", Color(0.7, 0.65, 0.147), true);
637
ed_settings->set_initial_value("editors/visual_editors/connection_colors/boolean_color", Color(0.243, 0.612, 0.349), true);
638
ed_settings->set_initial_value("editors/visual_editors/connection_colors/transform_color", Color(0.71, 0.357, 0.64), true);
639
ed_settings->set_initial_value("editors/visual_editors/connection_colors/sampler_color", Color(0.659, 0.4, 0.137), true);
640
641
// Node category colors (used for the node headers)
642
ed_settings->set_initial_value("editors/visual_editors/category_colors/output_color", Color(0.26, 0.10, 0.15), true);
643
ed_settings->set_initial_value("editors/visual_editors/category_colors/color_color", Color(0.5, 0.5, 0.1), true);
644
ed_settings->set_initial_value("editors/visual_editors/category_colors/conditional_color", Color(0.208, 0.522, 0.298), true);
645
ed_settings->set_initial_value("editors/visual_editors/category_colors/input_color", Color(0.502, 0.2, 0.204), true);
646
ed_settings->set_initial_value("editors/visual_editors/category_colors/scalar_color", Color(0.1, 0.5, 0.6), true);
647
ed_settings->set_initial_value("editors/visual_editors/category_colors/textures_color", Color(0.5, 0.3, 0.1), true);
648
ed_settings->set_initial_value("editors/visual_editors/category_colors/transform_color", Color(0.5, 0.3, 0.5), true);
649
ed_settings->set_initial_value("editors/visual_editors/category_colors/utility_color", Color(0.2, 0.2, 0.2), true);
650
ed_settings->set_initial_value("editors/visual_editors/category_colors/vector_color", Color(0.2, 0.2, 0.5), true);
651
ed_settings->set_initial_value("editors/visual_editors/category_colors/special_color", Color(0.098, 0.361, 0.294), true);
652
ed_settings->set_initial_value("editors/visual_editors/category_colors/particle_color", Color(0.12, 0.358, 0.8), true);
653
654
} else if (visual_shader_color_theme == "Legacy") {
655
// Connection type colors
656
ed_settings->set_initial_value("editors/visual_editors/connection_colors/scalar_color", Color(0.38, 0.85, 0.96), true);
657
ed_settings->set_initial_value("editors/visual_editors/connection_colors/vector2_color", Color(0.74, 0.57, 0.95), true);
658
ed_settings->set_initial_value("editors/visual_editors/connection_colors/vector3_color", Color(0.84, 0.49, 0.93), true);
659
ed_settings->set_initial_value("editors/visual_editors/connection_colors/vector4_color", Color(1.0, 0.125, 0.95), true);
660
ed_settings->set_initial_value("editors/visual_editors/connection_colors/boolean_color", Color(0.55, 0.65, 0.94), true);
661
ed_settings->set_initial_value("editors/visual_editors/connection_colors/transform_color", Color(0.96, 0.66, 0.43), true);
662
ed_settings->set_initial_value("editors/visual_editors/connection_colors/sampler_color", Color(1.0, 1.0, 0.0), true);
663
664
// Node category colors (used for the node headers)
665
Ref<StyleBoxFlat> gn_panel_style = p_theme->get_stylebox(SceneStringName(panel), "GraphNode");
666
Color gn_bg_color = gn_panel_style->get_bg_color();
667
ed_settings->set_initial_value("editors/visual_editors/category_colors/output_color", gn_bg_color, true);
668
ed_settings->set_initial_value("editors/visual_editors/category_colors/color_color", gn_bg_color, true);
669
ed_settings->set_initial_value("editors/visual_editors/category_colors/conditional_color", gn_bg_color, true);
670
ed_settings->set_initial_value("editors/visual_editors/category_colors/input_color", gn_bg_color, true);
671
ed_settings->set_initial_value("editors/visual_editors/category_colors/scalar_color", gn_bg_color, true);
672
ed_settings->set_initial_value("editors/visual_editors/category_colors/textures_color", gn_bg_color, true);
673
ed_settings->set_initial_value("editors/visual_editors/category_colors/transform_color", gn_bg_color, true);
674
ed_settings->set_initial_value("editors/visual_editors/category_colors/utility_color", gn_bg_color, true);
675
ed_settings->set_initial_value("editors/visual_editors/category_colors/vector_color", gn_bg_color, true);
676
ed_settings->set_initial_value("editors/visual_editors/category_colors/special_color", gn_bg_color, true);
677
ed_settings->set_initial_value("editors/visual_editors/category_colors/particle_color", gn_bg_color, true);
678
}
679
}
680
681
void EditorThemeManager::_reset_dirty_flag() {
682
outdated_cache_dirty = true;
683
}
684
685
// Public interface for theme generation.
686
687
Ref<EditorTheme> EditorThemeManager::generate_theme(const Ref<EditorTheme> &p_old_theme) {
688
OS::get_singleton()->benchmark_begin_measure(get_benchmark_key(), "Generate Theme");
689
690
Ref<EditorTheme> theme = _create_base_theme(p_old_theme);
691
692
OS::get_singleton()->benchmark_begin_measure(get_benchmark_key(), "Merge Custom Theme");
693
694
const String custom_theme_path = EDITOR_GET("interface/theme/custom_theme");
695
if (!custom_theme_path.is_empty()) {
696
Ref<Theme> custom_theme = ResourceLoader::load(custom_theme_path);
697
if (custom_theme.is_valid()) {
698
theme->merge_with(custom_theme);
699
}
700
}
701
702
OS::get_singleton()->benchmark_end_measure(get_benchmark_key(), "Merge Custom Theme");
703
704
OS::get_singleton()->benchmark_end_measure(get_benchmark_key(), "Generate Theme");
705
benchmark_run++;
706
707
return theme;
708
}
709
710
bool EditorThemeManager::is_generated_theme_outdated() {
711
// This list includes settings used by files in the editor/themes folder.
712
// Note that the editor scale is purposefully omitted because it cannot be changed
713
// without a restart, so there is no point regenerating the theme.
714
715
if (outdated_cache_dirty) {
716
// TODO: We can use this information more intelligently to do partial theme updates and speed things up.
717
outdated_cache = EditorSettings::get_singleton()->check_changed_settings_in_group("interface/theme") ||
718
EditorSettings::get_singleton()->check_changed_settings_in_group("interface/editor/font") ||
719
EditorSettings::get_singleton()->check_changed_settings_in_group("interface/editor/main_font") ||
720
EditorSettings::get_singleton()->check_changed_settings_in_group("interface/editor/code_font") ||
721
EditorSettings::get_singleton()->check_changed_settings_in_group("editors/visual_editors") ||
722
EditorSettings::get_singleton()->check_changed_settings_in_group("text_editor/theme") ||
723
EditorSettings::get_singleton()->check_changed_settings_in_group("text_editor/help/help") ||
724
EditorSettings::get_singleton()->check_changed_settings_in_group("docks/property_editor/subresource_hue_tint") ||
725
EditorSettings::get_singleton()->check_changed_settings_in_group("filesystem/file_dialog/thumbnail_size") ||
726
EditorSettings::get_singleton()->check_changed_settings_in_group("run/output/font_size");
727
728
// The outdated flag is relevant at the moment of changing editor settings.
729
callable_mp_static(&EditorThemeManager::_reset_dirty_flag).call_deferred();
730
outdated_cache_dirty = false;
731
}
732
733
return outdated_cache;
734
}
735
736
bool EditorThemeManager::is_dark_theme() {
737
Color base_color = EDITOR_GET("interface/theme/base_color");
738
return base_color.get_luminance() < 0.5;
739
}
740
741
bool EditorThemeManager::is_dark_icon_and_font() {
742
int icon_font_color_setting = EDITOR_GET("interface/theme/icon_and_font_color");
743
if (icon_font_color_setting == ColorMode::AUTO_COLOR) {
744
return is_dark_theme();
745
}
746
747
return icon_font_color_setting == ColorMode::LIGHT_COLOR;
748
}
749
750
void EditorThemeManager::initialize() {
751
EditorColorMap::create();
752
EditorTheme::initialize();
753
}
754
755
void EditorThemeManager::finalize() {
756
EditorColorMap::finish();
757
EditorTheme::finalize();
758
}
759
760