diff --git a/core/global_constants.cpp b/core/global_constants.cpp index 9e8c0b9b5e79..9346c499d517 100644 --- a/core/global_constants.cpp +++ b/core/global_constants.cpp @@ -584,6 +584,7 @@ void register_global_constants() { BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_RANGE); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_EXP_RANGE); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_ENUM); + BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_ENUM_SUGGESTION); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_EXP_EASING); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_LENGTH); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_KEY_ACCEL); diff --git a/core/object.h b/core/object.h index 2dff4654de81..b46ab496ae0a 100644 --- a/core/object.h +++ b/core/object.h @@ -93,6 +93,7 @@ enum PropertyHint { PROPERTY_HINT_OBJECT_TOO_BIG, ///< object is too big to send PROPERTY_HINT_NODE_PATH_VALID_TYPES, PROPERTY_HINT_SAVE_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,". This opens a save dialog + PROPERTY_HINT_ENUM_SUGGESTION, ///< hint_text= "val1,val2,val3,etc" PROPERTY_HINT_MAX, // When updating PropertyHint, also sync the hardcoded list in VisualScriptEditorVariableEdit }; diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index 1ba70d06d3a6..e12d0ba7fa40 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -1420,6 +1420,10 @@ Hints that an integer, float or string property is an enumerated value to pick in a list specified via a hint string such as [code]"Hello,Something,Else"[/code]. + + Hints that a string property can be an enumerated value to pick in a list specified via a hint string such as [code]"Hello,Something,Else"[/code]. + Unlike [constant PROPERTY_HINT_ENUM] a property with this hint still accepts arbitrary values and can be empty. The list of values serves to suggest possible values. + Hints that a float property should be edited via an exponential easing function. The hint string can include [code]"attenuation"[/code] to flip the curve horizontally and/or [code]"inout"[/code] to also include in/out easing. diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml index bf93b7814c1f..efabe2ba18e3 100644 --- a/doc/classes/Control.xml +++ b/doc/classes/Control.xml @@ -231,7 +231,7 @@ - Returns a [Color] from the first matching [Theme] in the tree if that [Theme] has a color item with the specified [code]name[/code] and [code]theme_type[/code]. If [code]theme_type[/code] is omitted the class name of the current control is used as the type. If the type is a class name its parent classes are also checked, in order of inheritance. + Returns a [Color] from the first matching [Theme] in the tree if that [Theme] has a color item with the specified [code]name[/code] and [code]theme_type[/code]. If [code]theme_type[/code] is omitted the class name of the current control is used as the type, or [member theme_type_variation] if it is defined. If the type is a class name its parent classes are also checked, in order of inheritance. For the current control its local overrides are considered first (see [method add_color_override]), then its assigned [member theme]. After the current control, each parent control and its assigned [member theme] are considered; controls without a [member theme] assigned are skipped. If no matching [Theme] is found in the tree, a custom project [Theme] (see [member ProjectSettings.gui/theme/custom]) and the default [Theme] are used. [codeblock] func _ready(): @@ -855,6 +855,12 @@ Changing this property replaces the current [Theme] resource this node and all its [Control] children use. + + The name of a theme type variation used by this [Control] to look up its own theme items. When empty, the class name of the node is used (e.g. [code]Button[/code] for the [Button] control), as well as the class names of all parent classes (in order of inheritance). + When set, this property gives the highest priority to the type of the specified name. This type can in turn extend another type, forming a dependency chain. See [method Theme.set_type_variation]. If the theme item cannot be found using this type or its base types, lookup falls back on the class names. + [b]Note:[/b] To look up [Control]'s own items use various [code]get_*[/code] methods without specifying [code]theme_type[/code]. + [b]Note:[/b] Theme items are looked for in the tree order, from branch to root, where each [Control] node is checked for its [member theme] property. The earliest match against any type/class name is returned. The project-level Theme and the default Theme are checked last. + diff --git a/doc/classes/Theme.xml b/doc/classes/Theme.xml index f47ea0312e56..4d44e33dc217 100644 --- a/doc/classes/Theme.xml +++ b/doc/classes/Theme.xml @@ -20,7 +20,7 @@ - + Clears the [Color] at [code]name[/code] if the theme has [code]node_type[/code]. @@ -28,7 +28,7 @@ - + Clears the constant at [code]name[/code] if the theme has [code]node_type[/code]. @@ -36,7 +36,7 @@ - + Clears the [Font] at [code]name[/code] if the theme has [code]node_type[/code]. @@ -44,7 +44,7 @@ - + Clears the icon at [code]name[/code] if the theme has [code]node_type[/code]. @@ -52,7 +52,7 @@ - + Clears [StyleBox] at [code]name[/code] if the theme has [code]node_type[/code]. @@ -61,11 +61,18 @@ - + Clears the theme item of [code]data_type[/code] at [code]name[/code] if the theme has [code]node_type[/code]. + + + + + Unmarks [code]theme_type[/code] as being a variation of another theme type. See [method set_type_variation]. + + @@ -82,14 +89,14 @@ - + Returns the [Color] at [code]name[/code] if the theme has [code]node_type[/code]. - + Returns all the [Color]s as a [PoolStringArray] filled with each [Color]'s name, for use in [method get_color], if the theme has [code]node_type[/code]. @@ -103,14 +110,14 @@ - + Returns the constant at [code]name[/code] if the theme has [code]node_type[/code]. - + Returns all the constants as a [PoolStringArray] filled with each constant's name, for use in [method get_constant], if the theme has [code]node_type[/code]. @@ -124,14 +131,14 @@ - + Returns the [Font] at [code]name[/code] if the theme has [code]node_type[/code]. If such item does not exist and [member default_font] is set on the theme, the default font will be returned. - + Returns all the [Font]s as a [PoolStringArray] filled with each [Font]'s name, for use in [method get_font], if the theme has [code]node_type[/code]. @@ -145,14 +152,14 @@ - + Returns the icon [Texture] at [code]name[/code] if the theme has [code]node_type[/code]. - + Returns all the icons as a [PoolStringArray] filled with each [Texture]'s name, for use in [method get_icon], if the theme has [code]node_type[/code]. @@ -166,7 +173,7 @@ - + Returns the [StyleBox] at [code]name[/code] if the theme has [code]node_type[/code]. Valid [code]name[/code]s may be found using [method get_stylebox_list]. Valid [code]node_type[/code]s may be found using [method get_stylebox_types]. @@ -174,7 +181,7 @@ - + Returns all the [StyleBox]s as a [PoolStringArray] filled with each [StyleBox]'s name, for use in [method get_stylebox], if the theme has [code]node_type[/code]. Valid [code]node_type[/code]s may be found using [method get_stylebox_types]. @@ -190,7 +197,7 @@ - + Returns the theme item of [code]data_type[/code] at [code]name[/code] if the theme has [code]node_type[/code]. Valid [code]name[/code]s may be found using [method get_theme_item_list] or a data type specific method. Valid [code]node_type[/code]s may be found using [method get_theme_item_types] or a data type specific method. @@ -199,7 +206,7 @@ - + Returns all the theme items of [code]data_type[/code] as a [PoolStringArray] filled with each theme items's name, for use in [method get_theme_item] or a data type specific method, if the theme has [code]node_type[/code]. Valid [code]node_type[/code]s may be found using [method get_theme_item_types] or a data type specific method. @@ -214,16 +221,30 @@ - + Returns all the theme types as a [PoolStringArray] filled with unique type names, for use in other [code]get_*[/code] functions of this theme. [b]Note:[/b] [code]node_type[/code] has no effect and will be removed in future version. + + + + + Returns the name of the base theme type if [code]theme_type[/code] is a valid variation type. Returns an empty string otherwise. + + + + + + + Returns a list of all type variations for the given [code]base_type[/code]. + + - + Returns [code]true[/code] if [Color] with [code]name[/code] is in [code]node_type[/code]. Returns [code]false[/code] if the theme does not have [code]node_type[/code]. @@ -232,7 +253,7 @@ - + Returns [code]true[/code] if constant with [code]name[/code] is in [code]node_type[/code]. Returns [code]false[/code] if the theme does not have [code]node_type[/code]. @@ -247,7 +268,7 @@ - + Returns [code]true[/code] if [Font] with [code]name[/code] is in [code]node_type[/code]. Returns [code]false[/code] if the theme does not have [code]node_type[/code]. @@ -256,7 +277,7 @@ - + Returns [code]true[/code] if icon [Texture] with [code]name[/code] is in [code]node_type[/code]. Returns [code]false[/code] if the theme does not have [code]node_type[/code]. @@ -265,7 +286,7 @@ - + Returns [code]true[/code] if [StyleBox] with [code]name[/code] is in [code]node_type[/code]. Returns [code]false[/code] if the theme does not have [code]node_type[/code]. @@ -275,12 +296,20 @@ - + Returns [code]true[/code] if a theme item of [code]data_type[/code] with [code]name[/code] is in [code]node_type[/code]. Returns [code]false[/code] if the theme does not have [code]node_type[/code]. + + + + + + Returns [code]true[/code] if [code]theme_type[/code] is marked as a variation of [code]base_type[/code]. + + @@ -293,7 +322,7 @@ - + Renames the [Color] at [code]old_name[/code] to [code]name[/code] if the theme has [code]node_type[/code]. If [code]name[/code] is already taken, this method fails. @@ -302,7 +331,7 @@ - + Renames the constant at [code]old_name[/code] to [code]name[/code] if the theme has [code]node_type[/code]. If [code]name[/code] is already taken, this method fails. @@ -311,7 +340,7 @@ - + Renames the [Font] at [code]old_name[/code] to [code]name[/code] if the theme has [code]node_type[/code]. If [code]name[/code] is already taken, this method fails. @@ -320,7 +349,7 @@ - + Renames the icon at [code]old_name[/code] to [code]name[/code] if the theme has [code]node_type[/code]. If [code]name[/code] is already taken, this method fails. @@ -329,7 +358,7 @@ - + Renames [StyleBox] at [code]old_name[/code] to [code]name[/code] if the theme has [code]node_type[/code]. If [code]name[/code] is already taken, this method fails. @@ -339,7 +368,7 @@ - + Renames the theme item of [code]data_type[/code] at [code]old_name[/code] to [code]name[/code] if the theme has [code]node_type[/code]. If [code]name[/code] is already taken, this method fails. @@ -347,7 +376,7 @@ - + Sets the theme's [Color] to [code]color[/code] at [code]name[/code] in [code]node_type[/code]. @@ -357,7 +386,7 @@ - + Sets the theme's constant to [code]constant[/code] at [code]name[/code] in [code]node_type[/code]. @@ -367,7 +396,7 @@ - + Sets the theme's [Font] to [code]font[/code] at [code]name[/code] in [code]node_type[/code]. @@ -377,7 +406,7 @@ - + Sets the theme's icon [Texture] to [code]texture[/code] at [code]name[/code] in [code]node_type[/code]. @@ -387,7 +416,7 @@ - + Sets theme's [StyleBox] to [code]stylebox[/code] at [code]name[/code] in [code]node_type[/code]. @@ -398,7 +427,7 @@ - + Sets the theme item of [code]data_type[/code] to [code]value[/code] at [code]name[/code] in [code]node_type[/code]. @@ -406,6 +435,17 @@ Creates [code]node_type[/code] if the theme does not have it. + + + + + + Marks [code]theme_type[/code] as a variation of [code]base_type[/code]. + This adds [code]theme_type[/code] as a suggested option for [member Control.theme_type_variation] on a [Control] that is of the [code]base_type[/code] class. + Variations can also be nested, i.e. [code]base_type[/code] can be another variation. If a chain of variations ends with a [code]base_type[/code] matching the class of the [Control], the whole chain is going to be suggested as options. + [b]Note:[/b] Suggestions only show up if this theme resource is set as the project default theme. See [member ProjectSettings.gui/theme/custom]. + + diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 824ba85fb2fc..b8b11303d4b0 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -173,40 +173,152 @@ EditorPropertyMultilineText::EditorPropertyMultilineText() { ///////////////////// TEXT ENUM ///////////////////////// +void EditorPropertyTextEnum::_emit_changed_value(String p_string) { + emit_changed(get_edited_property(), p_string); +} + void EditorPropertyTextEnum::_option_selected(int p_which) { - emit_changed(get_edited_property(), options->get_item_text(p_which)); + _emit_changed_value(option_button->get_item_text(p_which)); +} + +void EditorPropertyTextEnum::_edit_custom_value() { + default_layout->hide(); + edit_custom_layout->show(); + custom_value_edit->grab_focus(); +} + +void EditorPropertyTextEnum::_custom_value_submitted(String p_value) { + edit_custom_layout->hide(); + default_layout->show(); + + _emit_changed_value(p_value.strip_edges()); +} + +void EditorPropertyTextEnum::_custom_value_accepted() { + String new_value = custom_value_edit->get_text().strip_edges(); + _custom_value_submitted(new_value); +} + +void EditorPropertyTextEnum::_custom_value_cancelled() { + custom_value_edit->set_text(get_edited_object()->get(get_edited_property())); + + edit_custom_layout->hide(); + default_layout->show(); } void EditorPropertyTextEnum::update_property() { - String which = get_edited_object()->get(get_edited_property()); - for (int i = 0; i < options->get_item_count(); i++) { - String t = options->get_item_text(i); - if (t == which) { - options->select(i); - return; + String current_value = get_edited_object()->get(get_edited_property()); + int default_option = options.find(current_value); + + // The list can change in the loose mode. + if (loose_mode) { + custom_value_edit->set_text(current_value); + option_button->clear(); + + // Manually entered value. + if (default_option < 0 && !current_value.empty()) { + option_button->add_item(current_value, options.size() + 1001); + option_button->select(0); + + option_button->add_separator(); } + + // Add an explicit empty value for clearing the property. + option_button->add_item("", options.size() + 1000); + + for (int i = 0; i < options.size(); i++) { + option_button->add_item(options[i], i); + if (options[i] == current_value) { + option_button->select(option_button->get_item_count() - 1); + } + } + } else { + option_button->select(default_option); } } -void EditorPropertyTextEnum::setup(const Vector &p_options) { +void EditorPropertyTextEnum::setup(const Vector &p_options, bool p_loose_mode) { + loose_mode = p_loose_mode; + + options.clear(); + + if (loose_mode) { + // Add an explicit empty value for clearing the property in the loose mode. + option_button->add_item("", options.size() + 1000); + } + for (int i = 0; i < p_options.size(); i++) { - options->add_item(p_options[i], i); + options.push_back(p_options[i]); + option_button->add_item(p_options[i], i); + } + + if (loose_mode) { + edit_button->show(); } } void EditorPropertyTextEnum::_bind_methods() { ClassDB::bind_method(D_METHOD("_option_selected"), &EditorPropertyTextEnum::_option_selected); + ClassDB::bind_method(D_METHOD("_edit_custom_value"), &EditorPropertyTextEnum::_edit_custom_value); + ClassDB::bind_method(D_METHOD("_custom_value_submitted"), &EditorPropertyTextEnum::_custom_value_submitted); + ClassDB::bind_method(D_METHOD("_custom_value_accepted"), &EditorPropertyTextEnum::_custom_value_accepted); + ClassDB::bind_method(D_METHOD("_custom_value_cancelled"), &EditorPropertyTextEnum::_custom_value_cancelled); } -EditorPropertyTextEnum::EditorPropertyTextEnum() { - options = memnew(OptionButton); - options->set_clip_text(true); - options->set_flat(true); +void EditorPropertyTextEnum::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: + edit_button->set_icon(get_icon("Edit", "EditorIcons")); + accept_button->set_icon(get_icon("ImportCheck", "EditorIcons")); + cancel_button->set_icon(get_icon("ImportFail", "EditorIcons")); + break; + } +} - add_child(options); - add_focusable(options); - options->connect("item_selected", this, "_option_selected"); +EditorPropertyTextEnum::EditorPropertyTextEnum() { + default_layout = memnew(HBoxContainer); + add_child(default_layout); + + edit_custom_layout = memnew(HBoxContainer); + edit_custom_layout->hide(); + add_child(edit_custom_layout); + + option_button = memnew(OptionButton); + option_button->set_h_size_flags(SIZE_EXPAND_FILL); + option_button->set_clip_text(true); + option_button->set_flat(true); + default_layout->add_child(option_button); + option_button->connect("item_selected", this, "_option_selected"); + + edit_button = memnew(Button); + edit_button->set_flat(true); + edit_button->hide(); + default_layout->add_child(edit_button); + edit_button->connect("pressed", this, "_edit_custom_value"); + + custom_value_edit = memnew(LineEdit); + custom_value_edit->set_h_size_flags(SIZE_EXPAND_FILL); + edit_custom_layout->add_child(custom_value_edit); + custom_value_edit->connect("text_entered", this, "_custom_value_submitted"); + + accept_button = memnew(Button); + accept_button->set_flat(true); + edit_custom_layout->add_child(accept_button); + accept_button->connect("pressed", this, "_custom_value_accepted"); + + cancel_button = memnew(Button); + cancel_button->set_flat(true); + edit_custom_layout->add_child(cancel_button); + cancel_button->connect("pressed", this, "_custom_value_cancelled"); + + add_focusable(option_button); + add_focusable(edit_button); + add_focusable(custom_value_edit); + add_focusable(accept_button); + add_focusable(cancel_button); } + ///////////////////// PATH ///////////////////////// void EditorPropertyPath::_path_selected(const String &p_path) { @@ -2728,10 +2840,10 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } } break; case Variant::STRING: { - if (p_hint == PROPERTY_HINT_ENUM) { + if (p_hint == PROPERTY_HINT_ENUM || p_hint == PROPERTY_HINT_ENUM_SUGGESTION) { EditorPropertyTextEnum *editor = memnew(EditorPropertyTextEnum); - Vector options = p_hint_text.split(","); - editor->setup(options); + Vector options = p_hint_text.split(",", false); + editor->setup(options, (p_hint == PROPERTY_HINT_ENUM_SUGGESTION)); add_property_editor(p_path, editor); } else if (p_hint == PROPERTY_HINT_MULTILINE_TEXT) { EditorPropertyMultilineText *editor = memnew(EditorPropertyMultilineText); diff --git a/editor/editor_properties.h b/editor/editor_properties.h index fa8d65738607..9acea06f0204 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -89,15 +89,34 @@ class EditorPropertyMultilineText : public EditorProperty { class EditorPropertyTextEnum : public EditorProperty { GDCLASS(EditorPropertyTextEnum, EditorProperty); - OptionButton *options; + HBoxContainer *default_layout; + HBoxContainer *edit_custom_layout; + + OptionButton *option_button; + Button *edit_button; + + LineEdit *custom_value_edit; + Button *accept_button; + Button *cancel_button; + + Vector options; + bool loose_mode = false; + + void _emit_changed_value(String p_string); void _option_selected(int p_which); + void _edit_custom_value(); + void _custom_value_submitted(String p_value); + void _custom_value_accepted(); + void _custom_value_cancelled(); + protected: static void _bind_methods(); + void _notification(int p_what); public: - void setup(const Vector &p_options); + void setup(const Vector &p_options, bool p_loose_mode = false); virtual void update_property(); EditorPropertyTextEnum(); }; diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index 0d48fbf568f2..4eadfe531556 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -2158,7 +2158,12 @@ OrderedHashMap ThemeTypeEditor::_get_type_items(String p_type_ if (include_default) { names.clear(); - (Theme::get_default().operator->()->*get_list_func)(p_type_name, &names); + String default_type = p_type_name; + if (edited_theme->get_type_variation_base(p_type_name) != StringName()) { + default_type = edited_theme->get_type_variation_base(p_type_name); + } + + (Theme::get_default().operator->()->*get_list_func)(default_type, &names); names.sort_custom(); for (List::Element *E = names.front(); E; E = E->next()) { items[E->get()] = false; @@ -2483,6 +2488,20 @@ void ThemeTypeEditor::_update_type_items() { stylebox_items_list->add_child(item_control); } } + + // Various type settings. + if (edited_type.empty() || ClassDB::class_exists(edited_type)) { + type_variation_edit->set_editable(false); + type_variation_edit->set_text(""); + type_variation_button->hide(); + type_variation_locked->set_visible(!edited_type.empty()); + } else { + type_variation_edit->set_editable(true); + type_variation_edit->set_text(edited_theme->get_type_variation_base(edited_type)); + _add_focusable(type_variation_edit); + type_variation_button->show(); + type_variation_locked->hide(); + } } void ThemeTypeEditor::_list_type_selected(int p_index) { @@ -2491,11 +2510,19 @@ void ThemeTypeEditor::_list_type_selected(int p_index) { } void ThemeTypeEditor::_add_type_button_cbk() { + add_type_mode = ADD_THEME_TYPE; + add_type_dialog->set_title(TTR("Add Item Type")); + add_type_dialog->get_ok()->set_text(TTR("Add Type")); + add_type_dialog->set_include_own_types(false); add_type_dialog->popup_centered(Size2(560, 420) * EDSCALE); } void ThemeTypeEditor::_add_default_type_items() { List names; + String default_type = edited_type; + if (edited_theme->get_type_variation_base(edited_type) != StringName()) { + default_type = edited_theme->get_type_variation_base(edited_type); + } updating = true; // Prevent changes from immediately being reported while the operation is still ongoing. @@ -2503,7 +2530,7 @@ void ThemeTypeEditor::_add_default_type_items() { { names.clear(); - Theme::get_default()->get_icon_list(edited_type, &names); + Theme::get_default()->get_icon_list(default_type, &names); for (List::Element *E = names.front(); E; E = E->next()) { if (!edited_theme->has_icon(E->get(), edited_type)) { edited_theme->set_icon(E->get(), edited_type, Ref()); @@ -2512,7 +2539,7 @@ void ThemeTypeEditor::_add_default_type_items() { } { names.clear(); - Theme::get_default()->get_stylebox_list(edited_type, &names); + Theme::get_default()->get_stylebox_list(default_type, &names); for (List::Element *E = names.front(); E; E = E->next()) { if (!edited_theme->has_stylebox(E->get(), edited_type)) { edited_theme->set_stylebox(E->get(), edited_type, Ref()); @@ -2521,7 +2548,7 @@ void ThemeTypeEditor::_add_default_type_items() { } { names.clear(); - Theme::get_default()->get_font_list(edited_type, &names); + Theme::get_default()->get_font_list(default_type, &names); for (List::Element *E = names.front(); E; E = E->next()) { if (!edited_theme->has_font(E->get(), edited_type)) { edited_theme->set_font(E->get(), edited_type, Ref()); @@ -2530,7 +2557,7 @@ void ThemeTypeEditor::_add_default_type_items() { } { names.clear(); - Theme::get_default()->get_color_list(edited_type, &names); + Theme::get_default()->get_color_list(default_type, &names); for (List::Element *E = names.front(); E; E = E->next()) { if (!edited_theme->has_color(E->get(), edited_type)) { edited_theme->set_color(E->get(), edited_type, Theme::get_default()->get_color(E->get(), edited_type)); @@ -2539,7 +2566,7 @@ void ThemeTypeEditor::_add_default_type_items() { } { names.clear(); - Theme::get_default()->get_constant_list(edited_type, &names); + Theme::get_default()->get_constant_list(default_type, &names); for (List::Element *E = names.front(); E; E = E->next()) { if (!edited_theme->has_constant(E->get(), edited_type)) { edited_theme->set_constant(E->get(), edited_type, Theme::get_default()->get_constant(E->get(), edited_type)); @@ -2817,8 +2844,28 @@ void ThemeTypeEditor::_update_stylebox_from_leading() { edited_theme->_unfreeze_and_propagate_changes(); } +void ThemeTypeEditor::_type_variation_changed(const String p_value) { + if (p_value.empty()) { + edited_theme->clear_type_variation(edited_type); + } else { + edited_theme->set_type_variation(edited_type, StringName(p_value)); + } +} + +void ThemeTypeEditor::_add_type_variation_cbk() { + add_type_mode = ADD_VARIATION_BASE; + add_type_dialog->set_title(TTR("Set Variation Base Type")); + add_type_dialog->get_ok()->set_text(TTR("Set Base Type")); + add_type_dialog->set_include_own_types(true); + add_type_dialog->popup_centered(Size2(560, 420) * EDSCALE); +} + void ThemeTypeEditor::_add_type_dialog_selected(const String p_type_name) { - select_type(p_type_name); + if (add_type_mode == ADD_THEME_TYPE) { + select_type(p_type_name); + } else if (add_type_mode == ADD_VARIATION_BASE) { + _type_variation_changed(p_type_name); + } } void ThemeTypeEditor::_notification(int p_what) { @@ -2832,9 +2879,12 @@ void ThemeTypeEditor::_notification(int p_what) { data_type_tabs->set_tab_icon(2, get_icon("Font", "EditorIcons")); data_type_tabs->set_tab_icon(3, get_icon("ImageTexture", "EditorIcons")); data_type_tabs->set_tab_icon(4, get_icon("StyleBoxFlat", "EditorIcons")); + data_type_tabs->set_tab_icon(5, get_icon("Tools", "EditorIcons")); data_type_tabs->add_style_override("tab_selected", get_stylebox("tab_selected_odd", "TabContainer")); data_type_tabs->add_style_override("panel", get_stylebox("panel_odd", "TabContainer")); + + type_variation_button->set_icon(get_icon("Add", "EditorIcons")); } break; } } @@ -2847,6 +2897,7 @@ void ThemeTypeEditor::_bind_methods() { ClassDB::bind_method("_list_type_selected", &ThemeTypeEditor::_list_type_selected); ClassDB::bind_method("_add_type_button_cbk", &ThemeTypeEditor::_add_type_button_cbk); + ClassDB::bind_method("_add_type_variation_cbk", &ThemeTypeEditor::_add_type_variation_cbk); ClassDB::bind_method("_add_type_dialog_selected", &ThemeTypeEditor::_add_type_dialog_selected); ClassDB::bind_method("_add_default_type_items", &ThemeTypeEditor::_add_default_type_items); @@ -2868,6 +2919,7 @@ void ThemeTypeEditor::_bind_methods() { ClassDB::bind_method("_pin_leading_stylebox", &ThemeTypeEditor::_pin_leading_stylebox); ClassDB::bind_method("_unpin_leading_stylebox", &ThemeTypeEditor::_unpin_leading_stylebox); ClassDB::bind_method("_update_stylebox_from_leading", &ThemeTypeEditor::_update_stylebox_from_leading); + ClassDB::bind_method("_type_variation_changed", &ThemeTypeEditor::_type_variation_changed); } void ThemeTypeEditor::set_edited_theme(const Ref &p_theme) { @@ -2878,6 +2930,8 @@ void ThemeTypeEditor::set_edited_theme(const Ref &p_theme) { edited_theme = p_theme; edited_theme->connect("changed", this, "_update_type_list_debounced"); _update_type_list(); + + add_type_dialog->set_edited_theme(edited_theme); } void ThemeTypeEditor::select_type(String p_type_name) { @@ -2956,6 +3010,45 @@ ThemeTypeEditor::ThemeTypeEditor() { icon_items_list = _create_item_list(Theme::DATA_TYPE_ICON); stylebox_items_list = _create_item_list(Theme::DATA_TYPE_STYLEBOX); + VBoxContainer *type_settings_tab = memnew(VBoxContainer); + type_settings_tab->set_custom_minimum_size(Size2(0, 160) * EDSCALE); + data_type_tabs->add_child(type_settings_tab); + data_type_tabs->set_tab_title(data_type_tabs->get_tab_count() - 1, ""); + + ScrollContainer *type_settings_sc = memnew(ScrollContainer); + type_settings_sc->set_v_size_flags(SIZE_EXPAND_FILL); + type_settings_sc->set_enable_h_scroll(false); + type_settings_tab->add_child(type_settings_sc); + VBoxContainer *type_settings_list = memnew(VBoxContainer); + type_settings_list->set_h_size_flags(SIZE_EXPAND_FILL); + type_settings_sc->add_child(type_settings_list); + + VBoxContainer *type_variation_vb = memnew(VBoxContainer); + type_settings_list->add_child(type_variation_vb); + + HBoxContainer *type_variation_hb = memnew(HBoxContainer); + type_variation_vb->add_child(type_variation_hb); + Label *type_variation_label = memnew(Label); + type_variation_hb->add_child(type_variation_label); + type_variation_label->set_text(TTR("Base Type")); + type_variation_label->set_h_size_flags(Control::SIZE_EXPAND_FILL); + type_variation_edit = memnew(LineEdit); + type_variation_hb->add_child(type_variation_edit); + type_variation_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL); + type_variation_edit->connect("text_changed", this, "_type_variation_changed"); + type_variation_edit->connect("focus_exited", this, "_update_type_items"); + type_variation_button = memnew(Button); + type_variation_hb->add_child(type_variation_button); + type_variation_button->set_tooltip(TTR("Select the variation base type from a list of available types.")); + type_variation_button->connect("pressed", this, "_add_type_variation_cbk"); + + type_variation_locked = memnew(Label); + type_variation_vb->add_child(type_variation_locked); + type_variation_locked->set_align(Label::ALIGN_CENTER); + type_variation_locked->set_autowrap(true); + type_variation_locked->set_text(TTR("A type associated with a built-in class cannot be marked as a variation of another type.")); + type_variation_locked->hide(); + add_type_dialog = memnew(ThemeTypeDialog); add_type_dialog->set_title(TTR("Add Item Type")); add_child(add_type_dialog); diff --git a/editor/plugins/theme_editor_plugin.h b/editor/plugins/theme_editor_plugin.h index 728f3898e1b1..6152449c7d0f 100644 --- a/editor/plugins/theme_editor_plugin.h +++ b/editor/plugins/theme_editor_plugin.h @@ -321,6 +321,16 @@ class ThemeTypeEditor : public MarginContainer { VBoxContainer *icon_items_list; VBoxContainer *stylebox_items_list; + LineEdit *type_variation_edit; + Button *type_variation_button; + Label *type_variation_locked; + + enum TypeDialogMode { + ADD_THEME_TYPE, + ADD_VARIATION_BASE, + }; + + TypeDialogMode add_type_mode = ADD_THEME_TYPE; ThemeTypeDialog *add_type_dialog; Vector focusables; @@ -357,6 +367,9 @@ class ThemeTypeEditor : public MarginContainer { void _unpin_leading_stylebox(); void _update_stylebox_from_leading(); + void _type_variation_changed(const String p_value); + void _add_type_variation_cbk(); + void _add_type_dialog_selected(const String p_type_name); protected: diff --git a/editor/plugins/theme_editor_preview.cpp b/editor/plugins/theme_editor_preview.cpp index dfb656ba0a19..a69e524b36cb 100644 --- a/editor/plugins/theme_editor_preview.cpp +++ b/editor/plugins/theme_editor_preview.cpp @@ -116,7 +116,11 @@ void ThemeEditorPreview::_draw_picker_overlay() { highlight_rect.position = picker_overlay->get_global_transform().affine_inverse().xform(highlight_rect.position); picker_overlay->draw_style_box(theme_cache.preview_picker_overlay, highlight_rect); - String highlight_name = hovered_control->get_class_name(); + String highlight_name = hovered_control->get_theme_type_variation(); + if (highlight_name == StringName()) { + highlight_name = hovered_control->get_class_name(); + } + Rect2 highlight_label_rect = highlight_rect; highlight_label_rect.size = theme_cache.preview_picker_font->get_string_size(highlight_name); @@ -147,7 +151,10 @@ void ThemeEditorPreview::_gui_input_picker_overlay(const Ref &p_even if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { if (hovered_control) { - StringName theme_type = hovered_control->get_class_name(); + StringName theme_type = hovered_control->get_theme_type_variation(); + if (theme_type == StringName()) { + theme_type = hovered_control->get_class_name(); + } emit_signal("control_picked", theme_type); picker_button->set_pressed(false); diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index c09ae5f3810c..b96b67b1274d 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -400,6 +400,34 @@ void Control::_get_property_list(List *p_list) const { } } +void Control::_validate_property(PropertyInfo &property) const { + if (property.name == "theme_type_variation") { + List names; + + // Only the default theme and the project theme are used for the list of options. + // This is an imposed limitation to simplify the logic needed to leverage those options. + Theme::get_default()->get_type_variation_list(get_class_name(), &names); + if (Theme::get_project_default().is_valid()) { + Theme::get_project_default()->get_type_variation_list(get_class_name(), &names); + } + names.sort_custom(); + + Vector unique_names; + String hint_string; + for (const List::Element *E = names.front(); E; E = E->next()) { + // Skip duplicate values. + if (unique_names.find(E->get()) != -1) { + continue; + } + + hint_string += String(E->get()) + ","; + unique_names.push_back(E->get()); + } + + property.hint_string = hint_string; + } +} + Control *Control::get_parent_control() const { return data.parent; } @@ -850,15 +878,19 @@ bool Control::has_theme_item_in_types(Control *p_theme_owner, Theme::DataType p_ } void Control::_get_theme_type_dependencies(const StringName &p_theme_type, List *p_list) const { - if (p_theme_type == StringName() || p_theme_type == get_class_name()) { - Theme::get_default()->get_type_dependencies(get_class_name(), p_list); + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { + if (Theme::get_project_default().is_valid() && Theme::get_project_default()->get_type_variation_base(data.theme_type_variation) != StringName()) { + Theme::get_project_default()->get_type_dependencies(get_class_name(), data.theme_type_variation, p_list); + } else { + Theme::get_default()->get_type_dependencies(get_class_name(), data.theme_type_variation, p_list); + } } else { - Theme::get_default()->get_type_dependencies(p_theme_type, p_list); + Theme::get_default()->get_type_dependencies(p_theme_type, StringName(), p_list); } } Ref Control::get_icon(const StringName &p_name, const StringName &p_theme_type) const { - if (p_theme_type == StringName() || p_theme_type == get_class_name()) { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { const Ref *tex = data.icon_override.getptr(p_name); if (tex) { return *tex; @@ -913,7 +945,7 @@ Ref Control::get_shader(const StringName &p_name, const StringName &p_th } Ref Control::get_stylebox(const StringName &p_name, const StringName &p_theme_type) const { - if (p_theme_type == StringName() || p_theme_type == get_class_name()) { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { const Ref *style = data.style_override.getptr(p_name); if (style) { return *style; @@ -926,7 +958,7 @@ Ref Control::get_stylebox(const StringName &p_name, const StringName & } Ref Control::get_font(const StringName &p_name, const StringName &p_theme_type) const { - if (p_theme_type == StringName() || p_theme_type == get_class_name()) { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { const Ref *font = data.font_override.getptr(p_name); if (font) { return *font; @@ -939,7 +971,7 @@ Ref Control::get_font(const StringName &p_name, const StringName &p_theme_ } Color Control::get_color(const StringName &p_name, const StringName &p_theme_type) const { - if (p_theme_type == StringName() || p_theme_type == get_class_name()) { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { const Color *color = data.color_override.getptr(p_name); if (color) { return *color; @@ -952,7 +984,7 @@ Color Control::get_color(const StringName &p_name, const StringName &p_theme_typ } int Control::get_constant(const StringName &p_name, const StringName &p_theme_type) const { - if (p_theme_type == StringName() || p_theme_type == get_class_name()) { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { const int *constant = data.constant_override.getptr(p_name); if (constant) { return *constant; @@ -995,7 +1027,7 @@ bool Control::has_constant_override(const StringName &p_name) const { } bool Control::has_icon(const StringName &p_name, const StringName &p_theme_type) const { - if (p_theme_type == StringName() || p_theme_type == get_class_name()) { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { if (has_icon_override(p_name)) { return true; } @@ -1046,7 +1078,7 @@ bool Control::has_shader(const StringName &p_name, const StringName &p_theme_typ } bool Control::has_stylebox(const StringName &p_name, const StringName &p_theme_type) const { - if (p_theme_type == StringName() || p_theme_type == get_class_name()) { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { if (has_stylebox_override(p_name)) { return true; } @@ -1058,7 +1090,7 @@ bool Control::has_stylebox(const StringName &p_name, const StringName &p_theme_t } bool Control::has_font(const StringName &p_name, const StringName &p_theme_type) const { - if (p_theme_type == StringName() || p_theme_type == get_class_name()) { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { if (has_font_override(p_name)) { return true; } @@ -1070,7 +1102,7 @@ bool Control::has_font(const StringName &p_name, const StringName &p_theme_type) } bool Control::has_color(const StringName &p_name, const StringName &p_theme_type) const { - if (p_theme_type == StringName() || p_theme_type == get_class_name()) { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { if (has_color_override(p_name)) { return true; } @@ -1082,7 +1114,7 @@ bool Control::has_color(const StringName &p_name, const StringName &p_theme_type } bool Control::has_constant(const StringName &p_name, const StringName &p_theme_type) const { - if (p_theme_type == StringName() || p_theme_type == get_class_name()) { + if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { if (has_constant_override(p_name)) { return true; } @@ -2086,16 +2118,25 @@ void Control::set_theme(const Ref &p_theme) { } } +Ref Control::get_theme() const { + return data.theme; +} + +void Control::set_theme_type_variation(const StringName &p_theme_type) { + data.theme_type_variation = p_theme_type; + _propagate_theme_changed(this, data.theme_owner); +} + +StringName Control::get_theme_type_variation() const { + return data.theme_type_variation; +} + void Control::accept_event() { if (is_inside_tree()) { get_viewport()->_gui_accept_event(); } } -Ref Control::get_theme() const { - return data.theme; -} - void Control::set_tooltip(const String &p_tooltip) { data.tooltip = p_tooltip; update_configuration_warning(); @@ -2637,6 +2678,9 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("set_theme", "theme"), &Control::set_theme); ClassDB::bind_method(D_METHOD("get_theme"), &Control::get_theme); + ClassDB::bind_method(D_METHOD("set_theme_type_variation", "theme_type"), &Control::set_theme_type_variation); + ClassDB::bind_method(D_METHOD("get_theme_type_variation"), &Control::get_theme_type_variation); + ClassDB::bind_method(D_METHOD("add_icon_override", "name", "texture"), &Control::add_icon_override); ClassDB::bind_method(D_METHOD("add_shader_override", "name", "shader"), &Control::add_shader_override); ClassDB::bind_method(D_METHOD("add_stylebox_override", "name", "stylebox"), &Control::add_style_override); @@ -2786,6 +2830,7 @@ void Control::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "size_flags_stretch_ratio", PROPERTY_HINT_RANGE, "0,20,0.01,or_greater"), "set_stretch_ratio", "get_stretch_ratio"); ADD_GROUP("Theme", ""); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "theme", PROPERTY_HINT_RESOURCE_TYPE, "Theme"), "set_theme", "get_theme"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "theme_type_variation", PROPERTY_HINT_ENUM_SUGGESTION), "set_theme_type_variation", "get_theme_type_variation"); BIND_ENUM_CONSTANT(FOCUS_NONE); BIND_ENUM_CONSTANT(FOCUS_CLICK); diff --git a/scene/gui/control.h b/scene/gui/control.h index c681e285f6f1..0a0fb1b58c29 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -181,6 +181,7 @@ class Control : public CanvasItem { uint64_t modal_frame; //frame used to put something as modal Ref theme; Control *theme_owner; + StringName theme_type_variation; String tooltip; CursorShape default_cursor; @@ -255,8 +256,8 @@ class Control : public CanvasItem { void _get_property_list(List *p_list) const; void _notification(int p_notification); - static void _bind_methods(); + virtual void _validate_property(PropertyInfo &property) const; //bind helpers @@ -379,6 +380,9 @@ class Control : public CanvasItem { void set_theme(const Ref &p_theme); Ref get_theme() const; + void set_theme_type_variation(const StringName &p_theme_type); + StringName get_theme_type_variation() const; + void set_h_size_flags(int p_flags); int get_h_size_flags() const; diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp index 88f7c46974c0..e189e4872d30 100644 --- a/scene/resources/theme.cpp +++ b/scene/resources/theme.cpp @@ -46,19 +46,21 @@ bool Theme::_set(const StringName &p_name, const Variant &p_value) { if (sname.find("/") != -1) { String type = sname.get_slicec('/', 1); - String node_type = sname.get_slicec('/', 0); + String theme_type = sname.get_slicec('/', 0); String name = sname.get_slicec('/', 2); if (type == "icons") { - set_icon(name, node_type, p_value); + set_icon(name, theme_type, p_value); } else if (type == "styles") { - set_stylebox(name, node_type, p_value); + set_stylebox(name, theme_type, p_value); } else if (type == "fonts") { - set_font(name, node_type, p_value); + set_font(name, theme_type, p_value); } else if (type == "colors") { - set_color(name, node_type, p_value); + set_color(name, theme_type, p_value); } else if (type == "constants") { - set_constant(name, node_type, p_value); + set_constant(name, theme_type, p_value); + } else if (type == "base_type") { + set_type_variation(theme_type, p_value); } else { return false; } @@ -74,31 +76,33 @@ bool Theme::_get(const StringName &p_name, Variant &r_ret) const { if (sname.find("/") != -1) { String type = sname.get_slicec('/', 1); - String node_type = sname.get_slicec('/', 0); + String theme_type = sname.get_slicec('/', 0); String name = sname.get_slicec('/', 2); if (type == "icons") { - if (!has_icon(name, node_type)) { + if (!has_icon(name, theme_type)) { r_ret = Ref(); } else { - r_ret = get_icon(name, node_type); + r_ret = get_icon(name, theme_type); } } else if (type == "styles") { - if (!has_stylebox(name, node_type)) { + if (!has_stylebox(name, theme_type)) { r_ret = Ref(); } else { - r_ret = get_stylebox(name, node_type); + r_ret = get_stylebox(name, theme_type); } } else if (type == "fonts") { - if (!has_font(name, node_type)) { + if (!has_font(name, theme_type)) { r_ret = Ref(); } else { - r_ret = get_font(name, node_type); + r_ret = get_font(name, theme_type); } } else if (type == "colors") { - r_ret = get_color(name, node_type); + r_ret = get_color(name, theme_type); } else if (type == "constants") { - r_ret = get_constant(name, node_type); + r_ret = get_constant(name, theme_type); + } else if (type == "base_type") { + r_ret = get_type_variation_base(theme_type); } else { return false; } @@ -114,6 +118,14 @@ void Theme::_get_property_list(List *p_list) const { const StringName *key = nullptr; + // Type variations. + while ((key = variation_map.next(key))) { + list.push_back(PropertyInfo(Variant::STRING, String() + *key + "/base_type")); + } + + key = nullptr; + + // Icons. while ((key = icon_map.next(key))) { const StringName *key2 = nullptr; @@ -124,6 +136,7 @@ void Theme::_get_property_list(List *p_list) const { key = nullptr; + // Styles. while ((key = style_map.next(key))) { const StringName *key2 = nullptr; @@ -134,6 +147,7 @@ void Theme::_get_property_list(List *p_list) const { key = nullptr; + // Fonts. while ((key = font_map.next(key))) { const StringName *key2 = nullptr; @@ -144,6 +158,7 @@ void Theme::_get_property_list(List *p_list) const { key = nullptr; + // Colors. while ((key = color_map.next(key))) { const StringName *key2 = nullptr; @@ -154,6 +169,7 @@ void Theme::_get_property_list(List *p_list) const { key = nullptr; + // Constants. while ((key = constant_map.next(key))) { const StringName *key2 = nullptr; @@ -226,81 +242,81 @@ bool Theme::has_default_theme_font() const { } // Icons. -void Theme::set_icon(const StringName &p_name, const StringName &p_node_type, const Ref &p_icon) { +void Theme::set_icon(const StringName &p_name, const StringName &p_theme_type, const Ref &p_icon) { bool existing = false; - if (icon_map[p_node_type].has(p_name) && icon_map[p_node_type][p_name].is_valid()) { + if (icon_map[p_theme_type].has(p_name) && icon_map[p_theme_type][p_name].is_valid()) { existing = true; - icon_map[p_node_type][p_name]->disconnect("changed", this, "_emit_theme_changed"); + icon_map[p_theme_type][p_name]->disconnect("changed", this, "_emit_theme_changed"); } - icon_map[p_node_type][p_name] = p_icon; + icon_map[p_theme_type][p_name] = p_icon; if (p_icon.is_valid()) { - icon_map[p_node_type][p_name]->connect("changed", this, "_emit_theme_changed", varray(), CONNECT_REFERENCE_COUNTED); + icon_map[p_theme_type][p_name]->connect("changed", this, "_emit_theme_changed", varray(), CONNECT_REFERENCE_COUNTED); } _emit_theme_changed(!existing); } -Ref Theme::get_icon(const StringName &p_name, const StringName &p_node_type) const { - if (icon_map.has(p_node_type) && icon_map[p_node_type].has(p_name) && icon_map[p_node_type][p_name].is_valid()) { - return icon_map[p_node_type][p_name]; +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_node_type) const { - return (icon_map.has(p_node_type) && icon_map[p_node_type].has(p_name) && icon_map[p_node_type][p_name].is_valid()); +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_node_type) const { - return (icon_map.has(p_node_type) && icon_map[p_node_type].has(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)); } -void Theme::rename_icon(const StringName &p_old_name, const StringName &p_name, const StringName &p_node_type) { - ERR_FAIL_COND_MSG(!icon_map.has(p_node_type), "Cannot rename the icon '" + String(p_old_name) + "' because the node type '" + String(p_node_type) + "' does not exist."); - ERR_FAIL_COND_MSG(icon_map[p_node_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_node_type].has(p_old_name), "Cannot rename the icon '" + String(p_old_name) + "' because it does not exist."); +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_node_type][p_name] = icon_map[p_node_type][p_old_name]; - icon_map[p_node_type].erase(p_old_name); + 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(true); } -void Theme::clear_icon(const StringName &p_name, const StringName &p_node_type) { - ERR_FAIL_COND_MSG(!icon_map.has(p_node_type), "Cannot clear the icon '" + String(p_name) + "' because the node type '" + String(p_node_type) + "' does not exist."); - ERR_FAIL_COND_MSG(!icon_map[p_node_type].has(p_name), "Cannot clear the icon '" + String(p_name) + "' because it does not exist."); +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_node_type][p_name].is_valid()) { - icon_map[p_node_type][p_name]->disconnect("changed", this, "_emit_theme_changed"); + if (icon_map[p_theme_type][p_name].is_valid()) { + icon_map[p_theme_type][p_name]->disconnect("changed", this, "_emit_theme_changed"); } - icon_map[p_node_type].erase(p_name); + icon_map[p_theme_type].erase(p_name); _emit_theme_changed(true); } -void Theme::get_icon_list(StringName p_node_type, List *p_list) const { +void Theme::get_icon_list(StringName p_theme_type, List *p_list) const { ERR_FAIL_NULL(p_list); - if (!icon_map.has(p_node_type)) { + if (!icon_map.has(p_theme_type)) { return; } const StringName *key = nullptr; - while ((key = icon_map[p_node_type].next(key))) { + while ((key = icon_map[p_theme_type].next(key))) { p_list->push_back(*key); } } -void Theme::add_icon_type(const StringName &p_node_type) { - if (icon_map.has(p_node_type)) { +void Theme::add_icon_type(const StringName &p_theme_type) { + if (icon_map.has(p_theme_type)) { return; } - icon_map[p_node_type] = HashMap>(); + icon_map[p_theme_type] = HashMap>(); } void Theme::get_icon_types(List *p_list) const { @@ -313,124 +329,124 @@ void Theme::get_icon_types(List *p_list) const { } // Shaders. -void Theme::set_shader(const StringName &p_name, const StringName &p_node_type, const Ref &p_shader) { - bool existing = (shader_map.has(p_node_type) && shader_map[p_node_type].has(p_name)); - shader_map[p_node_type][p_name] = p_shader; +void Theme::set_shader(const StringName &p_name, const StringName &p_theme_type, const Ref &p_shader) { + bool existing = (shader_map.has(p_theme_type) && shader_map[p_theme_type].has(p_name)); + shader_map[p_theme_type][p_name] = p_shader; _emit_theme_changed(!existing); } -Ref Theme::get_shader(const StringName &p_name, const StringName &p_node_type) const { - if (shader_map.has(p_node_type) && shader_map[p_node_type].has(p_name) && shader_map[p_node_type][p_name].is_valid()) { - return shader_map[p_node_type][p_name]; +Ref Theme::get_shader(const StringName &p_name, const StringName &p_theme_type) const { + if (shader_map.has(p_theme_type) && shader_map[p_theme_type].has(p_name) && shader_map[p_theme_type][p_name].is_valid()) { + return shader_map[p_theme_type][p_name]; } else { return nullptr; } } -bool Theme::has_shader(const StringName &p_name, const StringName &p_node_type) const { - return (shader_map.has(p_node_type) && shader_map[p_node_type].has(p_name) && shader_map[p_node_type][p_name].is_valid()); +bool Theme::has_shader(const StringName &p_name, const StringName &p_theme_type) const { + return (shader_map.has(p_theme_type) && shader_map[p_theme_type].has(p_name) && shader_map[p_theme_type][p_name].is_valid()); } -void Theme::clear_shader(const StringName &p_name, const StringName &p_node_type) { - ERR_FAIL_COND(!shader_map.has(p_node_type)); - ERR_FAIL_COND(!shader_map[p_node_type].has(p_name)); +void Theme::clear_shader(const StringName &p_name, const StringName &p_theme_type) { + ERR_FAIL_COND(!shader_map.has(p_theme_type)); + ERR_FAIL_COND(!shader_map[p_theme_type].has(p_name)); - shader_map[p_node_type].erase(p_name); + shader_map[p_theme_type].erase(p_name); _emit_theme_changed(true); } -void Theme::get_shader_list(const StringName &p_node_type, List *p_list) const { +void Theme::get_shader_list(const StringName &p_theme_type, List *p_list) const { ERR_FAIL_NULL(p_list); - if (!shader_map.has(p_node_type)) { + if (!shader_map.has(p_theme_type)) { return; } const StringName *key = nullptr; - while ((key = shader_map[p_node_type].next(key))) { + while ((key = shader_map[p_theme_type].next(key))) { p_list->push_back(*key); } } // Styleboxes. -void Theme::set_stylebox(const StringName &p_name, const StringName &p_node_type, const Ref &p_style) { +void Theme::set_stylebox(const StringName &p_name, const StringName &p_theme_type, const Ref &p_style) { bool existing = false; - if (style_map[p_node_type].has(p_name) && style_map[p_node_type][p_name].is_valid()) { + if (style_map[p_theme_type].has(p_name) && style_map[p_theme_type][p_name].is_valid()) { existing = true; - style_map[p_node_type][p_name]->disconnect("changed", this, "_emit_theme_changed"); + style_map[p_theme_type][p_name]->disconnect("changed", this, "_emit_theme_changed"); } - style_map[p_node_type][p_name] = p_style; + style_map[p_theme_type][p_name] = p_style; if (p_style.is_valid()) { - style_map[p_node_type][p_name]->connect("changed", this, "_emit_theme_changed", varray(), CONNECT_REFERENCE_COUNTED); + style_map[p_theme_type][p_name]->connect("changed", this, "_emit_theme_changed", varray(), CONNECT_REFERENCE_COUNTED); } _emit_theme_changed(!existing); } -Ref Theme::get_stylebox(const StringName &p_name, const StringName &p_node_type) const { - if (style_map.has(p_node_type) && style_map[p_node_type].has(p_name) && style_map[p_node_type][p_name].is_valid()) { - return style_map[p_node_type][p_name]; +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_node_type) const { - return (style_map.has(p_node_type) && style_map[p_node_type].has(p_name) && style_map[p_node_type][p_name].is_valid()); +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_node_type) const { - return (style_map.has(p_node_type) && style_map[p_node_type].has(p_name)); +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_node_type) { - ERR_FAIL_COND_MSG(!style_map.has(p_node_type), "Cannot rename the stylebox '" + String(p_old_name) + "' because the node type '" + String(p_node_type) + "' does not exist."); - ERR_FAIL_COND_MSG(style_map[p_node_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_node_type].has(p_old_name), "Cannot rename the stylebox '" + String(p_old_name) + "' because it does not exist."); +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_node_type][p_name] = style_map[p_node_type][p_old_name]; - style_map[p_node_type].erase(p_old_name); + 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(true); } -void Theme::clear_stylebox(const StringName &p_name, const StringName &p_node_type) { - ERR_FAIL_COND_MSG(!style_map.has(p_node_type), "Cannot clear the stylebox '" + String(p_name) + "' because the node type '" + String(p_node_type) + "' does not exist."); - ERR_FAIL_COND_MSG(!style_map[p_node_type].has(p_name), "Cannot clear the stylebox '" + String(p_name) + "' because it does not exist."); +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_node_type][p_name].is_valid()) { - style_map[p_node_type][p_name]->disconnect("changed", this, "_emit_theme_changed"); + if (style_map[p_theme_type][p_name].is_valid()) { + style_map[p_theme_type][p_name]->disconnect("changed", this, "_emit_theme_changed"); } - style_map[p_node_type].erase(p_name); + style_map[p_theme_type].erase(p_name); _emit_theme_changed(true); } -void Theme::get_stylebox_list(StringName p_node_type, List *p_list) const { +void Theme::get_stylebox_list(StringName p_theme_type, List *p_list) const { ERR_FAIL_NULL(p_list); - if (!style_map.has(p_node_type)) { + if (!style_map.has(p_theme_type)) { return; } const StringName *key = nullptr; - while ((key = style_map[p_node_type].next(key))) { + while ((key = style_map[p_theme_type].next(key))) { p_list->push_back(*key); } } -void Theme::add_stylebox_type(const StringName &p_node_type) { - if (style_map.has(p_node_type)) { +void Theme::add_stylebox_type(const StringName &p_theme_type) { + if (style_map.has(p_theme_type)) { return; } - style_map[p_node_type] = HashMap>(); + style_map[p_theme_type] = HashMap>(); } void Theme::get_stylebox_types(List *p_list) const { @@ -443,25 +459,25 @@ void Theme::get_stylebox_types(List *p_list) const { } // Fonts. -void Theme::set_font(const StringName &p_name, const StringName &p_node_type, const Ref &p_font) { +void Theme::set_font(const StringName &p_name, const StringName &p_theme_type, const Ref &p_font) { bool existing = false; - if (font_map[p_node_type][p_name].is_valid()) { + if (font_map[p_theme_type][p_name].is_valid()) { existing = true; - font_map[p_node_type][p_name]->disconnect("changed", this, "_emit_theme_changed"); + font_map[p_theme_type][p_name]->disconnect("changed", this, "_emit_theme_changed"); } - font_map[p_node_type][p_name] = p_font; + font_map[p_theme_type][p_name] = p_font; if (p_font.is_valid()) { - font_map[p_node_type][p_name]->connect("changed", this, "_emit_theme_changed", varray(), CONNECT_REFERENCE_COUNTED); + font_map[p_theme_type][p_name]->connect("changed", this, "_emit_theme_changed", varray(), CONNECT_REFERENCE_COUNTED); } _emit_theme_changed(!existing); } -Ref Theme::get_font(const StringName &p_name, const StringName &p_node_type) const { - if (font_map.has(p_node_type) && font_map[p_node_type].has(p_name) && font_map[p_node_type][p_name].is_valid()) { - return font_map[p_node_type][p_name]; +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 { @@ -469,57 +485,57 @@ Ref Theme::get_font(const StringName &p_name, const StringName &p_node_typ } } -bool Theme::has_font(const StringName &p_name, const StringName &p_node_type) const { - return ((font_map.has(p_node_type) && font_map[p_node_type].has(p_name) && font_map[p_node_type][p_name].is_valid()) || has_default_theme_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_node_type) const { - return (font_map.has(p_node_type) && font_map[p_node_type].has(p_name)); +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_node_type) { - ERR_FAIL_COND_MSG(!font_map.has(p_node_type), "Cannot rename the font '" + String(p_old_name) + "' because the node type '" + String(p_node_type) + "' does not exist."); - ERR_FAIL_COND_MSG(font_map[p_node_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_node_type].has(p_old_name), "Cannot rename the font '" + String(p_old_name) + "' because it does not exist."); +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_node_type][p_name] = font_map[p_node_type][p_old_name]; - font_map[p_node_type].erase(p_old_name); + 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(true); } -void Theme::clear_font(const StringName &p_name, const StringName &p_node_type) { - ERR_FAIL_COND_MSG(!font_map.has(p_node_type), "Cannot clear the font '" + String(p_name) + "' because the node type '" + String(p_node_type) + "' does not exist."); - ERR_FAIL_COND_MSG(!font_map[p_node_type].has(p_name), "Cannot clear the font '" + String(p_name) + "' because it does not exist."); +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_node_type][p_name].is_valid()) { - font_map[p_node_type][p_name]->disconnect("changed", this, "_emit_theme_changed"); + if (font_map[p_theme_type][p_name].is_valid()) { + font_map[p_theme_type][p_name]->disconnect("changed", this, "_emit_theme_changed"); } - font_map[p_node_type].erase(p_name); + font_map[p_theme_type].erase(p_name); _emit_theme_changed(true); } -void Theme::get_font_list(StringName p_node_type, List *p_list) const { +void Theme::get_font_list(StringName p_theme_type, List *p_list) const { ERR_FAIL_NULL(p_list); - if (!font_map.has(p_node_type)) { + if (!font_map.has(p_theme_type)) { return; } const StringName *key = nullptr; - while ((key = font_map[p_node_type].next(key))) { + while ((key = font_map[p_theme_type].next(key))) { p_list->push_back(*key); } } -void Theme::add_font_type(const StringName &p_node_type) { - if (font_map.has(p_node_type)) { +void Theme::add_font_type(const StringName &p_theme_type) { + if (font_map.has(p_theme_type)) { return; } - font_map[p_node_type] = HashMap>(); + font_map[p_theme_type] = HashMap>(); } void Theme::get_font_types(List *p_list) const { @@ -532,68 +548,68 @@ void Theme::get_font_types(List *p_list) const { } // Colors. -void Theme::set_color(const StringName &p_name, const StringName &p_node_type, const Color &p_color) { - bool existing = has_color_nocheck(p_name, p_node_type); - color_map[p_node_type][p_name] = p_color; +void Theme::set_color(const StringName &p_name, const StringName &p_theme_type, const Color &p_color) { + bool existing = has_color_nocheck(p_name, p_theme_type); + color_map[p_theme_type][p_name] = p_color; _emit_theme_changed(!existing); } -Color Theme::get_color(const StringName &p_name, const StringName &p_node_type) const { - if (color_map.has(p_node_type) && color_map[p_node_type].has(p_name)) { - return color_map[p_node_type][p_name]; +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_node_type) const { - return (color_map.has(p_node_type) && color_map[p_node_type].has(p_name)); +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_node_type) const { - return (color_map.has(p_node_type) && color_map[p_node_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_node_type) { - ERR_FAIL_COND_MSG(!color_map.has(p_node_type), "Cannot rename the color '" + String(p_old_name) + "' because the node type '" + String(p_node_type) + "' does not exist."); - ERR_FAIL_COND_MSG(color_map[p_node_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_node_type].has(p_old_name), "Cannot rename the color '" + String(p_old_name) + "' because it does not exist."); +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_node_type][p_name] = color_map[p_node_type][p_old_name]; - color_map[p_node_type].erase(p_old_name); + 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(true); } -void Theme::clear_color(const StringName &p_name, const StringName &p_node_type) { - ERR_FAIL_COND_MSG(!color_map.has(p_node_type), "Cannot clear the color '" + String(p_name) + "' because the node type '" + String(p_node_type) + "' does not exist."); - ERR_FAIL_COND_MSG(!color_map[p_node_type].has(p_name), "Cannot clear the color '" + String(p_name) + "' because it does not exist."); +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_node_type].erase(p_name); + color_map[p_theme_type].erase(p_name); _emit_theme_changed(true); } -void Theme::get_color_list(StringName p_node_type, List *p_list) const { +void Theme::get_color_list(StringName p_theme_type, List *p_list) const { ERR_FAIL_NULL(p_list); - if (!color_map.has(p_node_type)) { + if (!color_map.has(p_theme_type)) { return; } const StringName *key = nullptr; - while ((key = color_map[p_node_type].next(key))) { + while ((key = color_map[p_theme_type].next(key))) { p_list->push_back(*key); } } -void Theme::add_color_type(const StringName &p_node_type) { - if (color_map.has(p_node_type)) { +void Theme::add_color_type(const StringName &p_theme_type) { + if (color_map.has(p_theme_type)) { return; } - color_map[p_node_type] = HashMap(); + color_map[p_theme_type] = HashMap(); } void Theme::get_color_types(List *p_list) const { @@ -606,68 +622,68 @@ void Theme::get_color_types(List *p_list) const { } // Theme constants. -void Theme::set_constant(const StringName &p_name, const StringName &p_node_type, int p_constant) { - bool existing = has_constant_nocheck(p_name, p_node_type); - constant_map[p_node_type][p_name] = p_constant; +void Theme::set_constant(const StringName &p_name, const StringName &p_theme_type, int p_constant) { + bool existing = has_constant_nocheck(p_name, p_theme_type); + constant_map[p_theme_type][p_name] = p_constant; _emit_theme_changed(!existing); } -int Theme::get_constant(const StringName &p_name, const StringName &p_node_type) const { - if (constant_map.has(p_node_type) && constant_map[p_node_type].has(p_name)) { - return constant_map[p_node_type][p_name]; +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_node_type) const { - return (constant_map.has(p_node_type) && constant_map[p_node_type].has(p_name)); +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_node_type) const { - return (constant_map.has(p_node_type) && constant_map[p_node_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_node_type) { - ERR_FAIL_COND_MSG(!constant_map.has(p_node_type), "Cannot rename the constant '" + String(p_old_name) + "' because the node type '" + String(p_node_type) + "' does not exist."); - ERR_FAIL_COND_MSG(constant_map[p_node_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_node_type].has(p_old_name), "Cannot rename the constant '" + String(p_old_name) + "' because it does not exist."); +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_node_type][p_name] = constant_map[p_node_type][p_old_name]; - constant_map[p_node_type].erase(p_old_name); + 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(true); } -void Theme::clear_constant(const StringName &p_name, const StringName &p_node_type) { - ERR_FAIL_COND_MSG(!constant_map.has(p_node_type), "Cannot clear the constant '" + String(p_name) + "' because the node type '" + String(p_node_type) + "' does not exist."); - ERR_FAIL_COND_MSG(!constant_map[p_node_type].has(p_name), "Cannot clear the constant '" + String(p_name) + "' because it does not exist."); +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_node_type].erase(p_name); + constant_map[p_theme_type].erase(p_name); _emit_theme_changed(true); } -void Theme::get_constant_list(StringName p_node_type, List *p_list) const { +void Theme::get_constant_list(StringName p_theme_type, List *p_list) const { ERR_FAIL_NULL(p_list); - if (!constant_map.has(p_node_type)) { + if (!constant_map.has(p_theme_type)) { return; } const StringName *key = nullptr; - while ((key = constant_map[p_node_type].next(key))) { + while ((key = constant_map[p_theme_type].next(key))) { p_list->push_back(*key); } } -void Theme::add_constant_type(const StringName &p_node_type) { - if (constant_map.has(p_node_type)) { +void Theme::add_constant_type(const StringName &p_theme_type) { + if (constant_map.has(p_theme_type)) { return; } - constant_map[p_node_type] = HashMap(); + constant_map[p_theme_type] = HashMap(); } void Theme::get_constant_types(List *p_list) const { @@ -680,55 +696,55 @@ void Theme::get_constant_types(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_node_type, const Variant &p_value) { +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_node_type, color_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_node_type, constant_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); - set_font(p_name, p_node_type, font_value); + set_font(p_name, p_theme_type, font_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); - set_icon(p_name, p_node_type, icon_value); + 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); - set_stylebox(p_name, p_node_type, stylebox_value); + 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_node_type) const { +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_node_type); + return get_color(p_name, p_theme_type); case DATA_TYPE_CONSTANT: - return get_constant(p_name, p_node_type); + return get_constant(p_name, p_theme_type); case DATA_TYPE_FONT: - return get_font(p_name, p_node_type); + return get_font(p_name, p_theme_type); case DATA_TYPE_ICON: - return get_icon(p_name, p_node_type); + return get_icon(p_name, p_theme_type); case DATA_TYPE_STYLEBOX: - return get_stylebox(p_name, p_node_type); + return get_stylebox(p_name, p_theme_type); case DATA_TYPE_MAX: break; // Can't happen, but silences warning. } @@ -736,18 +752,18 @@ Variant Theme::get_theme_item(DataType p_data_type, const StringName &p_name, co return Variant(); } -bool Theme::has_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_node_type) const { +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_node_type); + return has_color(p_name, p_theme_type); case DATA_TYPE_CONSTANT: - return has_constant(p_name, p_node_type); + return has_constant(p_name, p_theme_type); case DATA_TYPE_FONT: - return has_font(p_name, p_node_type); + return has_font(p_name, p_theme_type); case DATA_TYPE_ICON: - return has_icon(p_name, p_node_type); + return has_icon(p_name, p_theme_type); case DATA_TYPE_STYLEBOX: - return has_stylebox(p_name, p_node_type); + return has_stylebox(p_name, p_theme_type); case DATA_TYPE_MAX: break; // Can't happen, but silences warning. } @@ -755,18 +771,18 @@ bool Theme::has_theme_item(DataType p_data_type, const StringName &p_name, const return false; } -bool Theme::has_theme_item_nocheck(DataType p_data_type, const StringName &p_name, const StringName &p_node_type) const { +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_node_type); + return has_color_nocheck(p_name, p_theme_type); case DATA_TYPE_CONSTANT: - return has_constant_nocheck(p_name, p_node_type); + return has_constant_nocheck(p_name, p_theme_type); case DATA_TYPE_FONT: - return has_font_nocheck(p_name, p_node_type); + return has_font_nocheck(p_name, p_theme_type); case DATA_TYPE_ICON: - return has_icon_nocheck(p_name, p_node_type); + return has_icon_nocheck(p_name, p_theme_type); case DATA_TYPE_STYLEBOX: - return has_stylebox_nocheck(p_name, p_node_type); + return has_stylebox_nocheck(p_name, p_theme_type); case DATA_TYPE_MAX: break; // Can't happen, but silences warning. } @@ -774,88 +790,88 @@ bool Theme::has_theme_item_nocheck(DataType p_data_type, const StringName &p_nam return false; } -void Theme::rename_theme_item(DataType p_data_type, const StringName &p_old_name, const StringName &p_name, const StringName &p_node_type) { +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_node_type); + rename_color(p_old_name, p_name, p_theme_type); break; case DATA_TYPE_CONSTANT: - rename_constant(p_old_name, p_name, p_node_type); + rename_constant(p_old_name, p_name, p_theme_type); break; case DATA_TYPE_FONT: - rename_font(p_old_name, p_name, p_node_type); + rename_font(p_old_name, p_name, p_theme_type); break; case DATA_TYPE_ICON: - rename_icon(p_old_name, p_name, p_node_type); + rename_icon(p_old_name, p_name, p_theme_type); break; case DATA_TYPE_STYLEBOX: - rename_stylebox(p_old_name, p_name, p_node_type); + 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_node_type) { +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_node_type); + clear_color(p_name, p_theme_type); break; case DATA_TYPE_CONSTANT: - clear_constant(p_name, p_node_type); + clear_constant(p_name, p_theme_type); break; case DATA_TYPE_FONT: - clear_font(p_name, p_node_type); + clear_font(p_name, p_theme_type); break; case DATA_TYPE_ICON: - clear_icon(p_name, p_node_type); + clear_icon(p_name, p_theme_type); break; case DATA_TYPE_STYLEBOX: - clear_stylebox(p_name, p_node_type); + 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_node_type, List *p_list) const { +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_node_type, p_list); + get_color_list(p_theme_type, p_list); break; case DATA_TYPE_CONSTANT: - get_constant_list(p_node_type, p_list); + get_constant_list(p_theme_type, p_list); break; case DATA_TYPE_FONT: - get_font_list(p_node_type, p_list); + get_font_list(p_theme_type, p_list); break; case DATA_TYPE_ICON: - get_icon_list(p_node_type, p_list); + get_icon_list(p_theme_type, p_list); break; case DATA_TYPE_STYLEBOX: - get_stylebox_list(p_node_type, p_list); + 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_node_type) { +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_node_type); + add_color_type(p_theme_type); break; case DATA_TYPE_CONSTANT: - add_constant_type(p_node_type); + add_constant_type(p_theme_type); break; case DATA_TYPE_FONT: - add_font_type(p_node_type); + add_font_type(p_theme_type); break; case DATA_TYPE_ICON: - add_icon_type(p_node_type); + add_icon_type(p_theme_type); break; case DATA_TYPE_STYLEBOX: - add_stylebox_type(p_node_type); + add_stylebox_type(p_theme_type); break; case DATA_TYPE_MAX: break; // Can't happen, but silences warning. @@ -884,6 +900,64 @@ void Theme::get_theme_item_types(DataType p_data_type, List *p_list) } } +// 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(true); +} + +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(true); +} + +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 List::Element *E = variation_base_map[p_base_type].front(); E; E = E->next()) { + // 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->get())) { + continue; + } + + p_list->push_back(E->get()); + // Continue looking for sub-variations. + get_type_variation_list(E->get(), p_list); + } +} + // Theme types. void Theme::get_type_list(List *p_list) const { ERR_FAIL_NULL(p_list); @@ -924,10 +998,24 @@ void Theme::get_type_list(List *p_list) const { } } -void Theme::get_type_dependencies(const StringName &p_base_type, List *p_list) { +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 using native class hierarchy. + // 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); @@ -936,11 +1024,11 @@ void Theme::get_type_dependencies(const StringName &p_base_type, List Theme::_get_icon_list(const String &p_node_type) const { +PoolVector Theme::_get_icon_list(const String &p_theme_type) const { PoolVector ilret; List il; - get_icon_list(p_node_type, &il); + get_icon_list(p_theme_type, &il); ilret.resize(il.size()); int i = 0; @@ -966,11 +1054,11 @@ PoolVector Theme::_get_icon_types() const { return ilret; } -PoolVector Theme::_get_stylebox_list(const String &p_node_type) const { +PoolVector Theme::_get_stylebox_list(const String &p_theme_type) const { PoolVector ilret; List il; - get_stylebox_list(p_node_type, &il); + get_stylebox_list(p_theme_type, &il); ilret.resize(il.size()); int i = 0; @@ -996,11 +1084,11 @@ PoolVector Theme::_get_stylebox_types() const { return ilret; } -PoolVector Theme::_get_font_list(const String &p_node_type) const { +PoolVector Theme::_get_font_list(const String &p_theme_type) const { PoolVector ilret; List il; - get_font_list(p_node_type, &il); + get_font_list(p_theme_type, &il); ilret.resize(il.size()); int i = 0; @@ -1026,11 +1114,11 @@ PoolVector Theme::_get_font_types() const { return ilret; } -PoolVector Theme::_get_color_list(const String &p_node_type) const { +PoolVector Theme::_get_color_list(const String &p_theme_type) const { PoolVector ilret; List il; - get_color_list(p_node_type, &il); + get_color_list(p_theme_type, &il); ilret.resize(il.size()); int i = 0; @@ -1056,11 +1144,11 @@ PoolVector Theme::_get_color_types() const { return ilret; } -PoolVector Theme::_get_constant_list(const String &p_node_type) const { +PoolVector Theme::_get_constant_list(const String &p_theme_type) const { PoolVector ilret; List il; - get_constant_list(p_node_type, &il); + get_constant_list(p_theme_type, &il); ilret.resize(il.size()); int i = 0; @@ -1086,18 +1174,18 @@ PoolVector Theme::_get_constant_types() const { return ilret; } -PoolVector Theme::_get_theme_item_list(DataType p_data_type, const String &p_node_type) const { +PoolVector 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_node_type); + return _get_color_list(p_theme_type); case DATA_TYPE_CONSTANT: - return _get_constant_list(p_node_type); + return _get_constant_list(p_theme_type); case DATA_TYPE_FONT: - return _get_font_list(p_node_type); + return _get_font_list(p_theme_type); case DATA_TYPE_ICON: - return _get_icon_list(p_node_type); + return _get_icon_list(p_theme_type); case DATA_TYPE_STYLEBOX: - return _get_stylebox_list(p_node_type); + return _get_stylebox_list(p_theme_type); case DATA_TYPE_MAX: break; // Can't happen, but silences warning. } @@ -1124,7 +1212,22 @@ PoolVector Theme::_get_theme_item_types(DataType p_data_type) const { return PoolVector(); } -PoolVector Theme::_get_type_list(const String &p_node_type) const { +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()); + + int i = 0; + String *w = ilret.ptrw(); + for (List::Element *E = il.front(); E; E = E->next(), i++) { + w[i] = E->get(); + } + return ilret; +} + +PoolVector Theme::_get_type_list(const String &p_theme_type) const { PoolVector ilret; List il; @@ -1285,6 +1388,14 @@ void Theme::merge_with(const Ref &p_other) { } } + // Type variations. + { + const StringName *K = nullptr; + while ((K = p_other->variation_map.next(K))) { + set_type_variation(*K, p_other->variation_map[*K]); + } + } + _unfreeze_and_propagate_changes(); } @@ -1336,63 +1447,72 @@ void Theme::clear() { color_map.clear(); constant_map.clear(); + variation_map.clear(); + variation_base_map.clear(); + _emit_theme_changed(true); } void Theme::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_icon", "name", "node_type", "texture"), &Theme::set_icon); - ClassDB::bind_method(D_METHOD("get_icon", "name", "node_type"), &Theme::get_icon); - ClassDB::bind_method(D_METHOD("has_icon", "name", "node_type"), &Theme::has_icon); - ClassDB::bind_method(D_METHOD("rename_icon", "old_name", "name", "node_type"), &Theme::rename_icon); - ClassDB::bind_method(D_METHOD("clear_icon", "name", "node_type"), &Theme::clear_icon); - ClassDB::bind_method(D_METHOD("get_icon_list", "node_type"), &Theme::_get_icon_list); + ClassDB::bind_method(D_METHOD("set_icon", "name", "theme_type", "texture"), &Theme::set_icon); + ClassDB::bind_method(D_METHOD("get_icon", "name", "theme_type"), &Theme::get_icon); + ClassDB::bind_method(D_METHOD("has_icon", "name", "theme_type"), &Theme::has_icon); + ClassDB::bind_method(D_METHOD("rename_icon", "old_name", "name", "theme_type"), &Theme::rename_icon); + ClassDB::bind_method(D_METHOD("clear_icon", "name", "theme_type"), &Theme::clear_icon); + ClassDB::bind_method(D_METHOD("get_icon_list", "theme_type"), &Theme::_get_icon_list); ClassDB::bind_method(D_METHOD("get_icon_types"), &Theme::_get_icon_types); - ClassDB::bind_method(D_METHOD("set_stylebox", "name", "node_type", "texture"), &Theme::set_stylebox); - ClassDB::bind_method(D_METHOD("get_stylebox", "name", "node_type"), &Theme::get_stylebox); - ClassDB::bind_method(D_METHOD("has_stylebox", "name", "node_type"), &Theme::has_stylebox); - ClassDB::bind_method(D_METHOD("rename_stylebox", "old_name", "name", "node_type"), &Theme::rename_stylebox); - ClassDB::bind_method(D_METHOD("clear_stylebox", "name", "node_type"), &Theme::clear_stylebox); - ClassDB::bind_method(D_METHOD("get_stylebox_list", "node_type"), &Theme::_get_stylebox_list); + ClassDB::bind_method(D_METHOD("set_stylebox", "name", "theme_type", "texture"), &Theme::set_stylebox); + ClassDB::bind_method(D_METHOD("get_stylebox", "name", "theme_type"), &Theme::get_stylebox); + ClassDB::bind_method(D_METHOD("has_stylebox", "name", "theme_type"), &Theme::has_stylebox); + ClassDB::bind_method(D_METHOD("rename_stylebox", "old_name", "name", "theme_type"), &Theme::rename_stylebox); + ClassDB::bind_method(D_METHOD("clear_stylebox", "name", "theme_type"), &Theme::clear_stylebox); + ClassDB::bind_method(D_METHOD("get_stylebox_list", "theme_type"), &Theme::_get_stylebox_list); ClassDB::bind_method(D_METHOD("get_stylebox_types"), &Theme::_get_stylebox_types); - ClassDB::bind_method(D_METHOD("set_font", "name", "node_type", "font"), &Theme::set_font); - ClassDB::bind_method(D_METHOD("get_font", "name", "node_type"), &Theme::get_font); - ClassDB::bind_method(D_METHOD("has_font", "name", "node_type"), &Theme::has_font); - ClassDB::bind_method(D_METHOD("rename_font", "old_name", "name", "node_type"), &Theme::rename_font); - ClassDB::bind_method(D_METHOD("clear_font", "name", "node_type"), &Theme::clear_font); - ClassDB::bind_method(D_METHOD("get_font_list", "node_type"), &Theme::_get_font_list); + ClassDB::bind_method(D_METHOD("set_font", "name", "theme_type", "font"), &Theme::set_font); + ClassDB::bind_method(D_METHOD("get_font", "name", "theme_type"), &Theme::get_font); + ClassDB::bind_method(D_METHOD("has_font", "name", "theme_type"), &Theme::has_font); + ClassDB::bind_method(D_METHOD("rename_font", "old_name", "name", "theme_type"), &Theme::rename_font); + ClassDB::bind_method(D_METHOD("clear_font", "name", "theme_type"), &Theme::clear_font); + ClassDB::bind_method(D_METHOD("get_font_list", "theme_type"), &Theme::_get_font_list); ClassDB::bind_method(D_METHOD("get_font_types"), &Theme::_get_font_types); - ClassDB::bind_method(D_METHOD("set_color", "name", "node_type", "color"), &Theme::set_color); - ClassDB::bind_method(D_METHOD("get_color", "name", "node_type"), &Theme::get_color); - ClassDB::bind_method(D_METHOD("has_color", "name", "node_type"), &Theme::has_color); - ClassDB::bind_method(D_METHOD("rename_color", "old_name", "name", "node_type"), &Theme::rename_color); - ClassDB::bind_method(D_METHOD("clear_color", "name", "node_type"), &Theme::clear_color); - ClassDB::bind_method(D_METHOD("get_color_list", "node_type"), &Theme::_get_color_list); + ClassDB::bind_method(D_METHOD("set_color", "name", "theme_type", "color"), &Theme::set_color); + ClassDB::bind_method(D_METHOD("get_color", "name", "theme_type"), &Theme::get_color); + ClassDB::bind_method(D_METHOD("has_color", "name", "theme_type"), &Theme::has_color); + ClassDB::bind_method(D_METHOD("rename_color", "old_name", "name", "theme_type"), &Theme::rename_color); + ClassDB::bind_method(D_METHOD("clear_color", "name", "theme_type"), &Theme::clear_color); + ClassDB::bind_method(D_METHOD("get_color_list", "theme_type"), &Theme::_get_color_list); ClassDB::bind_method(D_METHOD("get_color_types"), &Theme::_get_color_types); - ClassDB::bind_method(D_METHOD("set_constant", "name", "node_type", "constant"), &Theme::set_constant); - ClassDB::bind_method(D_METHOD("get_constant", "name", "node_type"), &Theme::get_constant); - ClassDB::bind_method(D_METHOD("has_constant", "name", "node_type"), &Theme::has_constant); - ClassDB::bind_method(D_METHOD("rename_constant", "old_name", "name", "node_type"), &Theme::rename_constant); - ClassDB::bind_method(D_METHOD("clear_constant", "name", "node_type"), &Theme::clear_constant); - ClassDB::bind_method(D_METHOD("get_constant_list", "node_type"), &Theme::_get_constant_list); + ClassDB::bind_method(D_METHOD("set_constant", "name", "theme_type", "constant"), &Theme::set_constant); + ClassDB::bind_method(D_METHOD("get_constant", "name", "theme_type"), &Theme::get_constant); + ClassDB::bind_method(D_METHOD("has_constant", "name", "theme_type"), &Theme::has_constant); + ClassDB::bind_method(D_METHOD("rename_constant", "old_name", "name", "theme_type"), &Theme::rename_constant); + ClassDB::bind_method(D_METHOD("clear_constant", "name", "theme_type"), &Theme::clear_constant); + ClassDB::bind_method(D_METHOD("get_constant_list", "theme_type"), &Theme::_get_constant_list); ClassDB::bind_method(D_METHOD("get_constant_types"), &Theme::_get_constant_types); 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_theme_item", "data_type", "name", "node_type", "value"), &Theme::set_theme_item); - ClassDB::bind_method(D_METHOD("get_theme_item", "data_type", "name", "node_type"), &Theme::get_theme_item); - ClassDB::bind_method(D_METHOD("has_theme_item", "data_type", "name", "node_type"), &Theme::has_theme_item); - ClassDB::bind_method(D_METHOD("rename_theme_item", "data_type", "old_name", "name", "node_type"), &Theme::rename_theme_item); - ClassDB::bind_method(D_METHOD("clear_theme_item", "data_type", "name", "node_type"), &Theme::clear_theme_item); - ClassDB::bind_method(D_METHOD("get_theme_item_list", "data_type", "node_type"), &Theme::_get_theme_item_list); + 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); + ClassDB::bind_method(D_METHOD("has_theme_item", "data_type", "name", "theme_type"), &Theme::has_theme_item); + ClassDB::bind_method(D_METHOD("rename_theme_item", "data_type", "old_name", "name", "theme_type"), &Theme::rename_theme_item); + ClassDB::bind_method(D_METHOD("clear_theme_item", "data_type", "name", "theme_type"), &Theme::clear_theme_item); + ClassDB::bind_method(D_METHOD("get_theme_item_list", "data_type", "theme_type"), &Theme::_get_theme_item_list); ClassDB::bind_method(D_METHOD("get_theme_item_types", "data_type"), &Theme::_get_theme_item_types); - ClassDB::bind_method(D_METHOD("get_type_list", "node_type"), &Theme::_get_type_list); + ClassDB::bind_method(D_METHOD("set_type_variation", "theme_type", "base_type"), &Theme::set_type_variation); + ClassDB::bind_method(D_METHOD("is_type_variation", "theme_type", "base_type"), &Theme::is_type_variation); + ClassDB::bind_method(D_METHOD("clear_type_variation", "theme_type"), &Theme::clear_type_variation); + ClassDB::bind_method(D_METHOD("get_type_variation_base", "theme_type"), &Theme::get_type_variation_base); + ClassDB::bind_method(D_METHOD("get_type_variation_list", "base_type"), &Theme::_get_type_variation_list); + + ClassDB::bind_method(D_METHOD("get_type_list", "theme_type"), &Theme::_get_type_list); ClassDB::bind_method(D_METHOD("_emit_theme_changed", "notify_list_changed"), &Theme::_emit_theme_changed, DEFVAL(false)); diff --git a/scene/resources/theme.h b/scene/resources/theme.h index 73559cf77afb..23d67e7a63b6 100644 --- a/scene/resources/theme.h +++ b/scene/resources/theme.h @@ -69,21 +69,25 @@ class Theme : public Resource { HashMap>> shader_map; HashMap> color_map; HashMap> constant_map; + HashMap variation_map; + HashMap> variation_base_map; - PoolVector _get_icon_list(const String &p_node_type) const; + PoolVector _get_icon_list(const String &p_theme_type) const; PoolVector _get_icon_types() const; - PoolVector _get_stylebox_list(const String &p_node_type) const; + PoolVector _get_stylebox_list(const String &p_theme_type) const; PoolVector _get_stylebox_types() const; - PoolVector _get_font_list(const String &p_node_type) const; + PoolVector _get_font_list(const String &p_theme_type) const; PoolVector _get_font_types() const; - PoolVector _get_color_list(const String &p_node_type) const; + PoolVector _get_color_list(const String &p_theme_type) const; PoolVector _get_color_types() const; - PoolVector _get_constant_list(const String &p_node_type) const; + PoolVector _get_constant_list(const String &p_theme_type) const; PoolVector _get_constant_types() const; - PoolVector _get_theme_item_list(DataType p_data_type, const String &p_node_type) const; + PoolVector _get_theme_item_list(DataType p_data_type, const String &p_theme_type) const; PoolVector _get_theme_item_types(DataType p_data_type) const; - PoolVector _get_type_list(const String &p_node_type) const; + + Vector _get_type_variation_list(const StringName &p_theme_type) const; + PoolVector _get_type_list(const String &p_theme_type) const; protected: bool _set(const StringName &p_name, const Variant &p_value); @@ -122,74 +126,80 @@ class Theme : public Resource { Ref get_default_theme_font() const; bool has_default_theme_font() const; - void set_icon(const StringName &p_name, const StringName &p_node_type, const Ref &p_icon); - Ref get_icon(const StringName &p_name, const StringName &p_node_type) const; - bool has_icon(const StringName &p_name, const StringName &p_node_type) const; - bool has_icon_nocheck(const StringName &p_name, const StringName &p_node_type) const; - void rename_icon(const StringName &p_old_name, const StringName &p_name, const StringName &p_node_type); - void clear_icon(const StringName &p_name, const StringName &p_node_type); - void get_icon_list(StringName p_node_type, List *p_list) const; - void add_icon_type(const StringName &p_node_type); + 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; + bool has_icon(const StringName &p_name, const StringName &p_theme_type) const; + bool has_icon_nocheck(const StringName &p_name, const StringName &p_theme_type) const; + void rename_icon(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type); + void clear_icon(const StringName &p_name, const StringName &p_theme_type); + void get_icon_list(StringName p_theme_type, List *p_list) const; + void add_icon_type(const StringName &p_theme_type); void get_icon_types(List *p_list) const; - void set_shader(const StringName &p_name, const StringName &p_node_type, const Ref &p_shader); - Ref get_shader(const StringName &p_name, const StringName &p_node_type) const; - bool has_shader(const StringName &p_name, const StringName &p_node_type) const; - void clear_shader(const StringName &p_name, const StringName &p_node_type); - void get_shader_list(const StringName &p_node_type, List *p_list) const; - - void set_stylebox(const StringName &p_name, const StringName &p_node_type, const Ref &p_style); - Ref get_stylebox(const StringName &p_name, const StringName &p_node_type) const; - bool has_stylebox(const StringName &p_name, const StringName &p_node_type) const; - bool has_stylebox_nocheck(const StringName &p_name, const StringName &p_node_type) const; - void rename_stylebox(const StringName &p_old_name, const StringName &p_name, const StringName &p_node_type); - void clear_stylebox(const StringName &p_name, const StringName &p_node_type); - void get_stylebox_list(StringName p_node_type, List *p_list) const; - void add_stylebox_type(const StringName &p_node_type); + void set_shader(const StringName &p_name, const StringName &p_theme_type, const Ref &p_shader); + Ref get_shader(const StringName &p_name, const StringName &p_theme_type) const; + bool has_shader(const StringName &p_name, const StringName &p_theme_type) const; + void clear_shader(const StringName &p_name, const StringName &p_theme_type); + void get_shader_list(const StringName &p_theme_type, List *p_list) const; + + void set_stylebox(const StringName &p_name, const StringName &p_theme_type, const Ref &p_style); + Ref get_stylebox(const StringName &p_name, const StringName &p_theme_type) const; + bool has_stylebox(const StringName &p_name, const StringName &p_theme_type) const; + bool has_stylebox_nocheck(const StringName &p_name, const StringName &p_theme_type) const; + void rename_stylebox(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type); + void clear_stylebox(const StringName &p_name, const StringName &p_theme_type); + void get_stylebox_list(StringName p_theme_type, List *p_list) const; + void add_stylebox_type(const StringName &p_theme_type); void get_stylebox_types(List *p_list) const; - void set_font(const StringName &p_name, const StringName &p_node_type, const Ref &p_font); - Ref get_font(const StringName &p_name, const StringName &p_node_type) const; - bool has_font(const StringName &p_name, const StringName &p_node_type) const; - bool has_font_nocheck(const StringName &p_name, const StringName &p_node_type) const; - void rename_font(const StringName &p_old_name, const StringName &p_name, const StringName &p_node_type); - void clear_font(const StringName &p_name, const StringName &p_node_type); - void get_font_list(StringName p_node_type, List *p_list) const; - void add_font_type(const StringName &p_node_type); + void set_font(const StringName &p_name, const StringName &p_theme_type, const Ref &p_font); + Ref get_font(const StringName &p_name, const StringName &p_theme_type) const; + bool has_font(const StringName &p_name, const StringName &p_theme_type) const; + bool has_font_nocheck(const StringName &p_name, const StringName &p_theme_type) const; + void rename_font(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type); + void clear_font(const StringName &p_name, const StringName &p_theme_type); + void get_font_list(StringName p_theme_type, List *p_list) const; + void add_font_type(const StringName &p_theme_type); void get_font_types(List *p_list) const; - void set_color(const StringName &p_name, const StringName &p_node_type, const Color &p_color); - Color get_color(const StringName &p_name, const StringName &p_node_type) const; - bool has_color(const StringName &p_name, const StringName &p_node_type) const; - bool has_color_nocheck(const StringName &p_name, const StringName &p_node_type) const; - void rename_color(const StringName &p_old_name, const StringName &p_name, const StringName &p_node_type); - void clear_color(const StringName &p_name, const StringName &p_node_type); - void get_color_list(StringName p_node_type, List *p_list) const; - void add_color_type(const StringName &p_node_type); + void set_color(const StringName &p_name, const StringName &p_theme_type, const Color &p_color); + Color get_color(const StringName &p_name, const StringName &p_theme_type) const; + bool has_color(const StringName &p_name, const StringName &p_theme_type) const; + bool has_color_nocheck(const StringName &p_name, const StringName &p_theme_type) const; + void rename_color(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type); + void clear_color(const StringName &p_name, const StringName &p_theme_type); + void get_color_list(StringName p_theme_type, List *p_list) const; + void add_color_type(const StringName &p_theme_type); void get_color_types(List *p_list) const; - void set_constant(const StringName &p_name, const StringName &p_node_type, int p_constant); - int get_constant(const StringName &p_name, const StringName &p_node_type) const; - bool has_constant(const StringName &p_name, const StringName &p_node_type) const; - bool has_constant_nocheck(const StringName &p_name, const StringName &p_node_type) const; - void rename_constant(const StringName &p_old_name, const StringName &p_name, const StringName &p_node_type); - void clear_constant(const StringName &p_name, const StringName &p_node_type); - void get_constant_list(StringName p_node_type, List *p_list) const; - void add_constant_type(const StringName &p_node_type); + void set_constant(const StringName &p_name, const StringName &p_theme_type, int p_constant); + int get_constant(const StringName &p_name, const StringName &p_theme_type) const; + bool has_constant(const StringName &p_name, const StringName &p_theme_type) const; + bool has_constant_nocheck(const StringName &p_name, const StringName &p_theme_type) const; + void rename_constant(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type); + void clear_constant(const StringName &p_name, const StringName &p_theme_type); + void get_constant_list(StringName p_theme_type, List *p_list) const; + void add_constant_type(const StringName &p_theme_type); void get_constant_types(List *p_list) const; - void set_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_node_type, const Variant &p_value); - Variant get_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_node_type) const; - bool has_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_node_type) const; - bool has_theme_item_nocheck(DataType p_data_type, const StringName &p_name, const StringName &p_node_type) const; - void rename_theme_item(DataType p_data_type, const StringName &p_old_name, const StringName &p_name, const StringName &p_node_type); - void clear_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_node_type); - void get_theme_item_list(DataType p_data_type, StringName p_node_type, List *p_list) const; - void add_theme_item_type(DataType p_data_type, const StringName &p_node_type); + void set_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type, const Variant &p_value); + Variant get_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type) const; + bool has_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type) const; + bool has_theme_item_nocheck(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type) const; + void rename_theme_item(DataType p_data_type, const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type); + void clear_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type); + void get_theme_item_list(DataType p_data_type, StringName p_theme_type, List *p_list) const; + void add_theme_item_type(DataType p_data_type, const StringName &p_theme_type); void get_theme_item_types(DataType p_data_type, List *p_list) const; + void set_type_variation(const StringName &p_theme_type, const StringName &p_base_type); + bool is_type_variation(const StringName &p_theme_type, const StringName &p_base_type) const; + void clear_type_variation(const StringName &p_theme_type); + StringName get_type_variation_base(const StringName &p_theme_type) const; + void get_type_variation_list(const StringName &p_base_type, List *p_list) const; + void get_type_list(List *p_list) const; - void get_type_dependencies(const StringName &p_base_type, List *p_list); + void get_type_dependencies(const StringName &p_base_type, const StringName &p_type_variant, List *p_list); void copy_default_theme(); void copy_theme(const Ref &p_other);