diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml
index b00bf1c2508..6fa59a15ac1 100644
--- a/doc/classes/Control.xml
+++ b/doc/classes/Control.xml
@@ -458,6 +458,27 @@
See [method get_theme_color] for details.
+
+
+
+ Returns the default base scale value from the first matching [Theme] in the tree if that [Theme] has a valid [member Theme.default_base_scale] value.
+ See [method get_theme_color] for details.
+
+
+
+
+
+ Returns the default font from the first matching [Theme] in the tree if that [Theme] has a valid [member Theme.default_font] value.
+ See [method get_theme_color] for details.
+
+
+
+
+
+ Returns the default font size value from the first matching [Theme] in the tree if that [Theme] has a valid [member Theme.default_font_size] value.
+ See [method get_theme_color] for details.
+
+
diff --git a/doc/classes/Theme.xml b/doc/classes/Theme.xml
index edf58744329..2f5648dda9e 100644
--- a/doc/classes/Theme.xml
+++ b/doc/classes/Theme.xml
@@ -273,6 +273,24 @@
Returns [code]false[/code] if the theme does not have [code]theme_type[/code].
+
+
+
+ Returns [code]true[/code] if this theme has a valid [member default_base_scale] value.
+
+
+
+
+
+ Returns [code]true[/code] if this theme has a valid [member default_font] value.
+
+
+
+
+
+ Returns [code]true[/code] if this theme has a valid [member default_font_size] value.
+
+
@@ -484,11 +502,17 @@
+
+ The default base scale factor of this [Theme] resource. Used by some controls to scale their visual properties based on a global scale factor. If this value is set to [code]0.0[/code], the global scale factor is used.
+ Use [method has_default_base_scale] to check if this value is valid.
+
- The theme's default font.
+ The default font of this [Theme] resource. Used as a fallback value for font items defined in this theme, but having invalid values. If this value is also invalid, the global default value is used.
+ Use [method has_default_font] to check if this value is valid.
- The theme's default font size. Set to [code]-1[/code] to ignore and use global default.
+ The default font size of this [Theme] resource. Used as a fallback value for font size items defined in this theme, but having invalid values. If this value is set to [code]-1[/code], the global default value is used.
+ Use [method has_default_font_size] to check if this value is valid.
diff --git a/doc/classes/Window.xml b/doc/classes/Window.xml
index 15d844aacb6..0653c8b4535 100644
--- a/doc/classes/Window.xml
+++ b/doc/classes/Window.xml
@@ -59,6 +59,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 0579fb4cbd2..6efbcbc61ee 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -292,7 +292,8 @@ void editor_register_and_generate_icons(Ref p_theme, bool p_dark_theme =
Ref create_editor_theme(const Ref p_theme) {
Ref theme = Ref(memnew(Theme));
- const float default_contrast = 0.3;
+ // Controls may rely on the scale for their internal drawing logic.
+ theme->set_default_theme_base_scale(EDSCALE);
// Theme settings
Color accent_color = EDITOR_GET("interface/theme/accent_color");
@@ -310,6 +311,8 @@ Ref create_editor_theme(const Ref p_theme) {
Color preset_base_color;
float preset_contrast = 0;
+ const float default_contrast = 0.3;
+
// Please use alphabetical order if you're adding a new theme here
// (after "Custom")
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index 611035fff90..54548e19416 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -35,7 +35,6 @@
#include "core/os/os.h"
#ifdef TOOLS_ENABLED
-#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#endif
#include "scene/main/window.h"
@@ -44,17 +43,7 @@ List ColorPicker::preset_cache;
void ColorPicker::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_THEME_CHANGED: {
- btn_pick->set_icon(get_theme_icon(SNAME("screen_picker"), SNAME("ColorPicker")));
- btn_add_preset->set_icon(get_theme_icon(SNAME("add_preset")));
- _update_presets();
- _update_controls();
- } break;
case NOTIFICATION_ENTER_TREE: {
- btn_pick->set_icon(get_theme_icon(SNAME("screen_picker"), SNAME("ColorPicker")));
- btn_add_preset->set_icon(get_theme_icon(SNAME("add_preset")));
-
- _update_controls();
_update_color();
#ifdef TOOLS_ENABLED
@@ -71,18 +60,39 @@ void ColorPicker::_notification(int p_what) {
}
}
#endif
- } break;
- case NOTIFICATION_PARENTED: {
+ [[fallthrough]];
+ }
+ case NOTIFICATION_THEME_CHANGED: {
+ btn_pick->set_icon(get_theme_icon(SNAME("screen_picker"), SNAME("ColorPicker")));
+ btn_add_preset->set_icon(get_theme_icon(SNAME("add_preset")));
+
+ uv_edit->set_custom_minimum_size(Size2(get_theme_constant(SNAME("sv_width")), get_theme_constant(SNAME("sv_height"))));
+ w_edit->set_custom_minimum_size(Size2(get_theme_constant(SNAME("h_width")), 0));
+
+ wheel_edit->set_custom_minimum_size(Size2(get_theme_constant(SNAME("sv_width")), get_theme_constant(SNAME("sv_height"))));
+ wheel_margin->add_theme_constant_override("margin_bottom", 8 * get_theme_default_base_scale());
+
for (int i = 0; i < 4; i++) {
+ labels[i]->set_custom_minimum_size(Size2(get_theme_constant(SNAME("label_width")), 0));
set_offset((Side)i, get_offset((Side)i) + get_theme_constant(SNAME("margin")));
}
+
+ if (Engine::get_singleton()->is_editor_hint()) {
+ // Adjust for the width of the "Script" icon.
+ text_type->set_custom_minimum_size(Size2(28 * get_theme_default_base_scale(), 0));
+ }
+
+ _update_presets();
+ _update_controls();
} break;
+
case NOTIFICATION_VISIBILITY_CHANGED: {
Popup *p = Object::cast_to(get_parent());
if (p) {
p->set_size(Size2(get_combined_minimum_size().width + get_theme_constant(SNAME("margin")) * 2, get_combined_minimum_size().height + get_theme_constant(SNAME("margin")) * 2));
}
} break;
+
case NOTIFICATION_WM_CLOSE_REQUEST: {
if (screen != nullptr && screen->is_visible()) {
screen->hide();
@@ -762,11 +772,7 @@ void ColorPicker::_slider_draw(int p_which) {
Size2 size = scroll[p_which]->get_size();
Color left_color;
Color right_color;
-#ifdef TOOLS_ENABLED
- const real_t margin = 4 * EDSCALE;
-#else
- const real_t margin = 4;
-#endif
+ const real_t margin = 4 * get_theme_default_base_scale();
if (p_which == 3) {
scroll[p_which]->draw_texture_rect(get_theme_icon(SNAME("sample_bg"), SNAME("ColorPicker")), Rect2(Point2(0, margin), Size2(size.x, margin)), true);
@@ -1147,7 +1153,6 @@ ColorPicker::ColorPicker() :
uv_edit->set_mouse_filter(MOUSE_FILTER_PASS);
uv_edit->set_h_size_flags(SIZE_EXPAND_FILL);
uv_edit->set_v_size_flags(SIZE_EXPAND_FILL);
- uv_edit->set_custom_minimum_size(Size2(get_theme_constant(SNAME("sv_width")), get_theme_constant(SNAME("sv_height"))));
uv_edit->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(0, uv_edit));
HBoxContainer *hb_smpl = memnew(HBoxContainer);
@@ -1219,9 +1224,6 @@ ColorPicker::ColorPicker() :
text_type->set_text("#");
text_type->set_tooltip(TTR("Switch between hexadecimal and code values."));
if (Engine::get_singleton()->is_editor_hint()) {
-#ifdef TOOLS_ENABLED
- text_type->set_custom_minimum_size(Size2(28 * EDSCALE, 0)); // Adjust for the width of the "Script" icon.
-#endif
text_type->connect("pressed", callable_mp(this, &ColorPicker::_text_type_toggled));
} else {
text_type->set_flat(true);
@@ -1236,7 +1238,6 @@ ColorPicker::ColorPicker() :
wheel_edit->set_h_size_flags(SIZE_EXPAND_FILL);
wheel_edit->set_v_size_flags(SIZE_EXPAND_FILL);
- wheel_edit->set_custom_minimum_size(Size2(get_theme_constant(SNAME("sv_width")), get_theme_constant(SNAME("sv_height"))));
hb_edit->add_child(wheel_edit);
wheel_mat.instantiate();
@@ -1244,12 +1245,7 @@ ColorPicker::ColorPicker() :
circle_mat.instantiate();
circle_mat->set_shader(circle_shader);
- MarginContainer *wheel_margin(memnew(MarginContainer));
-#ifdef TOOLS_ENABLED
- wheel_margin->add_theme_constant_override("margin_bottom", 8 * EDSCALE);
-#else
wheel_margin->add_theme_constant_override("margin_bottom", 8);
-#endif
wheel_edit->add_child(wheel_margin);
wheel_margin->add_child(wheel);
@@ -1261,7 +1257,6 @@ ColorPicker::ColorPicker() :
wheel_uv->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(0, wheel_uv));
hb_edit->add_child(w_edit);
- w_edit->set_custom_minimum_size(Size2(get_theme_constant(SNAME("h_width")), 0));
w_edit->set_h_size_flags(SIZE_FILL);
w_edit->set_v_size_flags(SIZE_EXPAND_FILL);
w_edit->connect("gui_input", callable_mp(this, &ColorPicker::_w_input));
diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h
index 67ca007eb51..ad4f5ad5b1b 100644
--- a/scene/gui/color_picker.h
+++ b/scene/gui/color_picker.h
@@ -81,6 +81,7 @@ private:
Control *uv_edit = memnew(Control);
Control *w_edit = memnew(Control);
AspectRatioContainer *wheel_edit = memnew(AspectRatioContainer);
+ MarginContainer *wheel_margin = memnew(MarginContainer);
Ref wheel_mat;
Ref circle_mat;
Control *wheel = memnew(Control);
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 1b8b5e17eda..e2d3807b404 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -833,11 +833,12 @@ T Control::get_theme_item_in_types(Control *p_theme_owner, Window *p_theme_owner
ERR_FAIL_COND_V_MSG(p_theme_types.size() == 0, T(), "At least one theme type must be specified.");
// First, look through each control or window node in the branch, until no valid parent can be found.
- // For each control iterate through its inheritance chain and see if p_name exists in any of them.
+ // Only nodes with a theme resource attached are considered.
Control *theme_owner = p_theme_owner;
Window *theme_owner_window = p_theme_owner_window;
while (theme_owner || theme_owner_window) {
+ // For each theme resource check the theme types provided and see if p_name exists with any of them.
for (const StringName &E : p_theme_types) {
if (theme_owner && theme_owner->data.theme->has_theme_item(p_data_type, p_name, E)) {
return theme_owner->data.theme->get_theme_item(p_data_type, p_name, E);
@@ -888,11 +889,12 @@ bool Control::has_theme_item_in_types(Control *p_theme_owner, Window *p_theme_ow
ERR_FAIL_COND_V_MSG(p_theme_types.size() == 0, false, "At least one theme type must be specified.");
// First, look through each control or window node in the branch, until no valid parent can be found.
- // For each control iterate through its inheritance chain and see if p_name exists in any of them.
+ // Only nodes with a theme resource attached are considered.
Control *theme_owner = p_theme_owner;
Window *theme_owner_window = p_theme_owner_window;
while (theme_owner || theme_owner_window) {
+ // For each theme resource check the theme types provided and see if p_name exists with any of them.
for (const StringName &E : p_theme_types) {
if (theme_owner && theme_owner->data.theme->has_theme_item(p_data_type, p_name, E)) {
return true;
@@ -1130,6 +1132,150 @@ bool Control::has_theme_constant(const StringName &p_name, const StringName &p_t
return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_CONSTANT, p_name, theme_types);
}
+float Control::fetch_theme_default_base_scale(Control *p_theme_owner, Window *p_theme_owner_window) {
+ // First, look through each control or window node in the branch, until no valid parent can be found.
+ // Only nodes with a theme resource attached are considered.
+ // For each theme resource see if their assigned theme has the default value defined and valid.
+ Control *theme_owner = p_theme_owner;
+ Window *theme_owner_window = p_theme_owner_window;
+
+ while (theme_owner || theme_owner_window) {
+ if (theme_owner && theme_owner->data.theme->has_default_theme_base_scale()) {
+ return theme_owner->data.theme->get_default_theme_base_scale();
+ }
+
+ if (theme_owner_window && theme_owner_window->theme->has_default_theme_base_scale()) {
+ return theme_owner_window->theme->get_default_theme_base_scale();
+ }
+
+ Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent();
+ Control *parent_c = Object::cast_to(parent);
+ if (parent_c) {
+ theme_owner = parent_c->data.theme_owner;
+ theme_owner_window = parent_c->data.theme_owner_window;
+ } else {
+ Window *parent_w = Object::cast_to(parent);
+ if (parent_w) {
+ theme_owner = parent_w->theme_owner;
+ theme_owner_window = parent_w->theme_owner_window;
+ } else {
+ theme_owner = nullptr;
+ theme_owner_window = nullptr;
+ }
+ }
+ }
+
+ // Secondly, check the project-defined Theme resource.
+ if (Theme::get_project_default().is_valid()) {
+ if (Theme::get_project_default()->has_default_theme_base_scale()) {
+ return Theme::get_project_default()->get_default_theme_base_scale();
+ }
+ }
+
+ // Lastly, fall back on the default Theme.
+ return Theme::get_default()->get_default_theme_base_scale();
+}
+
+float Control::get_theme_default_base_scale() const {
+ return fetch_theme_default_base_scale(data.theme_owner, data.theme_owner_window);
+}
+
+Ref Control::fetch_theme_default_font(Control *p_theme_owner, Window *p_theme_owner_window) {
+ // First, look through each control or window node in the branch, until no valid parent can be found.
+ // Only nodes with a theme resource attached are considered.
+ // For each theme resource see if their assigned theme has the default value defined and valid.
+ Control *theme_owner = p_theme_owner;
+ Window *theme_owner_window = p_theme_owner_window;
+
+ while (theme_owner || theme_owner_window) {
+ if (theme_owner && theme_owner->data.theme->has_default_theme_font()) {
+ return theme_owner->data.theme->get_default_theme_font();
+ }
+
+ if (theme_owner_window && theme_owner_window->theme->has_default_theme_font()) {
+ return theme_owner_window->theme->get_default_theme_font();
+ }
+
+ Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent();
+ Control *parent_c = Object::cast_to(parent);
+ if (parent_c) {
+ theme_owner = parent_c->data.theme_owner;
+ theme_owner_window = parent_c->data.theme_owner_window;
+ } else {
+ Window *parent_w = Object::cast_to(parent);
+ if (parent_w) {
+ theme_owner = parent_w->theme_owner;
+ theme_owner_window = parent_w->theme_owner_window;
+ } else {
+ theme_owner = nullptr;
+ theme_owner_window = nullptr;
+ }
+ }
+ }
+
+ // Secondly, check the project-defined Theme resource.
+ if (Theme::get_project_default().is_valid()) {
+ if (Theme::get_project_default()->has_default_theme_font()) {
+ return Theme::get_project_default()->get_default_theme_font();
+ }
+ }
+
+ // Lastly, fall back on the default Theme.
+ return Theme::get_default()->get_default_theme_font();
+}
+
+Ref Control::get_theme_default_font() const {
+ return fetch_theme_default_font(data.theme_owner, data.theme_owner_window);
+}
+
+int Control::fetch_theme_default_font_size(Control *p_theme_owner, Window *p_theme_owner_window) {
+ // First, look through each control or window node in the branch, until no valid parent can be found.
+ // Only nodes with a theme resource attached are considered.
+ // For each theme resource see if their assigned theme has the default value defined and valid.
+ Control *theme_owner = p_theme_owner;
+ Window *theme_owner_window = p_theme_owner_window;
+
+ while (theme_owner || theme_owner_window) {
+ if (theme_owner && theme_owner->data.theme->has_default_theme_font_size()) {
+ return theme_owner->data.theme->get_default_theme_font_size();
+ }
+
+ if (theme_owner_window && theme_owner_window->theme->has_default_theme_font_size()) {
+ return theme_owner_window->theme->get_default_theme_font_size();
+ }
+
+ Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent();
+ Control *parent_c = Object::cast_to(parent);
+ if (parent_c) {
+ theme_owner = parent_c->data.theme_owner;
+ theme_owner_window = parent_c->data.theme_owner_window;
+ } else {
+ Window *parent_w = Object::cast_to(parent);
+ if (parent_w) {
+ theme_owner = parent_w->theme_owner;
+ theme_owner_window = parent_w->theme_owner_window;
+ } else {
+ theme_owner = nullptr;
+ theme_owner_window = nullptr;
+ }
+ }
+ }
+
+ // Secondly, check the project-defined Theme resource.
+ if (Theme::get_project_default().is_valid()) {
+ if (Theme::get_project_default()->has_default_theme_font_size()) {
+ return Theme::get_project_default()->get_default_theme_font_size();
+ }
+ }
+
+ // Lastly, fall back on the default Theme.
+ return Theme::get_default()->get_default_theme_font_size();
+}
+
+int Control::get_theme_default_font_size() const {
+ return fetch_theme_default_font_size(data.theme_owner, data.theme_owner_window);
+}
+
Rect2 Control::get_parent_anchorable_rect() const {
if (!is_inside_tree()) {
return Rect2();
@@ -2789,6 +2935,10 @@ void Control::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_theme_color", "name", "theme_type"), &Control::has_theme_color, DEFVAL(""));
ClassDB::bind_method(D_METHOD("has_theme_constant", "name", "theme_type"), &Control::has_theme_constant, DEFVAL(""));
+ ClassDB::bind_method(D_METHOD("get_theme_default_base_scale"), &Control::get_theme_default_base_scale);
+ ClassDB::bind_method(D_METHOD("get_theme_default_font"), &Control::get_theme_default_font);
+ ClassDB::bind_method(D_METHOD("get_theme_default_font_size"), &Control::get_theme_default_font_size);
+
ClassDB::bind_method(D_METHOD("get_parent_control"), &Control::get_parent_control);
ClassDB::bind_method(D_METHOD("set_h_grow_direction", "direction"), &Control::set_h_grow_direction);
diff --git a/scene/gui/control.h b/scene/gui/control.h
index 87ff3918cb8..bdc06319ea6 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -509,6 +509,14 @@ public:
bool has_theme_color(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
bool has_theme_constant(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
+ static float fetch_theme_default_base_scale(Control *p_theme_owner, Window *p_theme_owner_window);
+ static Ref fetch_theme_default_font(Control *p_theme_owner, Window *p_theme_owner_window);
+ static int fetch_theme_default_font_size(Control *p_theme_owner, Window *p_theme_owner_window);
+
+ float get_theme_default_base_scale() const;
+ Ref get_theme_default_font() const;
+ int get_theme_default_font_size() const;
+
/* TOOLTIP */
void set_tooltip(const String &p_tooltip);
diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp
index 5d98aaa6982..71d2778cc30 100644
--- a/scene/gui/dialogs.cpp
+++ b/scene/gui/dialogs.cpp
@@ -37,7 +37,6 @@
#ifdef TOOLS_ENABLED
#include "editor/editor_node.h"
-#include "editor/editor_scale.h"
#include "scene/main/window.h" // Only used to check for more modals when dimming the editor.
#endif
@@ -363,8 +362,7 @@ Button *ConfirmationDialog::get_cancel_button() {
ConfirmationDialog::ConfirmationDialog() {
set_title(TTRC("Please Confirm..."));
-#ifdef TOOLS_ENABLED
- set_min_size(Size2(200, 70) * EDSCALE);
-#endif
+ set_min_size(Size2(200, 70));
+
cancel = add_cancel_button();
}
diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp
index 56b8a936e1b..5d024d3be73 100644
--- a/scene/gui/gradient_edit.cpp
+++ b/scene/gui/gradient_edit.cpp
@@ -32,15 +32,6 @@
#include "core/os/keyboard.h"
-#ifdef TOOLS_ENABLED
-#include "editor/editor_scale.h"
-#define SPACING (3 * EDSCALE)
-#define POINT_WIDTH (8 * EDSCALE)
-#else
-#define SPACING 3
-#define POINT_WIDTH 8
-#endif
-
GradientEdit::GradientEdit() {
set_focus_mode(FOCUS_ALL);
@@ -53,12 +44,12 @@ GradientEdit::GradientEdit() {
int GradientEdit::_get_point_from_pos(int x) {
int result = -1;
- int total_w = get_size().width - get_size().height - SPACING;
+ int total_w = get_size().width - get_size().height - draw_spacing;
float min_distance = 1e20;
for (int i = 0; i < points.size(); i++) {
//Check if we clicked at point
float distance = ABS(x - points[i].offset * total_w);
- float min = (POINT_WIDTH / 2 * 1.7); //make it easier to grab
+ float min = (draw_point_width / 2 * 1.7); //make it easier to grab
if (distance <= min && distance < min_distance) {
result = i;
min_distance = distance;
@@ -129,7 +120,7 @@ void GradientEdit::gui_input(const Ref &p_event) {
grabbed = _get_point_from_pos(x);
if (grabbed != -1) {
- int total_w = get_size().width - get_size().height - SPACING;
+ int total_w = get_size().width - get_size().height - draw_spacing;
Gradient::Point newPoint = points[grabbed];
newPoint.offset = CLAMP(x / float(total_w), 0, 1);
@@ -151,10 +142,10 @@ void GradientEdit::gui_input(const Ref &p_event) {
if (mb.is_valid() && mb->get_button_index() == 1 && mb->is_pressed()) {
update();
int x = mb->get_position().x;
- int total_w = get_size().width - get_size().height - SPACING;
+ int total_w = get_size().width - get_size().height - draw_spacing;
//Check if color selector was clicked.
- if (x > total_w + SPACING) {
+ if (x > total_w + draw_spacing) {
_show_color_picker();
return;
}
@@ -225,7 +216,7 @@ void GradientEdit::gui_input(const Ref &p_event) {
Ref mm = p_event;
if (mm.is_valid() && grabbing) {
- int total_w = get_size().width - get_size().height - SPACING;
+ int total_w = get_size().width - get_size().height - draw_spacing;
int x = mm->get_position().x;
@@ -297,6 +288,12 @@ void GradientEdit::_notification(int p_what) {
picker->connect("color_changed", callable_mp(this, &GradientEdit::_color_changed));
}
}
+
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ draw_spacing = BASE_SPACING * get_theme_default_base_scale();
+ draw_point_width = BASE_POINT_WIDTH * get_theme_default_base_scale();
+ }
+
if (p_what == NOTIFICATION_DRAW) {
int w = get_size().x;
int h = get_size().y;
@@ -305,7 +302,7 @@ void GradientEdit::_notification(int p_what) {
return; //Safety check. We have division by 'h'. And in any case there is nothing to draw with such size
}
- int total_w = get_size().width - get_size().height - SPACING;
+ int total_w = get_size().width - get_size().height - draw_spacing;
//Draw checker pattern for ramp
draw_texture_rect(get_theme_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons")), Rect2(0, 0, total_w, h), true);
@@ -358,7 +355,7 @@ void GradientEdit::_notification(int p_what) {
col.a = 0.9;
draw_line(Vector2(points[i].offset * total_w, 0), Vector2(points[i].offset * total_w, h / 2), col);
- Rect2 rect = Rect2(points[i].offset * total_w - POINT_WIDTH / 2, h / 2, POINT_WIDTH, h / 2);
+ Rect2 rect = Rect2(points[i].offset * total_w - draw_point_width / 2, h / 2, draw_point_width, h / 2);
draw_rect(rect, points[i].color, true);
draw_rect(rect, col, false);
if (grabbed == i) {
@@ -375,15 +372,15 @@ void GradientEdit::_notification(int p_what) {
}
//Draw "button" for color selector
- draw_texture_rect(get_theme_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons")), Rect2(total_w + SPACING, 0, h, h), true);
+ draw_texture_rect(get_theme_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons")), Rect2(total_w + draw_spacing, 0, h, h), true);
if (grabbed != -1) {
//Draw with selection color
- draw_rect(Rect2(total_w + SPACING, 0, h, h), points[grabbed].color);
+ draw_rect(Rect2(total_w + draw_spacing, 0, h, h), points[grabbed].color);
} else {
//if no color selected draw grey color with 'X' on top.
- draw_rect(Rect2(total_w + SPACING, 0, h, h), Color(0.5, 0.5, 0.5, 1));
- draw_line(Vector2(total_w + SPACING, 0), Vector2(total_w + SPACING + h, h), Color(1, 1, 1, 0.6));
- draw_line(Vector2(total_w + SPACING, h), Vector2(total_w + SPACING + h, 0), Color(1, 1, 1, 0.6));
+ draw_rect(Rect2(total_w + draw_spacing, 0, h, h), Color(0.5, 0.5, 0.5, 1));
+ draw_line(Vector2(total_w + draw_spacing, 0), Vector2(total_w + draw_spacing + h, h), Color(1, 1, 1, 0.6));
+ draw_line(Vector2(total_w + draw_spacing, h), Vector2(total_w + draw_spacing + h, 0), Color(1, 1, 1, 0.6));
}
//Draw borders around color ramp if in focus
diff --git a/scene/gui/gradient_edit.h b/scene/gui/gradient_edit.h
index a1736319639..f3a39daaf68 100644
--- a/scene/gui/gradient_edit.h
+++ b/scene/gui/gradient_edit.h
@@ -46,6 +46,13 @@ class GradientEdit : public Control {
int grabbed = -1;
Vector points;
+ // Make sure to use the scaled value below.
+ const int BASE_SPACING = 3;
+ const int BASE_POINT_WIDTH = 8;
+
+ int draw_spacing = BASE_SPACING;
+ int draw_point_width = BASE_POINT_WIDTH;
+
void _draw_checker(int x, int y, int w, int h);
void _color_changed(const Color &p_color);
int _get_point_from_pos(int x);
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index d9c08ec272a..35e31be9af4 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -36,10 +36,6 @@
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
-#ifdef TOOLS_ENABLED
-#include "editor/editor_scale.h"
-#endif
-
constexpr int MINIMAP_OFFSET = 12;
constexpr int MINIMAP_PADDING = 5;
@@ -436,6 +432,8 @@ void GraphEdit::_notification(int p_what) {
snap_button->set_icon(get_theme_icon(SNAME("snap")));
minimap_button->set_icon(get_theme_icon(SNAME("minimap")));
layout_button->set_icon(get_theme_icon(SNAME("layout")));
+
+ zoom_label->set_custom_minimum_size(Size2(48, 0) * get_theme_default_base_scale());
}
if (p_what == NOTIFICATION_READY) {
Size2 hmin = h_scroll->get_combined_minimum_size();
@@ -816,11 +814,7 @@ void GraphEdit::_draw_connection_line(CanvasItem *p_where, const Vector2 &p_from
scaled_points.push_back(points[i] * p_zoom);
}
-#ifdef TOOLS_ENABLED
- p_where->draw_polyline_colors(scaled_points, colors, Math::floor(p_width * EDSCALE), lines_antialiased);
-#else
- p_where->draw_polyline_colors(scaled_points, colors, p_width, lines_antialiased);
-#endif
+ p_where->draw_polyline_colors(scaled_points, colors, Math::floor(p_width * get_theme_default_base_scale()), lines_antialiased);
}
void GraphEdit::_connections_layer_draw() {
@@ -2272,11 +2266,7 @@ GraphEdit::GraphEdit() {
zoom_label->set_visible(false);
zoom_label->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
zoom_label->set_align(Label::ALIGN_CENTER);
-#ifdef TOOLS_ENABLED
- zoom_label->set_custom_minimum_size(Size2(48, 0) * EDSCALE);
-#else
zoom_label->set_custom_minimum_size(Size2(48, 0));
-#endif
_update_zoom_label();
zoom_minus = memnew(Button);
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 2c1092d8f95..653885aa081 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -40,7 +40,6 @@
#include "servers/display_server.h"
#include "servers/text_server.h"
#ifdef TOOLS_ENABLED
-#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#endif
#include "scene/main/window.h"
@@ -713,11 +712,7 @@ void LineEdit::_notification(int p_what) {
ofs_max -= r_icon->get_width();
}
-#ifdef TOOLS_ENABLED
- int caret_width = Math::round(EDSCALE);
-#else
- int caret_width = 1;
-#endif
+ int caret_width = Math::round(1 * get_theme_default_base_scale());
// Draw selections rects.
Vector2 ofs = Point2(x_ofs + scroll_offset, y_ofs);
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index bc25177275c..4588966d884 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -41,10 +41,6 @@
#include "modules/regex/regex.h"
#endif
-#ifdef TOOLS_ENABLED
-#include "editor/editor_scale.h"
-#endif
-
RichTextLabel::Item *RichTextLabel::_get_next_item(Item *p_item, bool p_free) const {
if (p_free) {
if (p_item->subitems.size()) {
@@ -995,19 +991,13 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
Color uc = font_color;
uc.a *= 0.5;
float y_off = TS->shaped_text_get_underline_position(rid);
- float underline_width = TS->shaped_text_get_underline_thickness(rid);
-#ifdef TOOLS_ENABLED
- underline_width *= EDSCALE;
-#endif
+ float underline_width = TS->shaped_text_get_underline_thickness(rid) * get_theme_default_base_scale();
draw_line(p_ofs + Vector2(off.x, off.y + y_off), p_ofs + Vector2(off.x + glyphs[i].advance * glyphs[i].repeat, off.y + y_off), uc, underline_width);
} else if (_find_strikethrough(it)) {
Color uc = font_color;
uc.a *= 0.5;
float y_off = -TS->shaped_text_get_ascent(rid) + TS->shaped_text_get_size(rid).y / 2;
- float underline_width = TS->shaped_text_get_underline_thickness(rid);
-#ifdef TOOLS_ENABLED
- underline_width *= EDSCALE;
-#endif
+ float underline_width = TS->shaped_text_get_underline_thickness(rid) * get_theme_default_base_scale();
draw_line(p_ofs + Vector2(off.x, off.y + y_off), p_ofs + Vector2(off.x + glyphs[i].advance * glyphs[i].repeat, off.y + y_off), uc, underline_width);
}
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index e2ddc761b8e..875049ab4e7 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -42,10 +42,6 @@
#include "scene/main/window.h"
-#ifdef TOOLS_ENABLED
-#include "editor/editor_scale.h"
-#endif
-
static bool _is_text_char(char32_t c) {
return !is_symbol(c);
}
@@ -1173,12 +1169,8 @@ void TextEdit::_notification(int p_what) {
}
}
- // Carets
-#ifdef TOOLS_ENABLED
- int caret_width = Math::round(EDSCALE);
-#else
- int caret_width = 1;
-#endif
+ // Carets.
+ int caret_width = Math::round(1 * get_theme_default_base_scale());
if (!clipped && caret.line == line && line_wrap_index == caret_wrap_index) {
caret.draw_pos.y = ofs_y + ldata->get_line_descent(line_wrap_index);
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 7d7596635cc..3f041bf65a5 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -41,10 +41,6 @@
#include "box_container.h"
-#ifdef TOOLS_ENABLED
-#include "editor/editor_scale.h"
-#endif
-
#include
Size2 TreeItem::Cell::get_icon_size() const {
@@ -1377,6 +1373,8 @@ void Tree::update_cache() {
cache.title_button_hover = get_theme_stylebox(SNAME("title_button_hover"));
cache.title_button_color = get_theme_color(SNAME("title_button_color"));
+ cache.base_scale = get_theme_default_base_scale();
+
v_scroll->set_custom_step(cache.font->get_height(cache.font_size));
}
@@ -2046,15 +2044,9 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
root_pos -= Point2i(cache.arrow->get_width(), 0);
}
- float line_width = cache.relationship_line_width;
- float parent_line_width = cache.parent_hl_line_width;
- float children_line_width = cache.children_hl_line_width;
-
-#ifdef TOOLS_ENABLED
- line_width *= Math::round(EDSCALE);
- parent_line_width *= Math::round(EDSCALE);
- children_line_width *= Math::round(EDSCALE);
-#endif
+ float line_width = cache.relationship_line_width * Math::round(cache.base_scale);
+ float parent_line_width = cache.parent_hl_line_width * Math::round(cache.base_scale);
+ float children_line_width = cache.children_hl_line_width * Math::round(cache.base_scale);
Point2i parent_pos = Point2i(parent_ofs - cache.arrow->get_width() / 2, p_pos.y + label_h / 2 + cache.arrow->get_height() / 2) - cache.offset + p_draw_ofs;
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index c4a6b6b058f..6ca9458e9bb 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -516,6 +516,8 @@ private:
Color custom_button_font_highlight;
Color font_outline_color;
+ float base_scale = 1.0;
+
int hseparation = 0;
int vseparation = 0;
int item_margin = 0;
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index ca5a3915d0a..a0f62c853f1 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -1266,6 +1266,18 @@ bool Window::has_theme_constant(const StringName &p_name, const StringName &p_th
return Control::has_theme_item_in_types(theme_owner, theme_owner_window, Theme::DATA_TYPE_CONSTANT, p_name, theme_types);
}
+float Window::get_theme_default_base_scale() const {
+ return Control::fetch_theme_default_base_scale(theme_owner, theme_owner_window);
+}
+
+Ref Window::get_theme_default_font() const {
+ return Control::fetch_theme_default_font(theme_owner, theme_owner_window);
+}
+
+int Window::get_theme_default_font_size() const {
+ return Control::fetch_theme_default_font_size(theme_owner, theme_owner_window);
+}
+
Rect2i Window::get_parent_rect() const {
ERR_FAIL_COND_V(!is_inside_tree(), Rect2i());
if (is_embedded()) {
@@ -1480,6 +1492,10 @@ void Window::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_theme_color", "name", "theme_type"), &Window::has_theme_color, DEFVAL(""));
ClassDB::bind_method(D_METHOD("has_theme_constant", "name", "theme_type"), &Window::has_theme_constant, DEFVAL(""));
+ ClassDB::bind_method(D_METHOD("get_theme_default_base_scale"), &Window::get_theme_default_base_scale);
+ ClassDB::bind_method(D_METHOD("get_theme_default_font"), &Window::get_theme_default_font);
+ ClassDB::bind_method(D_METHOD("get_theme_default_font_size"), &Window::get_theme_default_font_size);
+
ClassDB::bind_method(D_METHOD("set_layout_direction", "direction"), &Window::set_layout_direction);
ClassDB::bind_method(D_METHOD("get_layout_direction"), &Window::get_layout_direction);
ClassDB::bind_method(D_METHOD("is_layout_rtl"), &Window::is_layout_rtl);
diff --git a/scene/main/window.h b/scene/main/window.h
index 4f31d9cd1f3..def6eab7b8b 100644
--- a/scene/main/window.h
+++ b/scene/main/window.h
@@ -280,6 +280,10 @@ public:
bool has_theme_color(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
bool has_theme_constant(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
+ float get_theme_default_base_scale() const;
+ Ref get_theme_default_font() const;
+ int get_theme_default_font_size() const;
+
Rect2i get_parent_rect() const;
virtual DisplayServer::WindowID get_window_id() const override;
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index 94cf8264775..54bb7a82cfd 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -1036,9 +1036,16 @@ void make_default_theme(bool p_hidpi, Ref p_font) {
}
Ref large_font = default_font;
- fill_default_theme(t, default_font, large_font, default_icon, default_style, p_hidpi ? 2.0 : 1.0);
+
+ float default_scale = 1.0;
+ if (p_hidpi) {
+ default_scale = 2.0;
+ }
+
+ fill_default_theme(t, default_font, large_font, default_icon, default_style, default_scale);
Theme::set_default(t);
+ Theme::set_default_base_scale(default_scale);
Theme::set_default_icon(default_icon);
Theme::set_default_style(default_style);
Theme::set_default_font(default_font);
diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp
index e49d883ba4e..dd5f0b9c6c0 100644
--- a/scene/resources/theme.cpp
+++ b/scene/resources/theme.cpp
@@ -29,18 +29,1126 @@
/*************************************************************************/
#include "theme.h"
-#include "core/io/file_access.h"
#include "core/string/print_string.h"
-void Theme::_emit_theme_changed() {
- if (no_change_propagation) {
+// Universal Theme resources used when no other theme has the item.
+Ref Theme::default_theme;
+Ref Theme::project_default_theme;
+
+// Universal default values, final fallback for every theme.
+float Theme::default_base_scale = 1.0;
+Ref Theme::default_icon;
+Ref Theme::default_style;
+Ref Theme::default_font;
+int Theme::default_font_size = 16;
+
+// Dynamic properties.
+bool Theme::_set(const StringName &p_name, const Variant &p_value) {
+ String sname = p_name;
+
+ if (sname.find("/") != -1) {
+ String type = sname.get_slicec('/', 1);
+ String theme_type = sname.get_slicec('/', 0);
+ String name = sname.get_slicec('/', 2);
+
+ if (type == "icons") {
+ set_icon(name, theme_type, p_value);
+ } else if (type == "styles") {
+ set_stylebox(name, theme_type, p_value);
+ } else if (type == "fonts") {
+ set_font(name, theme_type, p_value);
+ } else if (type == "font_sizes") {
+ set_font_size(name, theme_type, p_value);
+ } else if (type == "colors") {
+ set_color(name, theme_type, p_value);
+ } else if (type == "constants") {
+ set_constant(name, theme_type, p_value);
+ } else if (type == "base_type") {
+ set_type_variation(theme_type, p_value);
+ } else {
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+bool Theme::_get(const StringName &p_name, Variant &r_ret) const {
+ String sname = p_name;
+
+ if (sname.find("/") != -1) {
+ String type = sname.get_slicec('/', 1);
+ String theme_type = sname.get_slicec('/', 0);
+ String name = sname.get_slicec('/', 2);
+
+ if (type == "icons") {
+ if (!has_icon(name, theme_type)) {
+ r_ret = Ref();
+ } else {
+ r_ret = get_icon(name, theme_type);
+ }
+ } else if (type == "styles") {
+ if (!has_stylebox(name, theme_type)) {
+ r_ret = Ref();
+ } else {
+ r_ret = get_stylebox(name, theme_type);
+ }
+ } else if (type == "fonts") {
+ if (!has_font(name, theme_type)) {
+ r_ret = Ref();
+ } else {
+ r_ret = get_font(name, theme_type);
+ }
+ } else if (type == "font_sizes") {
+ r_ret = get_font_size(name, theme_type);
+ } else if (type == "colors") {
+ r_ret = get_color(name, theme_type);
+ } else if (type == "constants") {
+ r_ret = get_constant(name, theme_type);
+ } else if (type == "base_type") {
+ r_ret = get_type_variation_base(theme_type);
+ } else {
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+void Theme::_get_property_list(List *p_list) const {
+ List list;
+
+ const StringName *key = nullptr;
+
+ // Type variations.
+ while ((key = variation_map.next(key))) {
+ list.push_back(PropertyInfo(Variant::STRING_NAME, String() + *key + "/base_type"));
+ }
+
+ key = nullptr;
+
+ // Icons.
+ while ((key = icon_map.next(key))) {
+ const StringName *key2 = nullptr;
+
+ while ((key2 = icon_map[*key].next(key2))) {
+ list.push_back(PropertyInfo(Variant::OBJECT, String() + *key + "/icons/" + *key2, PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL));
+ }
+ }
+
+ key = nullptr;
+
+ // Styles.
+ while ((key = style_map.next(key))) {
+ const StringName *key2 = nullptr;
+
+ while ((key2 = style_map[*key].next(key2))) {
+ list.push_back(PropertyInfo(Variant::OBJECT, String() + *key + "/styles/" + *key2, PROPERTY_HINT_RESOURCE_TYPE, "StyleBox", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL));
+ }
+ }
+
+ key = nullptr;
+
+ // Fonts.
+ while ((key = font_map.next(key))) {
+ const StringName *key2 = nullptr;
+
+ while ((key2 = font_map[*key].next(key2))) {
+ list.push_back(PropertyInfo(Variant::OBJECT, String() + *key + "/fonts/" + *key2, PROPERTY_HINT_RESOURCE_TYPE, "Font", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL));
+ }
+ }
+
+ key = nullptr;
+
+ // Font sizes.
+ while ((key = font_size_map.next(key))) {
+ const StringName *key2 = nullptr;
+
+ while ((key2 = font_size_map[*key].next(key2))) {
+ list.push_back(PropertyInfo(Variant::INT, String() + *key + "/font_sizes/" + *key2));
+ }
+ }
+
+ key = nullptr;
+
+ // Colors.
+ while ((key = color_map.next(key))) {
+ const StringName *key2 = nullptr;
+
+ while ((key2 = color_map[*key].next(key2))) {
+ list.push_back(PropertyInfo(Variant::COLOR, String() + *key + "/colors/" + *key2));
+ }
+ }
+
+ key = nullptr;
+
+ // Constants.
+ while ((key = constant_map.next(key))) {
+ const StringName *key2 = nullptr;
+
+ while ((key2 = constant_map[*key].next(key2))) {
+ list.push_back(PropertyInfo(Variant::INT, String() + *key + "/constants/" + *key2));
+ }
+ }
+
+ // Sort and store properties.
+ list.sort();
+ for (const PropertyInfo &E : list) {
+ p_list->push_back(E);
+ }
+}
+
+// Universal fallback Theme resources.
+Ref Theme::get_default() {
+ return default_theme;
+}
+
+void Theme::set_default(const Ref &p_default) {
+ default_theme = p_default;
+}
+
+Ref Theme::get_project_default() {
+ return project_default_theme;
+}
+
+void Theme::set_project_default(const Ref &p_project_default) {
+ project_default_theme = p_project_default;
+}
+
+// Universal fallback values for theme item types.
+void Theme::set_default_base_scale(float p_base_scale) {
+ default_base_scale = p_base_scale;
+}
+
+void Theme::set_default_icon(const Ref &p_icon) {
+ default_icon = p_icon;
+}
+
+void Theme::set_default_style(const Ref &p_style) {
+ default_style = p_style;
+}
+
+void Theme::set_default_font(const Ref &p_font) {
+ default_font = p_font;
+}
+
+void Theme::set_default_font_size(int p_font_size) {
+ default_font_size = p_font_size;
+}
+
+// Fallback values for theme item types, configurable per theme.
+void Theme::set_default_theme_base_scale(float p_base_scale) {
+ if (default_theme_base_scale == p_base_scale) {
return;
}
- notify_property_list_changed();
- emit_changed();
+ default_theme_base_scale = p_base_scale;
+
+ _emit_theme_changed();
}
+float Theme::get_default_theme_base_scale() const {
+ return default_theme_base_scale;
+}
+
+bool Theme::has_default_theme_base_scale() const {
+ return default_theme_base_scale > 0.0;
+}
+
+void Theme::set_default_theme_font(const Ref &p_default_font) {
+ if (default_theme_font == p_default_font) {
+ return;
+ }
+
+ if (default_theme_font.is_valid()) {
+ default_theme_font->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ }
+
+ default_theme_font = p_default_font;
+
+ if (default_theme_font.is_valid()) {
+ default_theme_font->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ }
+
+ _emit_theme_changed();
+}
+
+Ref Theme::get_default_theme_font() const {
+ return default_theme_font;
+}
+
+bool Theme::has_default_theme_font() const {
+ return default_theme_font.is_valid();
+}
+
+void Theme::set_default_theme_font_size(int p_font_size) {
+ if (default_theme_font_size == p_font_size) {
+ return;
+ }
+
+ default_theme_font_size = p_font_size;
+
+ _emit_theme_changed();
+}
+
+int Theme::get_default_theme_font_size() const {
+ return default_theme_font_size;
+}
+
+bool Theme::has_default_theme_font_size() const {
+ return default_theme_font_size > 0;
+}
+
+// Icons.
+void Theme::set_icon(const StringName &p_name, const StringName &p_theme_type, const Ref &p_icon) {
+ if (icon_map[p_theme_type].has(p_name) && icon_map[p_theme_type][p_name].is_valid()) {
+ icon_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ }
+
+ icon_map[p_theme_type][p_name] = p_icon;
+
+ if (p_icon.is_valid()) {
+ icon_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ }
+
+ _emit_theme_changed();
+}
+
+Ref Theme::get_icon(const StringName &p_name, const StringName &p_theme_type) const {
+ if (icon_map.has(p_theme_type) && icon_map[p_theme_type].has(p_name) && icon_map[p_theme_type][p_name].is_valid()) {
+ return icon_map[p_theme_type][p_name];
+ } else {
+ return default_icon;
+ }
+}
+
+bool Theme::has_icon(const StringName &p_name, const StringName &p_theme_type) const {
+ return (icon_map.has(p_theme_type) && icon_map[p_theme_type].has(p_name) && icon_map[p_theme_type][p_name].is_valid());
+}
+
+bool Theme::has_icon_nocheck(const StringName &p_name, const StringName &p_theme_type) const {
+ return (icon_map.has(p_theme_type) && icon_map[p_theme_type].has(p_name));
+}
+
+void Theme::rename_icon(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
+ 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.");
+ 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.");
+ 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.");
+
+ icon_map[p_theme_type][p_name] = icon_map[p_theme_type][p_old_name];
+ icon_map[p_theme_type].erase(p_old_name);
+
+ _emit_theme_changed();
+}
+
+void Theme::clear_icon(const StringName &p_name, const StringName &p_theme_type) {
+ 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.");
+ ERR_FAIL_COND_MSG(!icon_map[p_theme_type].has(p_name), "Cannot clear the icon '" + String(p_name) + "' because it does not exist.");
+
+ if (icon_map[p_theme_type][p_name].is_valid()) {
+ icon_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ }
+
+ icon_map[p_theme_type].erase(p_name);
+
+ _emit_theme_changed();
+}
+
+void Theme::get_icon_list(StringName p_theme_type, List *p_list) const {
+ ERR_FAIL_NULL(p_list);
+
+ if (!icon_map.has(p_theme_type)) {
+ return;
+ }
+
+ const StringName *key = nullptr;
+
+ while ((key = icon_map[p_theme_type].next(key))) {
+ p_list->push_back(*key);
+ }
+}
+
+void Theme::add_icon_type(const StringName &p_theme_type) {
+ if (icon_map.has(p_theme_type)) {
+ return;
+ }
+ icon_map[p_theme_type] = HashMap>();
+}
+
+void Theme::get_icon_type_list(List *p_list) const {
+ ERR_FAIL_NULL(p_list);
+
+ const StringName *key = nullptr;
+ while ((key = icon_map.next(key))) {
+ p_list->push_back(*key);
+ }
+}
+
+// Styleboxes.
+void Theme::set_stylebox(const StringName &p_name, const StringName &p_theme_type, const Ref &p_style) {
+ if (style_map[p_theme_type].has(p_name) && style_map[p_theme_type][p_name].is_valid()) {
+ style_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ }
+
+ style_map[p_theme_type][p_name] = p_style;
+
+ if (p_style.is_valid()) {
+ style_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ }
+
+ _emit_theme_changed();
+}
+
+Ref Theme::get_stylebox(const StringName &p_name, const StringName &p_theme_type) const {
+ if (style_map.has(p_theme_type) && style_map[p_theme_type].has(p_name) && style_map[p_theme_type][p_name].is_valid()) {
+ return style_map[p_theme_type][p_name];
+ } else {
+ return default_style;
+ }
+}
+
+bool Theme::has_stylebox(const StringName &p_name, const StringName &p_theme_type) const {
+ return (style_map.has(p_theme_type) && style_map[p_theme_type].has(p_name) && style_map[p_theme_type][p_name].is_valid());
+}
+
+bool Theme::has_stylebox_nocheck(const StringName &p_name, const StringName &p_theme_type) const {
+ return (style_map.has(p_theme_type) && style_map[p_theme_type].has(p_name));
+}
+
+void Theme::rename_stylebox(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
+ 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.");
+ 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.");
+ 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.");
+
+ style_map[p_theme_type][p_name] = style_map[p_theme_type][p_old_name];
+ style_map[p_theme_type].erase(p_old_name);
+
+ _emit_theme_changed();
+}
+
+void Theme::clear_stylebox(const StringName &p_name, const StringName &p_theme_type) {
+ 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.");
+ ERR_FAIL_COND_MSG(!style_map[p_theme_type].has(p_name), "Cannot clear the stylebox '" + String(p_name) + "' because it does not exist.");
+
+ if (style_map[p_theme_type][p_name].is_valid()) {
+ style_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ }
+
+ style_map[p_theme_type].erase(p_name);
+
+ _emit_theme_changed();
+}
+
+void Theme::get_stylebox_list(StringName p_theme_type, List *p_list) const {
+ ERR_FAIL_NULL(p_list);
+
+ if (!style_map.has(p_theme_type)) {
+ return;
+ }
+
+ const StringName *key = nullptr;
+
+ while ((key = style_map[p_theme_type].next(key))) {
+ p_list->push_back(*key);
+ }
+}
+
+void Theme::add_stylebox_type(const StringName &p_theme_type) {
+ if (style_map.has(p_theme_type)) {
+ return;
+ }
+ style_map[p_theme_type] = HashMap>();
+}
+
+void Theme::get_stylebox_type_list(List *p_list) const {
+ ERR_FAIL_NULL(p_list);
+
+ const StringName *key = nullptr;
+ while ((key = style_map.next(key))) {
+ p_list->push_back(*key);
+ }
+}
+
+// Fonts.
+void Theme::set_font(const StringName &p_name, const StringName &p_theme_type, const Ref &p_font) {
+ if (font_map[p_theme_type][p_name].is_valid()) {
+ font_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ }
+
+ font_map[p_theme_type][p_name] = p_font;
+
+ if (p_font.is_valid()) {
+ font_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ }
+
+ _emit_theme_changed();
+}
+
+Ref Theme::get_font(const StringName &p_name, const StringName &p_theme_type) const {
+ if (font_map.has(p_theme_type) && font_map[p_theme_type].has(p_name) && font_map[p_theme_type][p_name].is_valid()) {
+ return font_map[p_theme_type][p_name];
+ } else if (has_default_theme_font()) {
+ return default_theme_font;
+ } else {
+ return default_font;
+ }
+}
+
+bool Theme::has_font(const StringName &p_name, const StringName &p_theme_type) const {
+ 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_theme_font());
+}
+
+bool Theme::has_font_nocheck(const StringName &p_name, const StringName &p_theme_type) const {
+ return (font_map.has(p_theme_type) && font_map[p_theme_type].has(p_name));
+}
+
+void Theme::rename_font(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
+ 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.");
+ 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.");
+ 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.");
+
+ font_map[p_theme_type][p_name] = font_map[p_theme_type][p_old_name];
+ font_map[p_theme_type].erase(p_old_name);
+
+ _emit_theme_changed();
+}
+
+void Theme::clear_font(const StringName &p_name, const StringName &p_theme_type) {
+ 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.");
+ ERR_FAIL_COND_MSG(!font_map[p_theme_type].has(p_name), "Cannot clear the font '" + String(p_name) + "' because it does not exist.");
+
+ if (font_map[p_theme_type][p_name].is_valid()) {
+ font_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ }
+
+ font_map[p_theme_type].erase(p_name);
+
+ _emit_theme_changed();
+}
+
+void Theme::get_font_list(StringName p_theme_type, List *p_list) const {
+ ERR_FAIL_NULL(p_list);
+
+ if (!font_map.has(p_theme_type)) {
+ return;
+ }
+
+ const StringName *key = nullptr;
+
+ while ((key = font_map[p_theme_type].next(key))) {
+ p_list->push_back(*key);
+ }
+}
+
+void Theme::add_font_type(const StringName &p_theme_type) {
+ if (font_map.has(p_theme_type)) {
+ return;
+ }
+ font_map[p_theme_type] = HashMap>();
+}
+
+void Theme::get_font_type_list(List *p_list) const {
+ ERR_FAIL_NULL(p_list);
+
+ const StringName *key = nullptr;
+ while ((key = font_map.next(key))) {
+ p_list->push_back(*key);
+ }
+}
+
+// Font sizes.
+void Theme::set_font_size(const StringName &p_name, const StringName &p_theme_type, int p_font_size) {
+ font_size_map[p_theme_type][p_name] = p_font_size;
+
+ _emit_theme_changed();
+}
+
+int Theme::get_font_size(const StringName &p_name, const StringName &p_theme_type) const {
+ 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)) {
+ return font_size_map[p_theme_type][p_name];
+ } else if (has_default_theme_font_size()) {
+ return default_theme_font_size;
+ } else {
+ return default_font_size;
+ }
+}
+
+bool Theme::has_font_size(const StringName &p_name, const StringName &p_theme_type) const {
+ 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_theme_font_size());
+}
+
+bool Theme::has_font_size_nocheck(const StringName &p_name, const StringName &p_theme_type) const {
+ return (font_size_map.has(p_theme_type) && font_size_map[p_theme_type].has(p_name));
+}
+
+void Theme::rename_font_size(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
+ 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.");
+ 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.");
+ 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.");
+
+ font_size_map[p_theme_type][p_name] = font_size_map[p_theme_type][p_old_name];
+ font_size_map[p_theme_type].erase(p_old_name);
+
+ _emit_theme_changed();
+}
+
+void Theme::clear_font_size(const StringName &p_name, const StringName &p_theme_type) {
+ 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.");
+ 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.");
+
+ font_size_map[p_theme_type].erase(p_name);
+
+ _emit_theme_changed();
+}
+
+void Theme::get_font_size_list(StringName p_theme_type, List *p_list) const {
+ ERR_FAIL_NULL(p_list);
+
+ if (!font_size_map.has(p_theme_type)) {
+ return;
+ }
+
+ const StringName *key = nullptr;
+
+ while ((key = font_size_map[p_theme_type].next(key))) {
+ p_list->push_back(*key);
+ }
+}
+
+void Theme::add_font_size_type(const StringName &p_theme_type) {
+ if (font_size_map.has(p_theme_type)) {
+ return;
+ }
+ font_size_map[p_theme_type] = HashMap();
+}
+
+void Theme::get_font_size_type_list(List *p_list) const {
+ ERR_FAIL_NULL(p_list);
+
+ const StringName *key = nullptr;
+ while ((key = font_size_map.next(key))) {
+ p_list->push_back(*key);
+ }
+}
+
+// Colors.
+void Theme::set_color(const StringName &p_name, const StringName &p_theme_type, const Color &p_color) {
+ color_map[p_theme_type][p_name] = p_color;
+
+ _emit_theme_changed();
+}
+
+Color Theme::get_color(const StringName &p_name, const StringName &p_theme_type) const {
+ if (color_map.has(p_theme_type) && color_map[p_theme_type].has(p_name)) {
+ return color_map[p_theme_type][p_name];
+ } else {
+ return Color();
+ }
+}
+
+bool Theme::has_color(const StringName &p_name, const StringName &p_theme_type) const {
+ return (color_map.has(p_theme_type) && color_map[p_theme_type].has(p_name));
+}
+
+bool Theme::has_color_nocheck(const StringName &p_name, const StringName &p_theme_type) const {
+ return (color_map.has(p_theme_type) && color_map[p_theme_type].has(p_name));
+}
+
+void Theme::rename_color(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
+ 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.");
+ 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.");
+ 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.");
+
+ color_map[p_theme_type][p_name] = color_map[p_theme_type][p_old_name];
+ color_map[p_theme_type].erase(p_old_name);
+
+ _emit_theme_changed();
+}
+
+void Theme::clear_color(const StringName &p_name, const StringName &p_theme_type) {
+ 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.");
+ ERR_FAIL_COND_MSG(!color_map[p_theme_type].has(p_name), "Cannot clear the color '" + String(p_name) + "' because it does not exist.");
+
+ color_map[p_theme_type].erase(p_name);
+
+ _emit_theme_changed();
+}
+
+void Theme::get_color_list(StringName p_theme_type, List *p_list) const {
+ ERR_FAIL_NULL(p_list);
+
+ if (!color_map.has(p_theme_type)) {
+ return;
+ }
+
+ const StringName *key = nullptr;
+
+ while ((key = color_map[p_theme_type].next(key))) {
+ p_list->push_back(*key);
+ }
+}
+
+void Theme::add_color_type(const StringName &p_theme_type) {
+ if (color_map.has(p_theme_type)) {
+ return;
+ }
+ color_map[p_theme_type] = HashMap();
+}
+
+void Theme::get_color_type_list(List *p_list) const {
+ ERR_FAIL_NULL(p_list);
+
+ const StringName *key = nullptr;
+ while ((key = color_map.next(key))) {
+ p_list->push_back(*key);
+ }
+}
+
+// Theme constants.
+void Theme::set_constant(const StringName &p_name, const StringName &p_theme_type, int p_constant) {
+ constant_map[p_theme_type][p_name] = p_constant;
+
+ _emit_theme_changed();
+}
+
+int Theme::get_constant(const StringName &p_name, const StringName &p_theme_type) const {
+ if (constant_map.has(p_theme_type) && constant_map[p_theme_type].has(p_name)) {
+ return constant_map[p_theme_type][p_name];
+ } else {
+ return 0;
+ }
+}
+
+bool Theme::has_constant(const StringName &p_name, const StringName &p_theme_type) const {
+ return (constant_map.has(p_theme_type) && constant_map[p_theme_type].has(p_name));
+}
+
+bool Theme::has_constant_nocheck(const StringName &p_name, const StringName &p_theme_type) const {
+ return (constant_map.has(p_theme_type) && constant_map[p_theme_type].has(p_name));
+}
+
+void Theme::rename_constant(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
+ 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.");
+ 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.");
+ 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.");
+
+ constant_map[p_theme_type][p_name] = constant_map[p_theme_type][p_old_name];
+ constant_map[p_theme_type].erase(p_old_name);
+
+ _emit_theme_changed();
+}
+
+void Theme::clear_constant(const StringName &p_name, const StringName &p_theme_type) {
+ 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.");
+ ERR_FAIL_COND_MSG(!constant_map[p_theme_type].has(p_name), "Cannot clear the constant '" + String(p_name) + "' because it does not exist.");
+
+ constant_map[p_theme_type].erase(p_name);
+
+ _emit_theme_changed();
+}
+
+void Theme::get_constant_list(StringName p_theme_type, List *p_list) const {
+ ERR_FAIL_NULL(p_list);
+
+ if (!constant_map.has(p_theme_type)) {
+ return;
+ }
+
+ const StringName *key = nullptr;
+
+ while ((key = constant_map[p_theme_type].next(key))) {
+ p_list->push_back(*key);
+ }
+}
+
+void Theme::add_constant_type(const StringName &p_theme_type) {
+ if (constant_map.has(p_theme_type)) {
+ return;
+ }
+ constant_map[p_theme_type] = HashMap();
+}
+
+void Theme::get_constant_type_list(List *p_list) const {
+ ERR_FAIL_NULL(p_list);
+
+ const StringName *key = nullptr;
+ while ((key = constant_map.next(key))) {
+ p_list->push_back(*key);
+ }
+}
+
+// Generic methods for managing theme items.
+void Theme::set_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type, const Variant &p_value) {
+ switch (p_data_type) {
+ case DATA_TYPE_COLOR: {
+ 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()) + ").");
+
+ Color color_value = p_value;
+ set_color(p_name, p_theme_type, color_value);
+ } break;
+ case DATA_TYPE_CONSTANT: {
+ 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()) + ").");
+
+ int constant_value = p_value;
+ set_constant(p_name, p_theme_type, constant_value);
+ } break;
+ case DATA_TYPE_FONT: {
+ 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()) + ").");
+
+ Ref font_value = Object::cast_to(p_value.get_validated_object());
+ set_font(p_name, p_theme_type, font_value);
+ } break;
+ case DATA_TYPE_FONT_SIZE: {
+ 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()) + ").");
+
+ int font_size_value = p_value;
+ set_font_size(p_name, p_theme_type, font_size_value);
+ } break;
+ case DATA_TYPE_ICON: {
+ 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()) + ").");
+
+ Ref icon_value = Object::cast_to(p_value.get_validated_object());
+ set_icon(p_name, p_theme_type, icon_value);
+ } break;
+ case DATA_TYPE_STYLEBOX: {
+ 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()) + ").");
+
+ Ref stylebox_value = Object::cast_to(p_value.get_validated_object());
+ set_stylebox(p_name, p_theme_type, stylebox_value);
+ } break;
+ case DATA_TYPE_MAX:
+ break; // Can't happen, but silences warning.
+ }
+}
+
+Variant Theme::get_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type) const {
+ switch (p_data_type) {
+ case DATA_TYPE_COLOR:
+ return get_color(p_name, p_theme_type);
+ case DATA_TYPE_CONSTANT:
+ return get_constant(p_name, p_theme_type);
+ case DATA_TYPE_FONT:
+ return get_font(p_name, p_theme_type);
+ case DATA_TYPE_FONT_SIZE:
+ return get_font_size(p_name, p_theme_type);
+ case DATA_TYPE_ICON:
+ return get_icon(p_name, p_theme_type);
+ case DATA_TYPE_STYLEBOX:
+ return get_stylebox(p_name, p_theme_type);
+ case DATA_TYPE_MAX:
+ break; // Can't happen, but silences warning.
+ }
+
+ return Variant();
+}
+
+bool Theme::has_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type) const {
+ switch (p_data_type) {
+ case DATA_TYPE_COLOR:
+ return has_color(p_name, p_theme_type);
+ case DATA_TYPE_CONSTANT:
+ return has_constant(p_name, p_theme_type);
+ case DATA_TYPE_FONT:
+ return has_font(p_name, p_theme_type);
+ case DATA_TYPE_FONT_SIZE:
+ return has_font_size(p_name, p_theme_type);
+ case DATA_TYPE_ICON:
+ return has_icon(p_name, p_theme_type);
+ case DATA_TYPE_STYLEBOX:
+ return has_stylebox(p_name, p_theme_type);
+ case DATA_TYPE_MAX:
+ break; // Can't happen, but silences warning.
+ }
+
+ return false;
+}
+
+bool Theme::has_theme_item_nocheck(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type) const {
+ switch (p_data_type) {
+ case DATA_TYPE_COLOR:
+ return has_color_nocheck(p_name, p_theme_type);
+ case DATA_TYPE_CONSTANT:
+ return has_constant_nocheck(p_name, p_theme_type);
+ case DATA_TYPE_FONT:
+ return has_font_nocheck(p_name, p_theme_type);
+ case DATA_TYPE_FONT_SIZE:
+ return has_font_size_nocheck(p_name, p_theme_type);
+ case DATA_TYPE_ICON:
+ return has_icon_nocheck(p_name, p_theme_type);
+ case DATA_TYPE_STYLEBOX:
+ return has_stylebox_nocheck(p_name, p_theme_type);
+ case DATA_TYPE_MAX:
+ break; // Can't happen, but silences warning.
+ }
+
+ return false;
+}
+
+void Theme::rename_theme_item(DataType p_data_type, const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
+ switch (p_data_type) {
+ case DATA_TYPE_COLOR:
+ rename_color(p_old_name, p_name, p_theme_type);
+ break;
+ case DATA_TYPE_CONSTANT:
+ rename_constant(p_old_name, p_name, p_theme_type);
+ break;
+ case DATA_TYPE_FONT:
+ rename_font(p_old_name, p_name, p_theme_type);
+ break;
+ case DATA_TYPE_FONT_SIZE:
+ rename_font_size(p_old_name, p_name, p_theme_type);
+ break;
+ case DATA_TYPE_ICON:
+ rename_icon(p_old_name, p_name, p_theme_type);
+ break;
+ case DATA_TYPE_STYLEBOX:
+ rename_stylebox(p_old_name, p_name, p_theme_type);
+ break;
+ case DATA_TYPE_MAX:
+ break; // Can't happen, but silences warning.
+ }
+}
+
+void Theme::clear_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type) {
+ switch (p_data_type) {
+ case DATA_TYPE_COLOR:
+ clear_color(p_name, p_theme_type);
+ break;
+ case DATA_TYPE_CONSTANT:
+ clear_constant(p_name, p_theme_type);
+ break;
+ case DATA_TYPE_FONT:
+ clear_font(p_name, p_theme_type);
+ break;
+ case DATA_TYPE_FONT_SIZE:
+ clear_font_size(p_name, p_theme_type);
+ break;
+ case DATA_TYPE_ICON:
+ clear_icon(p_name, p_theme_type);
+ break;
+ case DATA_TYPE_STYLEBOX:
+ clear_stylebox(p_name, p_theme_type);
+ break;
+ case DATA_TYPE_MAX:
+ break; // Can't happen, but silences warning.
+ }
+}
+
+void Theme::get_theme_item_list(DataType p_data_type, StringName p_theme_type, List *p_list) const {
+ switch (p_data_type) {
+ case DATA_TYPE_COLOR:
+ get_color_list(p_theme_type, p_list);
+ break;
+ case DATA_TYPE_CONSTANT:
+ get_constant_list(p_theme_type, p_list);
+ break;
+ case DATA_TYPE_FONT:
+ get_font_list(p_theme_type, p_list);
+ break;
+ case DATA_TYPE_FONT_SIZE:
+ get_font_size_list(p_theme_type, p_list);
+ break;
+ case DATA_TYPE_ICON:
+ get_icon_list(p_theme_type, p_list);
+ break;
+ case DATA_TYPE_STYLEBOX:
+ get_stylebox_list(p_theme_type, p_list);
+ break;
+ case DATA_TYPE_MAX:
+ break; // Can't happen, but silences warning.
+ }
+}
+
+void Theme::add_theme_item_type(DataType p_data_type, const StringName &p_theme_type) {
+ switch (p_data_type) {
+ case DATA_TYPE_COLOR:
+ add_color_type(p_theme_type);
+ break;
+ case DATA_TYPE_CONSTANT:
+ add_constant_type(p_theme_type);
+ break;
+ case DATA_TYPE_FONT:
+ add_font_type(p_theme_type);
+ break;
+ case DATA_TYPE_FONT_SIZE:
+ add_font_size_type(p_theme_type);
+ break;
+ case DATA_TYPE_ICON:
+ add_icon_type(p_theme_type);
+ break;
+ case DATA_TYPE_STYLEBOX:
+ add_stylebox_type(p_theme_type);
+ break;
+ case DATA_TYPE_MAX:
+ break; // Can't happen, but silences warning.
+ }
+}
+
+void Theme::get_theme_item_type_list(DataType p_data_type, List *p_list) const {
+ switch (p_data_type) {
+ case DATA_TYPE_COLOR:
+ get_color_type_list(p_list);
+ break;
+ case DATA_TYPE_CONSTANT:
+ get_constant_type_list(p_list);
+ break;
+ case DATA_TYPE_FONT:
+ get_font_type_list(p_list);
+ break;
+ case DATA_TYPE_FONT_SIZE:
+ get_font_size_type_list(p_list);
+ break;
+ case DATA_TYPE_ICON:
+ get_icon_type_list(p_list);
+ break;
+ case DATA_TYPE_STYLEBOX:
+ get_stylebox_type_list(p_list);
+ break;
+ case DATA_TYPE_MAX:
+ break; // Can't happen, but silences warning.
+ }
+}
+
+// Theme type variations.
+void Theme::set_type_variation(const StringName &p_theme_type, const StringName &p_base_type) {
+ ERR_FAIL_COND_MSG(p_theme_type == StringName(), "An empty theme type cannot be marked as a variation of another type.");
+ 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.");
+ 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.");
+
+ if (variation_map.has(p_theme_type)) {
+ StringName old_base = variation_map[p_theme_type];
+ variation_base_map[old_base].erase(p_theme_type);
+ }
+
+ variation_map[p_theme_type] = p_base_type;
+ variation_base_map[p_base_type].push_back(p_theme_type);
+
+ _emit_theme_changed();
+}
+
+bool Theme::is_type_variation(const StringName &p_theme_type, const StringName &p_base_type) const {
+ return (variation_map.has(p_theme_type) && variation_map[p_theme_type] == p_base_type);
+}
+
+void Theme::clear_type_variation(const StringName &p_theme_type) {
+ ERR_FAIL_COND_MSG(!variation_map.has(p_theme_type), "Cannot clear the type variation '" + String(p_theme_type) + "' because it does not exist.");
+
+ StringName base_type = variation_map[p_theme_type];
+ variation_base_map[base_type].erase(p_theme_type);
+ variation_map.erase(p_theme_type);
+
+ _emit_theme_changed();
+}
+
+StringName Theme::get_type_variation_base(const StringName &p_theme_type) const {
+ if (!variation_map.has(p_theme_type)) {
+ return StringName();
+ }
+
+ return variation_map[p_theme_type];
+}
+
+void Theme::get_type_variation_list(const StringName &p_base_type, List *p_list) const {
+ ERR_FAIL_NULL(p_list);
+
+ if (!variation_base_map.has(p_base_type)) {
+ return;
+ }
+
+ for (const StringName &E : variation_base_map[p_base_type]) {
+ // Prevent infinite loops if variants were set to be cross-dependent (that's still invalid usage, but handling for stability sake).
+ if (p_list->find(E)) {
+ continue;
+ }
+
+ p_list->push_back(E);
+ // Continue looking for sub-variations.
+ get_type_variation_list(E, p_list);
+ }
+}
+
+// Theme types.
+void Theme::get_type_list(List *p_list) const {
+ ERR_FAIL_NULL(p_list);
+
+ Set types;
+ const StringName *key = nullptr;
+
+ // Icons.
+ while ((key = icon_map.next(key))) {
+ types.insert(*key);
+ }
+
+ key = nullptr;
+
+ // StyleBoxes.
+ while ((key = style_map.next(key))) {
+ types.insert(*key);
+ }
+
+ key = nullptr;
+
+ // Fonts.
+ while ((key = font_map.next(key))) {
+ types.insert(*key);
+ }
+
+ key = nullptr;
+
+ // Font sizes.
+ while ((key = font_size_map.next(key))) {
+ types.insert(*key);
+ }
+
+ key = nullptr;
+
+ // Colors.
+ while ((key = color_map.next(key))) {
+ types.insert(*key);
+ }
+
+ key = nullptr;
+
+ // Constants.
+ while ((key = constant_map.next(key))) {
+ types.insert(*key);
+ }
+
+ for (Set::Element *E = types.front(); E; E = E->next()) {
+ p_list->push_back(E->get());
+ }
+}
+
+void Theme::get_type_dependencies(const StringName &p_base_type, const StringName &p_type_variation, List *p_list) {
+ ERR_FAIL_NULL(p_list);
+
+ // Build the dependency chain for type variations.
+ if (p_type_variation != StringName()) {
+ StringName variation_name = p_type_variation;
+ while (variation_name != StringName()) {
+ p_list->push_back(variation_name);
+ variation_name = get_type_variation_base(variation_name);
+
+ // If we have reached the base type dependency, it's safe to stop (assuming no funny business was done to the Theme).
+ if (variation_name == p_base_type) {
+ break;
+ }
+ }
+ }
+
+ // Continue building the chain using native class hierarchy.
+ StringName class_name = p_base_type;
+ while (class_name != StringName()) {
+ p_list->push_back(class_name);
+ class_name = ClassDB::get_parent_class_nocheck(class_name);
+ }
+}
+
+// Internal methods for getting lists as a Vector of String (compatible with public API).
Vector Theme::_get_icon_list(const String &p_theme_type) const {
Vector ilret;
List il;
@@ -293,998 +1401,14 @@ Vector Theme::_get_type_list() const {
return ilret;
}
-bool Theme::_set(const StringName &p_name, const Variant &p_value) {
- String sname = p_name;
-
- if (sname.find("/") != -1) {
- String type = sname.get_slicec('/', 1);
- String theme_type = sname.get_slicec('/', 0);
- String name = sname.get_slicec('/', 2);
-
- if (type == "icons") {
- set_icon(name, theme_type, p_value);
- } else if (type == "styles") {
- set_stylebox(name, theme_type, p_value);
- } else if (type == "fonts") {
- set_font(name, theme_type, p_value);
- } else if (type == "font_sizes") {
- set_font_size(name, theme_type, p_value);
- } else if (type == "colors") {
- set_color(name, theme_type, p_value);
- } else if (type == "constants") {
- set_constant(name, theme_type, p_value);
- } else if (type == "base_type") {
- set_type_variation(theme_type, p_value);
- } else {
- return false;
- }
-
- return true;
- }
-
- return false;
-}
-
-bool Theme::_get(const StringName &p_name, Variant &r_ret) const {
- String sname = p_name;
-
- if (sname.find("/") != -1) {
- String type = sname.get_slicec('/', 1);
- String theme_type = sname.get_slicec('/', 0);
- String name = sname.get_slicec('/', 2);
-
- if (type == "icons") {
- if (!has_icon(name, theme_type)) {
- r_ret = Ref();
- } else {
- r_ret = get_icon(name, theme_type);
- }
- } else if (type == "styles") {
- if (!has_stylebox(name, theme_type)) {
- r_ret = Ref();
- } else {
- r_ret = get_stylebox(name, theme_type);
- }
- } else if (type == "fonts") {
- if (!has_font(name, theme_type)) {
- r_ret = Ref();
- } else {
- r_ret = get_font(name, theme_type);
- }
- } else if (type == "font_sizes") {
- r_ret = get_font_size(name, theme_type);
- } else if (type == "colors") {
- r_ret = get_color(name, theme_type);
- } else if (type == "constants") {
- r_ret = get_constant(name, theme_type);
- } else if (type == "base_type") {
- r_ret = get_type_variation_base(theme_type);
- } else {
- return false;
- }
-
- return true;
- }
-
- return false;
-}
-
-void Theme::_get_property_list(List *p_list) const {
- List list;
-
- const StringName *key = nullptr;
-
- // Type variations.
- while ((key = variation_map.next(key))) {
- list.push_back(PropertyInfo(Variant::STRING_NAME, String() + *key + "/base_type"));
- }
-
- key = nullptr;
-
- // Icons.
- while ((key = icon_map.next(key))) {
- const StringName *key2 = nullptr;
-
- while ((key2 = icon_map[*key].next(key2))) {
- list.push_back(PropertyInfo(Variant::OBJECT, String() + *key + "/icons/" + *key2, PROPERTY_HINT_RESOURCE_TYPE, "Texture2D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL));
- }
- }
-
- key = nullptr;
-
- // Styles.
- while ((key = style_map.next(key))) {
- const StringName *key2 = nullptr;
-
- while ((key2 = style_map[*key].next(key2))) {
- list.push_back(PropertyInfo(Variant::OBJECT, String() + *key + "/styles/" + *key2, PROPERTY_HINT_RESOURCE_TYPE, "StyleBox", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL));
- }
- }
-
- key = nullptr;
-
- // Fonts.
- while ((key = font_map.next(key))) {
- const StringName *key2 = nullptr;
-
- while ((key2 = font_map[*key].next(key2))) {
- list.push_back(PropertyInfo(Variant::OBJECT, String() + *key + "/fonts/" + *key2, PROPERTY_HINT_RESOURCE_TYPE, "Font", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL));
- }
- }
-
- key = nullptr;
-
- // Font sizes.
- while ((key = font_size_map.next(key))) {
- const StringName *key2 = nullptr;
-
- while ((key2 = font_size_map[*key].next(key2))) {
- list.push_back(PropertyInfo(Variant::INT, String() + *key + "/font_sizes/" + *key2));
- }
- }
-
- key = nullptr;
-
- // Colors.
- while ((key = color_map.next(key))) {
- const StringName *key2 = nullptr;
-
- while ((key2 = color_map[*key].next(key2))) {
- list.push_back(PropertyInfo(Variant::COLOR, String() + *key + "/colors/" + *key2));
- }
- }
-
- key = nullptr;
-
- // Constants.
- while ((key = constant_map.next(key))) {
- const StringName *key2 = nullptr;
-
- while ((key2 = constant_map[*key].next(key2))) {
- list.push_back(PropertyInfo(Variant::INT, String() + *key + "/constants/" + *key2));
- }
- }
-
- // Sort and store properties.
- list.sort();
- for (const PropertyInfo &E : list) {
- p_list->push_back(E);
- }
-}
-
-void Theme::set_default_theme_font(const Ref &p_default_font) {
- if (default_theme_font == p_default_font) {
+// Theme bulk manipulations.
+void Theme::_emit_theme_changed() {
+ if (no_change_propagation) {
return;
}
- if (default_theme_font.is_valid()) {
- default_theme_font->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
- }
-
- default_theme_font = p_default_font;
-
- if (default_theme_font.is_valid()) {
- default_theme_font->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED);
- }
-
- _emit_theme_changed();
-}
-
-Ref Theme::get_default_theme_font() const {
- return default_theme_font;
-}
-
-void Theme::set_default_theme_font_size(int p_font_size) {
- if (default_theme_font_size == p_font_size) {
- return;
- }
-
- default_theme_font_size = p_font_size;
-
- _emit_theme_changed();
-}
-
-int Theme::get_default_theme_font_size() const {
- return default_theme_font_size;
-}
-
-Ref Theme::project_default_theme;
-Ref Theme::default_theme;
-Ref Theme::default_icon;
-Ref Theme::default_style;
-Ref Theme::default_font;
-int Theme::default_font_size = 16;
-
-Ref Theme::get_default() {
- return default_theme;
-}
-
-void Theme::set_default(const Ref &p_default) {
- default_theme = p_default;
-}
-
-Ref Theme::get_project_default() {
- return project_default_theme;
-}
-
-void Theme::set_project_default(const Ref &p_project_default) {
- project_default_theme = p_project_default;
-}
-
-void Theme::set_default_icon(const Ref &p_icon) {
- default_icon = p_icon;
-}
-
-void Theme::set_default_style(const Ref &p_style) {
- default_style = p_style;
-}
-
-void Theme::set_default_font(const Ref &p_font) {
- default_font = p_font;
-}
-
-void Theme::set_default_font_size(int p_font_size) {
- default_font_size = p_font_size;
-}
-
-void Theme::set_icon(const StringName &p_name, const StringName &p_theme_type, const Ref &p_icon) {
- if (icon_map[p_theme_type].has(p_name) && icon_map[p_theme_type][p_name].is_valid()) {
- icon_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
- }
-
- icon_map[p_theme_type][p_name] = p_icon;
-
- if (p_icon.is_valid()) {
- icon_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED);
- }
-
- _emit_theme_changed();
-}
-
-Ref Theme::get_icon(const StringName &p_name, const StringName &p_theme_type) const {
- if (icon_map.has(p_theme_type) && icon_map[p_theme_type].has(p_name) && icon_map[p_theme_type][p_name].is_valid()) {
- return icon_map[p_theme_type][p_name];
- } else {
- return default_icon;
- }
-}
-
-bool Theme::has_icon(const StringName &p_name, const StringName &p_theme_type) const {
- return (icon_map.has(p_theme_type) && icon_map[p_theme_type].has(p_name) && icon_map[p_theme_type][p_name].is_valid());
-}
-
-bool Theme::has_icon_nocheck(const StringName &p_name, const StringName &p_theme_type) const {
- return (icon_map.has(p_theme_type) && icon_map[p_theme_type].has(p_name));
-}
-
-void Theme::rename_icon(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
- 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.");
- 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.");
- 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.");
-
- icon_map[p_theme_type][p_name] = icon_map[p_theme_type][p_old_name];
- icon_map[p_theme_type].erase(p_old_name);
-
- _emit_theme_changed();
-}
-
-void Theme::clear_icon(const StringName &p_name, const StringName &p_theme_type) {
- 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.");
- ERR_FAIL_COND_MSG(!icon_map[p_theme_type].has(p_name), "Cannot clear the icon '" + String(p_name) + "' because it does not exist.");
-
- if (icon_map[p_theme_type][p_name].is_valid()) {
- icon_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
- }
-
- icon_map[p_theme_type].erase(p_name);
-
- _emit_theme_changed();
-}
-
-void Theme::get_icon_list(StringName p_theme_type, List *p_list) const {
- ERR_FAIL_NULL(p_list);
-
- if (!icon_map.has(p_theme_type)) {
- return;
- }
-
- const StringName *key = nullptr;
-
- while ((key = icon_map[p_theme_type].next(key))) {
- p_list->push_back(*key);
- }
-}
-
-void Theme::add_icon_type(const StringName &p_theme_type) {
- if (icon_map.has(p_theme_type)) {
- return;
- }
- icon_map[p_theme_type] = HashMap>();
-}
-
-void Theme::get_icon_type_list(List *p_list) const {
- ERR_FAIL_NULL(p_list);
-
- const StringName *key = nullptr;
- while ((key = icon_map.next(key))) {
- p_list->push_back(*key);
- }
-}
-
-void Theme::set_stylebox(const StringName &p_name, const StringName &p_theme_type, const Ref &p_style) {
- if (style_map[p_theme_type].has(p_name) && style_map[p_theme_type][p_name].is_valid()) {
- style_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
- }
-
- style_map[p_theme_type][p_name] = p_style;
-
- if (p_style.is_valid()) {
- style_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED);
- }
-
- _emit_theme_changed();
-}
-
-Ref Theme::get_stylebox(const StringName &p_name, const StringName &p_theme_type) const {
- if (style_map.has(p_theme_type) && style_map[p_theme_type].has(p_name) && style_map[p_theme_type][p_name].is_valid()) {
- return style_map[p_theme_type][p_name];
- } else {
- return default_style;
- }
-}
-
-bool Theme::has_stylebox(const StringName &p_name, const StringName &p_theme_type) const {
- return (style_map.has(p_theme_type) && style_map[p_theme_type].has(p_name) && style_map[p_theme_type][p_name].is_valid());
-}
-
-bool Theme::has_stylebox_nocheck(const StringName &p_name, const StringName &p_theme_type) const {
- return (style_map.has(p_theme_type) && style_map[p_theme_type].has(p_name));
-}
-
-void Theme::rename_stylebox(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
- 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.");
- 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.");
- 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.");
-
- style_map[p_theme_type][p_name] = style_map[p_theme_type][p_old_name];
- style_map[p_theme_type].erase(p_old_name);
-
- _emit_theme_changed();
-}
-
-void Theme::clear_stylebox(const StringName &p_name, const StringName &p_theme_type) {
- 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.");
- ERR_FAIL_COND_MSG(!style_map[p_theme_type].has(p_name), "Cannot clear the stylebox '" + String(p_name) + "' because it does not exist.");
-
- if (style_map[p_theme_type][p_name].is_valid()) {
- style_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
- }
-
- style_map[p_theme_type].erase(p_name);
-
- _emit_theme_changed();
-}
-
-void Theme::get_stylebox_list(StringName p_theme_type, List *p_list) const {
- ERR_FAIL_NULL(p_list);
-
- if (!style_map.has(p_theme_type)) {
- return;
- }
-
- const StringName *key = nullptr;
-
- while ((key = style_map[p_theme_type].next(key))) {
- p_list->push_back(*key);
- }
-}
-
-void Theme::add_stylebox_type(const StringName &p_theme_type) {
- if (style_map.has(p_theme_type)) {
- return;
- }
- style_map[p_theme_type] = HashMap>();
-}
-
-void Theme::get_stylebox_type_list(List *p_list) const {
- ERR_FAIL_NULL(p_list);
-
- const StringName *key = nullptr;
- while ((key = style_map.next(key))) {
- p_list->push_back(*key);
- }
-}
-
-void Theme::set_font(const StringName &p_name, const StringName &p_theme_type, const Ref &p_font) {
- if (font_map[p_theme_type][p_name].is_valid()) {
- font_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
- }
-
- font_map[p_theme_type][p_name] = p_font;
-
- if (p_font.is_valid()) {
- font_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED);
- }
-
- _emit_theme_changed();
-}
-
-Ref Theme::get_font(const StringName &p_name, const StringName &p_theme_type) const {
- if (font_map.has(p_theme_type) && font_map[p_theme_type].has(p_name) && font_map[p_theme_type][p_name].is_valid()) {
- return font_map[p_theme_type][p_name];
- } else if (default_theme_font.is_valid()) {
- return default_theme_font;
- } else {
- return default_font;
- }
-}
-
-bool Theme::has_font(const StringName &p_name, const StringName &p_theme_type) const {
- return ((font_map.has(p_theme_type) && font_map[p_theme_type].has(p_name) && font_map[p_theme_type][p_name].is_valid()) || default_theme_font.is_valid());
-}
-
-bool Theme::has_font_nocheck(const StringName &p_name, const StringName &p_theme_type) const {
- return (font_map.has(p_theme_type) && font_map[p_theme_type].has(p_name));
-}
-
-void Theme::rename_font(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
- 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.");
- 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.");
- 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.");
-
- font_map[p_theme_type][p_name] = font_map[p_theme_type][p_old_name];
- font_map[p_theme_type].erase(p_old_name);
-
- _emit_theme_changed();
-}
-
-void Theme::clear_font(const StringName &p_name, const StringName &p_theme_type) {
- 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.");
- ERR_FAIL_COND_MSG(!font_map[p_theme_type].has(p_name), "Cannot clear the font '" + String(p_name) + "' because it does not exist.");
-
- if (font_map[p_theme_type][p_name].is_valid()) {
- font_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
- }
-
- font_map[p_theme_type].erase(p_name);
-
- _emit_theme_changed();
-}
-
-void Theme::get_font_list(StringName p_theme_type, List *p_list) const {
- ERR_FAIL_NULL(p_list);
-
- if (!font_map.has(p_theme_type)) {
- return;
- }
-
- const StringName *key = nullptr;
-
- while ((key = font_map[p_theme_type].next(key))) {
- p_list->push_back(*key);
- }
-}
-
-void Theme::add_font_type(const StringName &p_theme_type) {
- if (font_map.has(p_theme_type)) {
- return;
- }
- font_map[p_theme_type] = HashMap>();
-}
-
-void Theme::get_font_type_list(List *p_list) const {
- ERR_FAIL_NULL(p_list);
-
- const StringName *key = nullptr;
- while ((key = font_map.next(key))) {
- p_list->push_back(*key);
- }
-}
-
-void Theme::set_font_size(const StringName &p_name, const StringName &p_theme_type, int p_font_size) {
- font_size_map[p_theme_type][p_name] = p_font_size;
-
- _emit_theme_changed();
-}
-
-int Theme::get_font_size(const StringName &p_name, const StringName &p_theme_type) const {
- 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)) {
- return font_size_map[p_theme_type][p_name];
- } else if (default_theme_font_size > 0) {
- return default_theme_font_size;
- } else {
- return default_font_size;
- }
-}
-
-bool Theme::has_font_size(const StringName &p_name, const StringName &p_theme_type) const {
- 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)) || (default_theme_font_size > 0));
-}
-
-bool Theme::has_font_size_nocheck(const StringName &p_name, const StringName &p_theme_type) const {
- return (font_size_map.has(p_theme_type) && font_size_map[p_theme_type].has(p_name));
-}
-
-void Theme::rename_font_size(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
- 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.");
- 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.");
- 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.");
-
- font_size_map[p_theme_type][p_name] = font_size_map[p_theme_type][p_old_name];
- font_size_map[p_theme_type].erase(p_old_name);
-
- _emit_theme_changed();
-}
-
-void Theme::clear_font_size(const StringName &p_name, const StringName &p_theme_type) {
- 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.");
- 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.");
-
- font_size_map[p_theme_type].erase(p_name);
-
- _emit_theme_changed();
-}
-
-void Theme::get_font_size_list(StringName p_theme_type, List *p_list) const {
- ERR_FAIL_NULL(p_list);
-
- if (!font_size_map.has(p_theme_type)) {
- return;
- }
-
- const StringName *key = nullptr;
-
- while ((key = font_size_map[p_theme_type].next(key))) {
- p_list->push_back(*key);
- }
-}
-
-void Theme::add_font_size_type(const StringName &p_theme_type) {
- if (font_size_map.has(p_theme_type)) {
- return;
- }
- font_size_map[p_theme_type] = HashMap();
-}
-
-void Theme::get_font_size_type_list(List *p_list) const {
- ERR_FAIL_NULL(p_list);
-
- const StringName *key = nullptr;
- while ((key = font_size_map.next(key))) {
- p_list->push_back(*key);
- }
-}
-
-void Theme::set_color(const StringName &p_name, const StringName &p_theme_type, const Color &p_color) {
- color_map[p_theme_type][p_name] = p_color;
-
- _emit_theme_changed();
-}
-
-Color Theme::get_color(const StringName &p_name, const StringName &p_theme_type) const {
- if (color_map.has(p_theme_type) && color_map[p_theme_type].has(p_name)) {
- return color_map[p_theme_type][p_name];
- } else {
- return Color();
- }
-}
-
-bool Theme::has_color(const StringName &p_name, const StringName &p_theme_type) const {
- return (color_map.has(p_theme_type) && color_map[p_theme_type].has(p_name));
-}
-
-bool Theme::has_color_nocheck(const StringName &p_name, const StringName &p_theme_type) const {
- return (color_map.has(p_theme_type) && color_map[p_theme_type].has(p_name));
-}
-
-void Theme::rename_color(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
- 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.");
- 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.");
- 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.");
-
- color_map[p_theme_type][p_name] = color_map[p_theme_type][p_old_name];
- color_map[p_theme_type].erase(p_old_name);
-
- _emit_theme_changed();
-}
-
-void Theme::clear_color(const StringName &p_name, const StringName &p_theme_type) {
- 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.");
- ERR_FAIL_COND_MSG(!color_map[p_theme_type].has(p_name), "Cannot clear the color '" + String(p_name) + "' because it does not exist.");
-
- color_map[p_theme_type].erase(p_name);
-
- _emit_theme_changed();
-}
-
-void Theme::get_color_list(StringName p_theme_type, List *p_list) const {
- ERR_FAIL_NULL(p_list);
-
- if (!color_map.has(p_theme_type)) {
- return;
- }
-
- const StringName *key = nullptr;
-
- while ((key = color_map[p_theme_type].next(key))) {
- p_list->push_back(*key);
- }
-}
-
-void Theme::add_color_type(const StringName &p_theme_type) {
- if (color_map.has(p_theme_type)) {
- return;
- }
- color_map[p_theme_type] = HashMap();
-}
-
-void Theme::get_color_type_list(List *p_list) const {
- ERR_FAIL_NULL(p_list);
-
- const StringName *key = nullptr;
- while ((key = color_map.next(key))) {
- p_list->push_back(*key);
- }
-}
-
-void Theme::set_constant(const StringName &p_name, const StringName &p_theme_type, int p_constant) {
- constant_map[p_theme_type][p_name] = p_constant;
-
- _emit_theme_changed();
-}
-
-int Theme::get_constant(const StringName &p_name, const StringName &p_theme_type) const {
- if (constant_map.has(p_theme_type) && constant_map[p_theme_type].has(p_name)) {
- return constant_map[p_theme_type][p_name];
- } else {
- return 0;
- }
-}
-
-bool Theme::has_constant(const StringName &p_name, const StringName &p_theme_type) const {
- return (constant_map.has(p_theme_type) && constant_map[p_theme_type].has(p_name));
-}
-
-bool Theme::has_constant_nocheck(const StringName &p_name, const StringName &p_theme_type) const {
- return (constant_map.has(p_theme_type) && constant_map[p_theme_type].has(p_name));
-}
-
-void Theme::rename_constant(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
- 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.");
- 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.");
- 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.");
-
- constant_map[p_theme_type][p_name] = constant_map[p_theme_type][p_old_name];
- constant_map[p_theme_type].erase(p_old_name);
-
- _emit_theme_changed();
-}
-
-void Theme::clear_constant(const StringName &p_name, const StringName &p_theme_type) {
- 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.");
- ERR_FAIL_COND_MSG(!constant_map[p_theme_type].has(p_name), "Cannot clear the constant '" + String(p_name) + "' because it does not exist.");
-
- constant_map[p_theme_type].erase(p_name);
-
- _emit_theme_changed();
-}
-
-void Theme::get_constant_list(StringName p_theme_type, List *p_list) const {
- ERR_FAIL_NULL(p_list);
-
- if (!constant_map.has(p_theme_type)) {
- return;
- }
-
- const StringName *key = nullptr;
-
- while ((key = constant_map[p_theme_type].next(key))) {
- p_list->push_back(*key);
- }
-}
-
-void Theme::add_constant_type(const StringName &p_theme_type) {
- if (constant_map.has(p_theme_type)) {
- return;
- }
- constant_map[p_theme_type] = HashMap();
-}
-
-void Theme::get_constant_type_list(List *p_list) const {
- ERR_FAIL_NULL(p_list);
-
- const StringName *key = nullptr;
- while ((key = constant_map.next(key))) {
- p_list->push_back(*key);
- }
-}
-
-void Theme::set_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type, const Variant &p_value) {
- switch (p_data_type) {
- case DATA_TYPE_COLOR: {
- 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()) + ").");
-
- Color color_value = p_value;
- set_color(p_name, p_theme_type, color_value);
- } break;
- case DATA_TYPE_CONSTANT: {
- 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()) + ").");
-
- int constant_value = p_value;
- set_constant(p_name, p_theme_type, constant_value);
- } break;
- case DATA_TYPE_FONT: {
- 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()) + ").");
-
- Ref font_value = Object::cast_to(p_value.get_validated_object());
- set_font(p_name, p_theme_type, font_value);
- } break;
- case DATA_TYPE_FONT_SIZE: {
- 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()) + ").");
-
- int font_size_value = p_value;
- set_font_size(p_name, p_theme_type, font_size_value);
- } break;
- case DATA_TYPE_ICON: {
- 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()) + ").");
-
- Ref icon_value = Object::cast_to(p_value.get_validated_object());
- set_icon(p_name, p_theme_type, icon_value);
- } break;
- case DATA_TYPE_STYLEBOX: {
- 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()) + ").");
-
- Ref stylebox_value = Object::cast_to(p_value.get_validated_object());
- set_stylebox(p_name, p_theme_type, stylebox_value);
- } break;
- case DATA_TYPE_MAX:
- break; // Can't happen, but silences warning.
- }
-}
-
-Variant Theme::get_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type) const {
- switch (p_data_type) {
- case DATA_TYPE_COLOR:
- return get_color(p_name, p_theme_type);
- case DATA_TYPE_CONSTANT:
- return get_constant(p_name, p_theme_type);
- case DATA_TYPE_FONT:
- return get_font(p_name, p_theme_type);
- case DATA_TYPE_FONT_SIZE:
- return get_font_size(p_name, p_theme_type);
- case DATA_TYPE_ICON:
- return get_icon(p_name, p_theme_type);
- case DATA_TYPE_STYLEBOX:
- return get_stylebox(p_name, p_theme_type);
- case DATA_TYPE_MAX:
- break; // Can't happen, but silences warning.
- }
-
- return Variant();
-}
-
-bool Theme::has_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type) const {
- switch (p_data_type) {
- case DATA_TYPE_COLOR:
- return has_color(p_name, p_theme_type);
- case DATA_TYPE_CONSTANT:
- return has_constant(p_name, p_theme_type);
- case DATA_TYPE_FONT:
- return has_font(p_name, p_theme_type);
- case DATA_TYPE_FONT_SIZE:
- return has_font_size(p_name, p_theme_type);
- case DATA_TYPE_ICON:
- return has_icon(p_name, p_theme_type);
- case DATA_TYPE_STYLEBOX:
- return has_stylebox(p_name, p_theme_type);
- case DATA_TYPE_MAX:
- break; // Can't happen, but silences warning.
- }
-
- return false;
-}
-
-bool Theme::has_theme_item_nocheck(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type) const {
- switch (p_data_type) {
- case DATA_TYPE_COLOR:
- return has_color_nocheck(p_name, p_theme_type);
- case DATA_TYPE_CONSTANT:
- return has_constant_nocheck(p_name, p_theme_type);
- case DATA_TYPE_FONT:
- return has_font_nocheck(p_name, p_theme_type);
- case DATA_TYPE_FONT_SIZE:
- return has_font_size_nocheck(p_name, p_theme_type);
- case DATA_TYPE_ICON:
- return has_icon_nocheck(p_name, p_theme_type);
- case DATA_TYPE_STYLEBOX:
- return has_stylebox_nocheck(p_name, p_theme_type);
- case DATA_TYPE_MAX:
- break; // Can't happen, but silences warning.
- }
-
- return false;
-}
-
-void Theme::rename_theme_item(DataType p_data_type, const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type) {
- switch (p_data_type) {
- case DATA_TYPE_COLOR:
- rename_color(p_old_name, p_name, p_theme_type);
- break;
- case DATA_TYPE_CONSTANT:
- rename_constant(p_old_name, p_name, p_theme_type);
- break;
- case DATA_TYPE_FONT:
- rename_font(p_old_name, p_name, p_theme_type);
- break;
- case DATA_TYPE_FONT_SIZE:
- rename_font_size(p_old_name, p_name, p_theme_type);
- break;
- case DATA_TYPE_ICON:
- rename_icon(p_old_name, p_name, p_theme_type);
- break;
- case DATA_TYPE_STYLEBOX:
- rename_stylebox(p_old_name, p_name, p_theme_type);
- break;
- case DATA_TYPE_MAX:
- break; // Can't happen, but silences warning.
- }
-}
-
-void Theme::clear_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type) {
- switch (p_data_type) {
- case DATA_TYPE_COLOR:
- clear_color(p_name, p_theme_type);
- break;
- case DATA_TYPE_CONSTANT:
- clear_constant(p_name, p_theme_type);
- break;
- case DATA_TYPE_FONT:
- clear_font(p_name, p_theme_type);
- break;
- case DATA_TYPE_FONT_SIZE:
- clear_font_size(p_name, p_theme_type);
- break;
- case DATA_TYPE_ICON:
- clear_icon(p_name, p_theme_type);
- break;
- case DATA_TYPE_STYLEBOX:
- clear_stylebox(p_name, p_theme_type);
- break;
- case DATA_TYPE_MAX:
- break; // Can't happen, but silences warning.
- }
-}
-
-void Theme::get_theme_item_list(DataType p_data_type, StringName p_theme_type, List *p_list) const {
- switch (p_data_type) {
- case DATA_TYPE_COLOR:
- get_color_list(p_theme_type, p_list);
- break;
- case DATA_TYPE_CONSTANT:
- get_constant_list(p_theme_type, p_list);
- break;
- case DATA_TYPE_FONT:
- get_font_list(p_theme_type, p_list);
- break;
- case DATA_TYPE_FONT_SIZE:
- get_font_size_list(p_theme_type, p_list);
- break;
- case DATA_TYPE_ICON:
- get_icon_list(p_theme_type, p_list);
- break;
- case DATA_TYPE_STYLEBOX:
- get_stylebox_list(p_theme_type, p_list);
- break;
- case DATA_TYPE_MAX:
- break; // Can't happen, but silences warning.
- }
-}
-
-void Theme::add_theme_item_type(DataType p_data_type, const StringName &p_theme_type) {
- switch (p_data_type) {
- case DATA_TYPE_COLOR:
- add_color_type(p_theme_type);
- break;
- case DATA_TYPE_CONSTANT:
- add_constant_type(p_theme_type);
- break;
- case DATA_TYPE_FONT:
- add_font_type(p_theme_type);
- break;
- case DATA_TYPE_FONT_SIZE:
- add_font_size_type(p_theme_type);
- break;
- case DATA_TYPE_ICON:
- add_icon_type(p_theme_type);
- break;
- case DATA_TYPE_STYLEBOX:
- add_stylebox_type(p_theme_type);
- break;
- case DATA_TYPE_MAX:
- break; // Can't happen, but silences warning.
- }
-}
-
-void Theme::get_theme_item_type_list(DataType p_data_type, List *p_list) const {
- switch (p_data_type) {
- case DATA_TYPE_COLOR:
- get_color_type_list(p_list);
- break;
- case DATA_TYPE_CONSTANT:
- get_constant_type_list(p_list);
- break;
- case DATA_TYPE_FONT:
- get_font_type_list(p_list);
- break;
- case DATA_TYPE_FONT_SIZE:
- get_font_size_type_list(p_list);
- break;
- case DATA_TYPE_ICON:
- get_icon_type_list(p_list);
- break;
- case DATA_TYPE_STYLEBOX:
- get_stylebox_type_list(p_list);
- break;
- case DATA_TYPE_MAX:
- break; // Can't happen, but silences warning.
- }
-}
-
-void Theme::set_type_variation(const StringName &p_theme_type, const StringName &p_base_type) {
- ERR_FAIL_COND_MSG(p_theme_type == StringName(), "An empty theme type cannot be marked as a variation of another type.");
- 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.");
- 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.");
-
- if (variation_map.has(p_theme_type)) {
- StringName old_base = variation_map[p_theme_type];
- variation_base_map[old_base].erase(p_theme_type);
- }
-
- variation_map[p_theme_type] = p_base_type;
- variation_base_map[p_base_type].push_back(p_theme_type);
-
- _emit_theme_changed();
-}
-
-bool Theme::is_type_variation(const StringName &p_theme_type, const StringName &p_base_type) const {
- return (variation_map.has(p_theme_type) && variation_map[p_theme_type] == p_base_type);
-}
-
-void Theme::clear_type_variation(const StringName &p_theme_type) {
- ERR_FAIL_COND_MSG(!variation_map.has(p_theme_type), "Cannot clear the type variation '" + String(p_theme_type) + "' because it does not exist.");
-
- StringName base_type = variation_map[p_theme_type];
- variation_base_map[base_type].erase(p_theme_type);
- variation_map.erase(p_theme_type);
-
- _emit_theme_changed();
-}
-
-StringName Theme::get_type_variation_base(const StringName &p_theme_type) const {
- if (!variation_map.has(p_theme_type)) {
- return StringName();
- }
-
- return variation_map[p_theme_type];
-}
-
-void Theme::get_type_variation_list(const StringName &p_base_type, List *p_list) const {
- ERR_FAIL_NULL(p_list);
-
- if (!variation_base_map.has(p_base_type)) {
- return;
- }
-
- for (const StringName &E : variation_base_map[p_base_type]) {
- // Prevent infinite loops if variants were set to be cross-dependent (that's still invalid usage, but handling for stability sake).
- if (p_list->find(E)) {
- continue;
- }
-
- p_list->push_back(E);
- // Continue looking for sub-variations.
- get_type_variation_list(E, p_list);
- }
+ notify_property_list_changed();
+ emit_changed();
}
void Theme::_freeze_change_propagation() {
@@ -1296,60 +1420,6 @@ void Theme::_unfreeze_and_propagate_changes() {
_emit_theme_changed();
}
-void Theme::clear() {
- // These items need disconnecting.
- {
- const StringName *K = nullptr;
- while ((K = icon_map.next(K))) {
- const StringName *L = nullptr;
- while ((L = icon_map[*K].next(L))) {
- Ref icon = icon_map[*K][*L];
- if (icon.is_valid()) {
- icon->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
- }
- }
- }
- }
-
- {
- const StringName *K = nullptr;
- while ((K = style_map.next(K))) {
- const StringName *L = nullptr;
- while ((L = style_map[*K].next(L))) {
- Ref style = style_map[*K][*L];
- if (style.is_valid()) {
- style->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
- }
- }
- }
- }
-
- {
- const StringName *K = nullptr;
- while ((K = font_map.next(K))) {
- const StringName *L = nullptr;
- while ((L = font_map[*K].next(L))) {
- Ref font = font_map[*K][*L];
- if (font.is_valid()) {
- font->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
- }
- }
- }
- }
-
- icon_map.clear();
- style_map.clear();
- font_map.clear();
- font_size_map.clear();
- color_map.clear();
- constant_map.clear();
-
- variation_map.clear();
- variation_base_map.clear();
-
- _emit_theme_changed();
-}
-
void Theme::merge_with(const Ref &p_other) {
if (p_other.is_null()) {
return;
@@ -1434,80 +1504,58 @@ void Theme::merge_with(const Ref &p_other) {
_unfreeze_and_propagate_changes();
}
-void Theme::get_type_list(List *p_list) const {
- ERR_FAIL_NULL(p_list);
-
- Set types;
- const StringName *key = nullptr;
-
- // Icons.
- while ((key = icon_map.next(key))) {
- types.insert(*key);
- }
-
- key = nullptr;
-
- // StyleBoxes.
- while ((key = style_map.next(key))) {
- types.insert(*key);
- }
-
- key = nullptr;
-
- // Fonts.
- while ((key = font_map.next(key))) {
- types.insert(*key);
- }
-
- key = nullptr;
-
- // Font sizes.
- while ((key = font_size_map.next(key))) {
- types.insert(*key);
- }
-
- key = nullptr;
-
- // Colors.
- while ((key = color_map.next(key))) {
- types.insert(*key);
- }
-
- key = nullptr;
-
- // Constants.
- while ((key = constant_map.next(key))) {
- types.insert(*key);
- }
-
- for (Set::Element *E = types.front(); E; E = E->next()) {
- p_list->push_back(E->get());
- }
-}
-
-void Theme::get_type_dependencies(const StringName &p_base_type, const StringName &p_type_variation, List *p_list) {
- ERR_FAIL_NULL(p_list);
-
- // Build the dependency chain for type variations.
- if (p_type_variation != StringName()) {
- StringName variation_name = p_type_variation;
- while (variation_name != StringName()) {
- p_list->push_back(variation_name);
- variation_name = get_type_variation_base(variation_name);
-
- // If we have reached the base type dependency, it's safe to stop (assuming no funny business was done to the Theme).
- if (variation_name == p_base_type) {
- break;
+void Theme::clear() {
+ // These items need disconnecting.
+ {
+ const StringName *K = nullptr;
+ while ((K = icon_map.next(K))) {
+ const StringName *L = nullptr;
+ while ((L = icon_map[*K].next(L))) {
+ Ref icon = icon_map[*K][*L];
+ if (icon.is_valid()) {
+ icon->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ }
}
}
}
- // Continue building the chain using native class hierarchy.
- StringName class_name = p_base_type;
- while (class_name != StringName()) {
- p_list->push_back(class_name);
- class_name = ClassDB::get_parent_class_nocheck(class_name);
+ {
+ const StringName *K = nullptr;
+ while ((K = style_map.next(K))) {
+ const StringName *L = nullptr;
+ while ((L = style_map[*K].next(L))) {
+ Ref style = style_map[*K][*L];
+ if (style.is_valid()) {
+ style->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ }
+ }
+ }
}
+
+ {
+ const StringName *K = nullptr;
+ while ((K = font_map.next(K))) {
+ const StringName *L = nullptr;
+ while ((L = font_map[*K].next(L))) {
+ Ref font = font_map[*K][*L];
+ if (font.is_valid()) {
+ font->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ }
+ }
+ }
+ }
+
+ icon_map.clear();
+ style_map.clear();
+ font_map.clear();
+ font_size_map.clear();
+ color_map.clear();
+ constant_map.clear();
+
+ variation_map.clear();
+ variation_base_map.clear();
+
+ _emit_theme_changed();
}
void Theme::reset_state() {
@@ -1563,11 +1611,17 @@ void Theme::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_constant_list", "theme_type"), &Theme::_get_constant_list);
ClassDB::bind_method(D_METHOD("get_constant_type_list"), &Theme::_get_constant_type_list);
+ ClassDB::bind_method(D_METHOD("set_default_base_scale", "font_size"), &Theme::set_default_theme_base_scale);
+ ClassDB::bind_method(D_METHOD("get_default_base_scale"), &Theme::get_default_theme_base_scale);
+ ClassDB::bind_method(D_METHOD("has_default_base_scale"), &Theme::has_default_theme_base_scale);
+
ClassDB::bind_method(D_METHOD("set_default_font", "font"), &Theme::set_default_theme_font);
ClassDB::bind_method(D_METHOD("get_default_font"), &Theme::get_default_theme_font);
+ ClassDB::bind_method(D_METHOD("has_default_font"), &Theme::has_default_theme_font);
ClassDB::bind_method(D_METHOD("set_default_font_size", "font_size"), &Theme::set_default_theme_font_size);
ClassDB::bind_method(D_METHOD("get_default_font_size"), &Theme::get_default_theme_font_size);
+ ClassDB::bind_method(D_METHOD("has_default_font_size"), &Theme::has_default_theme_font_size);
ClassDB::bind_method(D_METHOD("set_theme_item", "data_type", "name", "theme_type", "value"), &Theme::set_theme_item);
ClassDB::bind_method(D_METHOD("get_theme_item", "data_type", "name", "theme_type"), &Theme::get_theme_item);
@@ -1588,6 +1642,7 @@ void Theme::_bind_methods() {
ClassDB::bind_method(D_METHOD("merge_with", "other"), &Theme::merge_with);
ClassDB::bind_method(D_METHOD("clear"), &Theme::clear);
+ 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");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "default_font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_default_font", "get_default_font");
ADD_PROPERTY(PropertyInfo(Variant::INT, "default_font_size"), "set_default_font_size", "get_default_font_size");
diff --git a/scene/resources/theme.h b/scene/resources/theme.h
index 15f21b91b8d..50466d99c23 100644
--- a/scene/resources/theme.h
+++ b/scene/resources/theme.h
@@ -96,13 +96,19 @@ protected:
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List *p_list) const;
- static Ref project_default_theme;
+ // Universal Theme resources used when no other theme has the item.
static Ref default_theme;
+ static Ref project_default_theme;
+
+ // Universal default values, final fallback for every theme.
+ static float default_base_scale;
static Ref default_icon;
static Ref default_style;
static Ref default_font;
static int default_font_size;
+ // Default values configurable for each individual theme.
+ float default_theme_base_scale = 0.0;
Ref default_theme_font;
int default_theme_font_size = -1;
@@ -120,16 +126,23 @@ public:
static Ref get_project_default();
static void set_project_default(const Ref &p_project_default);
+ static void set_default_base_scale(float p_base_scale);
static void set_default_icon(const Ref &p_icon);
static void set_default_style(const Ref &p_style);
static void set_default_font(const Ref &p_font);
static void set_default_font_size(int p_font_size);
+ void set_default_theme_base_scale(float p_base_scale);
+ float get_default_theme_base_scale() const;
+ bool has_default_theme_base_scale() const;
+
void set_default_theme_font(const Ref &p_default_font);
Ref get_default_theme_font() const;
+ bool has_default_theme_font() const;
void set_default_theme_font_size(int p_font_size);
int get_default_theme_font_size() const;
+ bool has_default_theme_font_size() const;
void set_icon(const StringName &p_name, const StringName &p_theme_type, const Ref &p_icon);
Ref get_icon(const StringName &p_name, const StringName &p_theme_type) const;