From 8aa78bc0501a37d7c1e81f0954ac5bd9b49f890f Mon Sep 17 00:00:00 2001 From: Yuri Sizov Date: Sat, 2 Oct 2021 19:26:20 +0300 Subject: Reorganize Theme resource code for better maintainability --- scene/resources/theme.cpp | 1363 +++++++++++++++++++++++---------------------- scene/resources/theme.h | 6 +- 2 files changed, 695 insertions(+), 674 deletions(-) (limited to 'scene') diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp index e49d883ba4..dab08d48c2 100644 --- a/scene/resources/theme.cpp +++ b/scene/resources/theme.cpp @@ -29,597 +29,344 @@ /*************************************************************************/ #include "theme.h" -#include "core/io/file_access.h" #include "core/string/print_string.h" -void Theme::_emit_theme_changed() { - if (no_change_propagation) { - return; - } +// Universal Theme resources used when no other theme has the item. +Ref Theme::default_theme; +Ref Theme::project_default_theme; - notify_property_list_changed(); - emit_changed(); -} +// Universal default values, final fallback for every theme. +Ref Theme::default_icon; +Ref Theme::default_style; +Ref Theme::default_font; +int Theme::default_font_size = 16; -Vector Theme::_get_icon_list(const String &p_theme_type) const { - Vector ilret; - List il; +// Dynamic properties. +bool Theme::_set(const StringName &p_name, const Variant &p_value) { + String sname = p_name; - get_icon_list(p_theme_type, &il); - ilret.resize(il.size()); + if (sname.find("/") != -1) { + String type = sname.get_slicec('/', 1); + String theme_type = sname.get_slicec('/', 0); + String name = sname.get_slicec('/', 2); - int i = 0; - String *w = ilret.ptrw(); - for (List::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); + 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 ilret; + + return false; } -Vector Theme::_get_icon_type_list() const { - Vector ilret; - List il; +bool Theme::_get(const StringName &p_name, Variant &r_ret) const { + String sname = p_name; - get_icon_type_list(&il); - ilret.resize(il.size()); + if (sname.find("/") != -1) { + String type = sname.get_slicec('/', 1); + String theme_type = sname.get_slicec('/', 0); + String name = sname.get_slicec('/', 2); - int i = 0; - String *w = ilret.ptrw(); - for (List::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); + 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 ilret; + + return false; } -Vector Theme::_get_stylebox_list(const String &p_theme_type) const { - Vector ilret; - List il; +void Theme::_get_property_list(List *p_list) const { + List list; - get_stylebox_list(p_theme_type, &il); - ilret.resize(il.size()); + const StringName *key = nullptr; - int i = 0; - String *w = ilret.ptrw(); - for (List::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); + // Type variations. + while ((key = variation_map.next(key))) { + list.push_back(PropertyInfo(Variant::STRING_NAME, String() + *key + "/base_type")); } - return ilret; -} -Vector Theme::_get_stylebox_type_list() const { - Vector ilret; - List il; + key = nullptr; - get_stylebox_type_list(&il); - ilret.resize(il.size()); + // Icons. + while ((key = icon_map.next(key))) { + const StringName *key2 = nullptr; - int i = 0; - String *w = ilret.ptrw(); - for (List::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); + 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)); + } } - return ilret; -} -Vector Theme::_get_font_list(const String &p_theme_type) const { - Vector ilret; - List il; + key = nullptr; - get_font_list(p_theme_type, &il); - ilret.resize(il.size()); + // Styles. + while ((key = style_map.next(key))) { + const StringName *key2 = nullptr; - int i = 0; - String *w = ilret.ptrw(); - for (List::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); + 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)); + } } - return ilret; -} -Vector Theme::_get_font_type_list() const { - Vector ilret; - List il; + key = nullptr; - get_font_type_list(&il); - ilret.resize(il.size()); + // Fonts. + while ((key = font_map.next(key))) { + const StringName *key2 = nullptr; - int i = 0; - String *w = ilret.ptrw(); - for (List::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); + 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)); + } } - return ilret; -} -Vector Theme::_get_font_size_list(const String &p_theme_type) const { - Vector ilret; - List il; + key = nullptr; - get_font_size_list(p_theme_type, &il); - ilret.resize(il.size()); + // Font sizes. + while ((key = font_size_map.next(key))) { + const StringName *key2 = nullptr; - int i = 0; - String *w = ilret.ptrw(); - for (List::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); + while ((key2 = font_size_map[*key].next(key2))) { + list.push_back(PropertyInfo(Variant::INT, String() + *key + "/font_sizes/" + *key2)); + } } - return ilret; -} -Vector Theme::_get_font_size_type_list() const { - Vector ilret; - List il; + key = nullptr; - get_font_size_type_list(&il); - ilret.resize(il.size()); + // Colors. + while ((key = color_map.next(key))) { + const StringName *key2 = nullptr; - int i = 0; - String *w = ilret.ptrw(); - for (List::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); + while ((key2 = color_map[*key].next(key2))) { + list.push_back(PropertyInfo(Variant::COLOR, String() + *key + "/colors/" + *key2)); + } } - return ilret; -} -Vector Theme::_get_color_list(const String &p_theme_type) const { - Vector ilret; - List il; + key = nullptr; - get_color_list(p_theme_type, &il); - ilret.resize(il.size()); + // Constants. + while ((key = constant_map.next(key))) { + const StringName *key2 = nullptr; - int i = 0; - String *w = ilret.ptrw(); - for (List::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); + 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); } - return ilret; } -Vector Theme::_get_color_type_list() const { - Vector ilret; - List il; +// Universal fallback Theme resources. +Ref Theme::get_default() { + return default_theme; +} - get_color_type_list(&il); - ilret.resize(il.size()); +void Theme::set_default(const Ref &p_default) { + default_theme = p_default; +} - int i = 0; - String *w = ilret.ptrw(); - for (List::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); - } - return ilret; +Ref Theme::get_project_default() { + return project_default_theme; } -Vector Theme::_get_constant_list(const String &p_theme_type) const { - Vector ilret; - List il; +void Theme::set_project_default(const Ref &p_project_default) { + project_default_theme = p_project_default; +} - get_constant_list(p_theme_type, &il); - ilret.resize(il.size()); +// Universal fallback values for theme item types. +void Theme::set_default_icon(const Ref &p_icon) { + default_icon = p_icon; +} - int i = 0; - String *w = ilret.ptrw(); - for (List::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); - } - return ilret; +void Theme::set_default_style(const Ref &p_style) { + default_style = p_style; } -Vector Theme::_get_constant_type_list() const { - Vector ilret; - List il; +void Theme::set_default_font(const Ref &p_font) { + default_font = p_font; +} - get_constant_type_list(&il); - ilret.resize(il.size()); +void Theme::set_default_font_size(int p_font_size) { + default_font_size = p_font_size; +} - int i = 0; - String *w = ilret.ptrw(); - for (List::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); +// Fallback values for theme item types, configurable per theme. +void Theme::set_default_theme_font(const Ref &p_default_font) { + if (default_theme_font == p_default_font) { + return; } - return ilret; -} -Vector Theme::_get_theme_item_list(DataType p_data_type, const String &p_theme_type) const { - switch (p_data_type) { - case DATA_TYPE_COLOR: - return _get_color_list(p_theme_type); - case DATA_TYPE_CONSTANT: - return _get_constant_list(p_theme_type); - case DATA_TYPE_FONT: - return _get_font_list(p_theme_type); - case DATA_TYPE_FONT_SIZE: - return _get_font_size_list(p_theme_type); - case DATA_TYPE_ICON: - return _get_icon_list(p_theme_type); - case DATA_TYPE_STYLEBOX: - return _get_stylebox_list(p_theme_type); - case DATA_TYPE_MAX: - break; // Can't happen, but silences warning. + if (default_theme_font.is_valid()) { + default_theme_font->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed)); } - return Vector(); -} + default_theme_font = p_default_font; -Vector Theme::_get_theme_item_type_list(DataType p_data_type) const { - switch (p_data_type) { - case DATA_TYPE_COLOR: - return _get_color_type_list(); - case DATA_TYPE_CONSTANT: - return _get_constant_type_list(); - case DATA_TYPE_FONT: - return _get_font_type_list(); - case DATA_TYPE_FONT_SIZE: - return _get_font_size_type_list(); - case DATA_TYPE_ICON: - return _get_icon_type_list(); - case DATA_TYPE_STYLEBOX: - return _get_stylebox_type_list(); - case DATA_TYPE_MAX: - break; // Can't happen, but silences warning. + if (default_theme_font.is_valid()) { + default_theme_font->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED); } - return Vector(); + _emit_theme_changed(); } -Vector Theme::_get_type_variation_list(const StringName &p_theme_type) const { - Vector ilret; - List il; - - get_type_variation_list(p_theme_type, &il); - ilret.resize(il.size()); +Ref Theme::get_default_theme_font() const { + return default_theme_font; +} - int i = 0; - String *w = ilret.ptrw(); - for (List::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); +void Theme::set_default_theme_font_size(int p_font_size) { + if (default_theme_font_size == p_font_size) { + return; } - return ilret; -} -Vector Theme::_get_type_list() const { - Vector ilret; - List il; + default_theme_font_size = p_font_size; - get_type_list(&il); - ilret.resize(il.size()); + _emit_theme_changed(); +} - int i = 0; - String *w = ilret.ptrw(); - for (List::Element *E = il.front(); E; E = E->next(), i++) { - w[i] = E->get(); - } - return ilret; +int Theme::get_default_theme_font_size() const { + return default_theme_font_size; } -bool Theme::_set(const StringName &p_name, const Variant &p_value) { - String sname = p_name; +// 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)); + } - if (sname.find("/") != -1) { - String type = sname.get_slicec('/', 1); - String theme_type = sname.get_slicec('/', 0); - String name = sname.get_slicec('/', 2); + icon_map[p_theme_type][p_name] = p_icon; - 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; - } + 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); + } - return true; + _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; } +} - return false; +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::_get(const StringName &p_name, Variant &r_ret) const { - String sname = p_name; +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)); +} - if (sname.find("/") != -1) { - String type = sname.get_slicec('/', 1); - String theme_type = sname.get_slicec('/', 0); - String name = sname.get_slicec('/', 2); +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."); - 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; - } + icon_map[p_theme_type][p_name] = icon_map[p_theme_type][p_old_name]; + icon_map[p_theme_type].erase(p_old_name); - return true; + _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)); } - return false; + icon_map[p_theme_type].erase(p_name); + + _emit_theme_changed(); } -void Theme::_get_property_list(List *p_list) const { - List list; +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; - // Type variations. - while ((key = variation_map.next(key))) { - list.push_back(PropertyInfo(Variant::STRING_NAME, String() + *key + "/base_type")); + while ((key = icon_map[p_theme_type].next(key))) { + p_list->push_back(*key); } +} - key = nullptr; +void Theme::add_icon_type(const StringName &p_theme_type) { + if (icon_map.has(p_theme_type)) { + return; + } + icon_map[p_theme_type] = HashMap>(); +} - // Icons. - while ((key = icon_map.next(key))) { - const StringName *key2 = nullptr; +void Theme::get_icon_type_list(List *p_list) const { + ERR_FAIL_NULL(p_list); - 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)); - } + const StringName *key = nullptr; + while ((key = icon_map.next(key))) { + p_list->push_back(*key); } +} - key = nullptr; +// 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)); + } - // Styles. - while ((key = style_map.next(key))) { - const StringName *key2 = nullptr; + style_map[p_theme_type][p_name] = p_style; - 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) { - 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); + 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(); @@ -695,6 +442,7 @@ void Theme::get_stylebox_type_list(List *p_list) const { } } +// 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)); @@ -781,6 +529,7 @@ void Theme::get_font_type_list(List *p_list) const { } } +// 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; @@ -855,6 +604,7 @@ void Theme::get_font_size_type_list(List *p_list) const { } } +// 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; @@ -927,6 +677,7 @@ void Theme::get_color_type_list(List *p_list) const { } } +// 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; @@ -999,6 +750,7 @@ void Theme::get_constant_type_list(List *p_list) const { } } +// 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: { @@ -1205,86 +957,427 @@ void Theme::add_theme_item_type(DataType p_data_type, const StringName &p_theme_ } } -void Theme::get_theme_item_type_list(DataType p_data_type, List *p_list) const { +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; + + get_icon_list(p_theme_type, &il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector Theme::_get_icon_type_list() const { + Vector ilret; + List il; + + get_icon_type_list(&il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector Theme::_get_stylebox_list(const String &p_theme_type) const { + Vector ilret; + List il; + + get_stylebox_list(p_theme_type, &il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector Theme::_get_stylebox_type_list() const { + Vector ilret; + List il; + + get_stylebox_type_list(&il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector Theme::_get_font_list(const String &p_theme_type) const { + Vector ilret; + List il; + + get_font_list(p_theme_type, &il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector Theme::_get_font_type_list() const { + Vector ilret; + List il; + + get_font_type_list(&il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector Theme::_get_font_size_list(const String &p_theme_type) const { + Vector ilret; + List il; + + get_font_size_list(p_theme_type, &il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector Theme::_get_font_size_type_list() const { + Vector ilret; + List il; + + get_font_size_type_list(&il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector Theme::_get_color_list(const String &p_theme_type) const { + Vector ilret; + List il; + + get_color_list(p_theme_type, &il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector Theme::_get_color_type_list() const { + Vector ilret; + List il; + + get_color_type_list(&il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector Theme::_get_constant_list(const String &p_theme_type) const { + Vector ilret; + List il; + + get_constant_list(p_theme_type, &il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector Theme::_get_constant_type_list() const { + Vector ilret; + List il; + + get_constant_type_list(&il); + ilret.resize(il.size()); + + int i = 0; + String *w = ilret.ptrw(); + for (List::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +Vector Theme::_get_theme_item_list(DataType p_data_type, const String &p_theme_type) const { + switch (p_data_type) { + case DATA_TYPE_COLOR: + return _get_color_list(p_theme_type); + case DATA_TYPE_CONSTANT: + return _get_constant_list(p_theme_type); + case DATA_TYPE_FONT: + return _get_font_list(p_theme_type); + case DATA_TYPE_FONT_SIZE: + return _get_font_size_list(p_theme_type); + case DATA_TYPE_ICON: + return _get_icon_list(p_theme_type); + case DATA_TYPE_STYLEBOX: + return _get_stylebox_list(p_theme_type); + case DATA_TYPE_MAX: + break; // Can't happen, but silences warning. + } + + return Vector(); +} + +Vector Theme::_get_theme_item_type_list(DataType p_data_type) const { switch (p_data_type) { case DATA_TYPE_COLOR: - get_color_type_list(p_list); - break; + return _get_color_type_list(); case DATA_TYPE_CONSTANT: - get_constant_type_list(p_list); - break; + return _get_constant_type_list(); case DATA_TYPE_FONT: - get_font_type_list(p_list); - break; + return _get_font_type_list(); case DATA_TYPE_FONT_SIZE: - get_font_size_type_list(p_list); - break; + return _get_font_size_type_list(); case DATA_TYPE_ICON: - get_icon_type_list(p_list); - break; + return _get_icon_type_list(); case DATA_TYPE_STYLEBOX: - get_stylebox_type_list(p_list); - break; + return _get_stylebox_type_list(); 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); - } + return Vector(); +} - variation_map[p_theme_type] = p_base_type; - variation_base_map[p_base_type].push_back(p_theme_type); +Vector Theme::_get_type_variation_list(const StringName &p_theme_type) const { + Vector ilret; + List il; - _emit_theme_changed(); -} + get_type_variation_list(p_theme_type, &il); + ilret.resize(il.size()); -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); + int i = 0; + String *w = ilret.ptrw(); + for (List::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; } -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); +Vector Theme::_get_type_list() const { + Vector ilret; + List il; - _emit_theme_changed(); -} + get_type_list(&il); + ilret.resize(il.size()); -StringName Theme::get_type_variation_base(const StringName &p_theme_type) const { - if (!variation_map.has(p_theme_type)) { - return StringName(); + int i = 0; + String *w = ilret.ptrw(); + for (List::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); } - - return variation_map[p_theme_type]; + return ilret; } -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)) { +// Theme bulk manipulations. +void Theme::_emit_theme_changed() { + if (no_change_propagation) { 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 +1389,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 +1473,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); +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)); + } + } + } } - for (Set::Element *E = types.front(); E; E = E->next()) { - p_list->push_back(E->get()); + { + 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)); + } + } + } } -} - -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; + { + 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)); + } } } } - // 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); - } + 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() { diff --git a/scene/resources/theme.h b/scene/resources/theme.h index 15f21b91b8..35e466f899 100644 --- a/scene/resources/theme.h +++ b/scene/resources/theme.h @@ -96,13 +96,17 @@ 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 Ref default_icon; static Ref default_style; static Ref default_font; static int default_font_size; + // Default values configurable for each individual theme. Ref default_theme_font; int default_theme_font_size = -1; -- cgit v1.2.3