From e6c9a832c191cde380d10924d9a42e5d650ebd1d Mon Sep 17 00:00:00 2001 From: Danil Alexeev Date: Fri, 2 May 2025 18:54:40 +0300 Subject: [PATCH] Editor: Add ability to rename theme types --- doc/classes/Theme.xml | 9 ++ editor/plugins/theme_editor_plugin.cpp | 203 +++++++++++++++++++++---- editor/plugins/theme_editor_plugin.h | 13 +- scene/resources/theme.cpp | 121 +++++++++++++++ scene/resources/theme.h | 8 + 5 files changed, 326 insertions(+), 28 deletions(-) diff --git a/doc/classes/Theme.xml b/doc/classes/Theme.xml index 479456ae667..6dbc671cba9 100644 --- a/doc/classes/Theme.xml +++ b/doc/classes/Theme.xml @@ -462,6 +462,15 @@ [b]Note:[/b] This method is analogous to calling the corresponding data type specific method, but can be used for more generalized logic. + + + + + + Renames the theme type [param old_theme_type] to [param theme_type], if the old type exists and the new one doesn't exist. + [b]Note:[/b] Renaming a theme type to an empty name or a variation to a type associated with a built-in class removes type variation connections in a way that cannot be undone by reversing the rename alone. + + diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index 38596bb898f..08a06810060 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -54,9 +54,30 @@ #include "scene/gui/tab_bar.h" #include "scene/gui/tab_container.h" #include "scene/gui/texture_rect.h" -#include "scene/resources/packed_scene.h" #include "scene/theme/theme_db.h" +static void _rename_theme_type(EditorUndoRedoManager *p_ur, Theme *p_theme, const String &p_old_theme_type, const String &p_new_theme_type) { + p_ur->add_do_method(p_theme, "rename_type", p_old_theme_type, p_new_theme_type); + p_ur->add_undo_method(p_theme, "rename_type", p_new_theme_type, p_old_theme_type); + + // Renaming a theme type to an empty name or a variation to a type associated with a built-in class + // removes type variation connections in a way that cannot be undone by reversing the rename alone. + const StringName old_base_type = p_theme->get_type_variation_base(p_old_theme_type); + if ((!p_old_theme_type.is_empty() && p_new_theme_type.is_empty()) || (old_base_type != StringName() && ClassDB::class_exists(p_new_theme_type))) { + if (old_base_type != StringName()) { + p_ur->add_undo_method(p_theme, "set_type_variation", p_old_theme_type, old_base_type); + } + + List names; + p_theme->get_type_variation_list(p_old_theme_type, &names); + for (const StringName &E : names) { + p_ur->add_undo_method(p_theme, "set_type_variation", E, p_old_theme_type); + } + } +} + +/////////////////////// + void ThemeItemImportTree::_update_items_tree() { import_items_tree->clear(); TreeItem *root = import_items_tree->create_item(); @@ -1243,8 +1264,10 @@ void ThemeItemEditorDialog::_update_edit_types() { } TreeItem *list_item = edit_type_list->create_item(list_root); list_item->set_text(0, E); + list_item->set_metadata(0, E); + list_item->set_editable(0, true); list_item->set_icon(0, item_icon); - list_item->add_button(0, get_editor_theme_icon(SNAME("Remove")), TYPES_TREE_REMOVE_ITEM, false, TTR("Remove Type")); + list_item->add_button(0, get_editor_theme_icon(SNAME("Remove")), TYPES_TREE_REMOVE_ITEM, false, TTRC("Remove Type")); if (E == edited_item_type) { list_item->select(0); @@ -1306,6 +1329,46 @@ void ThemeItemEditorDialog::_edited_type_selected() { _update_edit_item_tree(selected_type); } +void ThemeItemEditorDialog::_edited_type_edited() { + TreeItem *edited_item = edit_type_list->get_selected(); + const String old_type_name = edited_item->get_metadata(0); + + String new_type_name = edited_item->get_text(0).strip_edges(); + if (!new_type_name.is_empty()) { // The type name can be empty, unlike the item name. + new_type_name = new_type_name.validate_ascii_identifier(); + } + + if (old_type_name == new_type_name) { + edited_item->set_text(0, old_type_name); + return; + } + + List theme_types; + edited_theme->get_type_list(&theme_types); + if (theme_types.find(new_type_name) != nullptr) { + edited_item->set_text(0, old_type_name); + return; + } + + // The list will be recreated, but let's update the item just in case. + edited_item->set_metadata(0, new_type_name); + edited_item->set_text(0, new_type_name); + + EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); + ur->create_action(TTR("Rename Theme Type")); + + _rename_theme_type(ur, *edited_theme, old_type_name, new_type_name); + + // Set `edited_item_type`. + ur->add_do_method(this, "_update_edit_item_tree", new_type_name); + ur->add_undo_method(this, "_update_edit_item_tree", old_type_name); + + ur->add_do_method(this, "_update_edit_types"); + ur->add_undo_method(this, "_update_edit_types"); + + ur->commit_action(); +} + void ThemeItemEditorDialog::_edited_type_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button) { if (p_button != MouseButton::LEFT) { return; @@ -1520,22 +1583,26 @@ void ThemeItemEditorDialog::_item_tree_button_pressed(Object *p_item, int p_colu } } -void ThemeItemEditorDialog::_add_theme_type(const String &p_new_text) { - const String new_type = edit_add_type_value->get_text().strip_edges(); +void ThemeItemEditorDialog::_add_theme_type() { + String new_type_name = edit_add_type_value->get_text().strip_edges(); + if (!new_type_name.is_empty()) { // The type name can be empty, unlike the item name. + new_type_name = new_type_name.validate_ascii_identifier(); + } edit_add_type_value->clear(); EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); ur->create_action(TTR("Add Theme Type")); - ur->add_do_method(*edited_theme, "add_type", new_type); - ur->add_undo_method(*edited_theme, "remove_type", new_type); + ur->add_do_method(*edited_theme, "add_type", new_type_name); + ur->add_undo_method(*edited_theme, "remove_type", new_type_name); + ur->add_do_method(this, "_update_edit_types"); ur->add_undo_method(this, "_update_edit_types"); ur->commit_action(); } -void ThemeItemEditorDialog::_add_theme_item(Theme::DataType p_data_type, String p_item_name, String p_item_type) { +void ThemeItemEditorDialog::_add_theme_item(Theme::DataType p_data_type, const String &p_item_name, const String &p_item_type) { EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); ur->create_action(TTR("Create Theme Item")); @@ -1617,8 +1684,8 @@ void ThemeItemEditorDialog::_remove_data_type_items(Theme::DataType p_data_type, ur->add_do_method(*edited_theme, "merge_with", new_snapshot); ur->add_undo_method(*edited_theme, "merge_with", old_snapshot); - ur->add_do_method(theme_type_editor, "_update_edit_item_tree", edited_item_type); - ur->add_undo_method(theme_type_editor, "_update_edit_item_tree", edited_item_type); + ur->add_do_method(this, "_update_edit_item_tree", edited_item_type); + ur->add_undo_method(this, "_update_edit_item_tree", edited_item_type); ur->commit_action(); } @@ -1802,14 +1869,16 @@ void ThemeItemEditorDialog::_open_rename_theme_item_dialog(Theme::DataType p_dat } void ThemeItemEditorDialog::_confirm_edit_theme_item() { + const String new_item_name = theme_item_name->get_text().strip_edges().validate_ascii_identifier(); + if (item_popup_mode == CREATE_THEME_ITEM) { - _add_theme_item(edit_item_data_type, theme_item_name->get_text(), edited_item_type); + _add_theme_item(edit_item_data_type, new_item_name, edited_item_type); } else if (item_popup_mode == RENAME_THEME_ITEM) { EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); ur->create_action(TTR("Rename Theme Item")); - ur->add_do_method(*edited_theme, "rename_theme_item", edit_item_data_type, edit_item_old_name, theme_item_name->get_text(), edited_item_type); - ur->add_undo_method(*edited_theme, "rename_theme_item", edit_item_data_type, theme_item_name->get_text(), edit_item_old_name, edited_item_type); + ur->add_do_method(*edited_theme, "rename_theme_item", edit_item_data_type, edit_item_old_name, new_item_name, edited_item_type); + ur->add_undo_method(*edited_theme, "rename_theme_item", edit_item_data_type, new_item_name, edit_item_old_name, edited_item_type); ur->add_do_method(this, "_update_edit_item_tree", edited_item_type); ur->add_undo_method(this, "_update_edit_item_tree", edited_item_type); @@ -1927,6 +1996,7 @@ ThemeItemEditorDialog::ThemeItemEditorDialog(ThemeTypeEditor *p_theme_type_edito edit_type_list->set_v_size_flags(Control::SIZE_EXPAND_FILL); edit_dialog_side_vb->add_child(edit_type_list); edit_type_list->connect(SceneStringName(item_selected), callable_mp(this, &ThemeItemEditorDialog::_edited_type_selected)); + edit_type_list->connect("item_edited", callable_mp(this, &ThemeItemEditorDialog::_edited_type_edited)); edit_type_list->connect("button_clicked", callable_mp(this, &ThemeItemEditorDialog::_edited_type_button_pressed)); edit_type_list->set_theme_type_variation("TreeSecondary"); @@ -1938,11 +2008,11 @@ ThemeItemEditorDialog::ThemeItemEditorDialog(ThemeTypeEditor *p_theme_type_edito edit_dialog_side_vb->add_child(edit_add_type_hb); edit_add_type_value = memnew(LineEdit); edit_add_type_value->set_h_size_flags(Control::SIZE_EXPAND_FILL); - edit_add_type_value->connect(SceneStringName(text_submitted), callable_mp(this, &ThemeItemEditorDialog::_add_theme_type)); + edit_add_type_value->connect(SceneStringName(text_submitted), callable_mp(this, &ThemeItemEditorDialog::_add_theme_type).unbind(1)); edit_add_type_hb->add_child(edit_add_type_value); edit_add_type_button = memnew(Button); edit_add_type_hb->add_child(edit_add_type_button); - edit_add_type_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeItemEditorDialog::_add_theme_type).bind("")); + edit_add_type_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeItemEditorDialog::_add_theme_type)); VBoxContainer *edit_items_vb = memnew(VBoxContainer); edit_items_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL); @@ -2350,7 +2420,14 @@ void ThemeTypeEditor::_update_type_list() { theme_type_list->clear(); - if (theme_types.size() > 0) { + if (theme_types.is_empty()) { + theme_type_list->set_disabled(true); + theme_type_list->add_item(TTRC("None")); + theme_type_list->set_item_auto_translate_mode(-1, AUTO_TRANSLATE_MODE_ALWAYS); + + edited_type = ""; + _update_type_items(); + } else { theme_type_list->set_disabled(false); bool item_reselected = false; @@ -2371,20 +2448,17 @@ void ThemeTypeEditor::_update_type_list() { e_idx++; } - if (!item_reselected) { + if (item_reselected) { + _update_type_items(); + } else { theme_type_list->select(0); _list_type_selected(0); - } else { - _update_type_items(); } - } else { - theme_type_list->set_disabled(true); - theme_type_list->add_item(TTR("None")); - - edited_type = ""; - _update_type_items(); } + rename_type_button->set_disabled(theme_types.is_empty()); + remove_type_button->set_disabled(theme_types.is_empty()); + updating = false; } @@ -2818,6 +2892,54 @@ void ThemeTypeEditor::_add_type_button_cbk() { add_type_dialog->popup_centered(Size2(560, 420) * EDSCALE); } +void ThemeTypeEditor::_rename_type_button_cbk() { + theme_type_rename_line_edit->set_text(edited_type); + theme_type_rename_dialog->reset_size(); + theme_type_rename_dialog->popup_centered(); + theme_type_rename_line_edit->grab_focus(); +} + +void ThemeTypeEditor::_theme_type_rename_dialog_confirmed() { + String new_type_name = theme_type_rename_line_edit->get_text().strip_edges(); + if (!new_type_name.is_empty()) { // The type name can be empty, unlike the item name. + new_type_name = new_type_name.validate_ascii_identifier(); + } + + if (edited_type == new_type_name) { + return; + } + + List theme_types; + edited_theme->get_type_list(&theme_types); + if (theme_types.find(new_type_name) != nullptr) { + return; + } + + EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); + ur->create_action(TTR("Rename Theme Type")); + + _rename_theme_type(ur, *edited_theme, edited_type, new_type_name); + + ur->add_do_method(this, "select_type", new_type_name); + ur->add_undo_method(this, "select_type", edited_type); + + ur->commit_action(); +} + +void ThemeTypeEditor::_remove_type_button_cbk() { + Ref old_snapshot = edited_theme->duplicate(); + + EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); + ur->create_action(TTR("Remove Theme Type")); + + ur->add_do_method(*edited_theme, "remove_type", edited_type); + // If the type was empty, it cannot be restored with merge, but thankfully we can fake it. + ur->add_undo_method(*edited_theme, "add_type", edited_type); + ur->add_undo_method(*edited_theme, "merge_with", old_snapshot); + + ur->commit_action(); +} + void ThemeTypeEditor::_add_default_type_items() { List names; String default_type = edited_type; @@ -2911,7 +3033,8 @@ void ThemeTypeEditor::_item_add_cbk(int p_data_type, Control *p_control) { return; } - String item_name = le->get_text().strip_edges(); + const String item_name = le->get_text().strip_edges().validate_ascii_identifier(); + EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); ur->create_action(TTR("Add Theme Item")); @@ -3069,7 +3192,7 @@ void ThemeTypeEditor::_item_rename_confirmed(int p_data_type, String p_item_name return; } - String new_name = le->get_text().strip_edges(); + const String new_name = le->get_text().strip_edges().validate_ascii_identifier(); if (new_name == p_item_name) { _item_rename_canceled(p_data_type, p_item_name, p_control); return; @@ -3377,6 +3500,8 @@ void ThemeTypeEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { add_type_button->set_button_icon(get_editor_theme_icon(SNAME("Add"))); + rename_type_button->set_button_icon(get_editor_theme_icon(SNAME("Rename"))); + remove_type_button->set_button_icon(get_editor_theme_icon(SNAME("Remove"))); data_type_tabs->set_tab_icon(0, get_editor_theme_icon(SNAME("Color"))); data_type_tabs->set_tab_icon(1, get_editor_theme_icon(SNAME("MemberConstant"))); @@ -3396,6 +3521,7 @@ void ThemeTypeEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_pin_leading_stylebox"), &ThemeTypeEditor::_pin_leading_stylebox); ClassDB::bind_method(D_METHOD("_unpin_leading_stylebox"), &ThemeTypeEditor::_unpin_leading_stylebox); ClassDB::bind_method(D_METHOD("_change_pinned_stylebox"), &ThemeTypeEditor::_change_pinned_stylebox); + ClassDB::bind_method(D_METHOD("select_type", "type_name"), &ThemeTypeEditor::select_type); } void ThemeTypeEditor::set_edited_theme(const Ref &p_theme) { @@ -3468,6 +3594,31 @@ ThemeTypeEditor::ThemeTypeEditor() { type_list_hb->add_child(add_type_button); add_type_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeTypeEditor::_add_type_button_cbk)); + rename_type_button = memnew(Button); + rename_type_button->set_disabled(true); + rename_type_button->set_tooltip_text(TTRC("Rename current type.")); + rename_type_button->set_accessibility_name(TTRC("Rename Current Type")); + type_list_hb->add_child(rename_type_button); + rename_type_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeTypeEditor::_rename_type_button_cbk)); + + theme_type_rename_dialog = memnew(ConfirmationDialog); + theme_type_rename_dialog->set_title(TTRC("Rename Theme Type")); + theme_type_rename_dialog->set_min_size(Size2(256, 64) * EDSCALE); + add_child(theme_type_rename_dialog); + theme_type_rename_dialog->connect(SceneStringName(confirmed), callable_mp(this, &ThemeTypeEditor::_theme_type_rename_dialog_confirmed)); + + theme_type_rename_line_edit = memnew(LineEdit); + theme_type_rename_line_edit->set_select_all_on_focus(true); + theme_type_rename_dialog->add_child(theme_type_rename_line_edit); + theme_type_rename_dialog->register_text_enter(theme_type_rename_line_edit); + + remove_type_button = memnew(Button); + remove_type_button->set_disabled(true); + remove_type_button->set_tooltip_text(TTRC("Remove current type.")); + remove_type_button->set_accessibility_name(TTRC("Remove Current Type")); + type_list_hb->add_child(remove_type_button); + remove_type_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeTypeEditor::_remove_type_button_cbk)); + HBoxContainer *type_controls = memnew(HBoxContainer); main_vb->add_child(type_controls); diff --git a/editor/plugins/theme_editor_plugin.h b/editor/plugins/theme_editor_plugin.h index 711da63d335..7e6d933d273 100644 --- a/editor/plugins/theme_editor_plugin.h +++ b/editor/plugins/theme_editor_plugin.h @@ -42,6 +42,7 @@ class CheckButton; class EditorFileDialog; class ItemList; class Label; +class LineEdit; class OptionButton; class PanelContainer; class TabBar; @@ -253,13 +254,14 @@ class ThemeItemEditorDialog : public AcceptDialog { void _dialog_about_to_show(); void _update_edit_types(); void _edited_type_selected(); + void _edited_type_edited(); void _edited_type_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button); void _update_edit_item_tree(String p_item_type); void _item_tree_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button); - void _add_theme_type(const String &p_new_text); - void _add_theme_item(Theme::DataType p_data_type, String p_item_name, String p_item_type); + void _add_theme_type(); + void _add_theme_item(Theme::DataType p_data_type, const String &p_item_name, const String &p_item_type); void _remove_theme_type(const String &p_theme_type); void _remove_data_type_items(Theme::DataType p_data_type, String p_item_type); void _remove_class_items(); @@ -344,6 +346,10 @@ class ThemeTypeEditor : public MarginContainer { OptionButton *theme_type_list = nullptr; Button *add_type_button = nullptr; + Button *rename_type_button = nullptr; + ConfirmationDialog *theme_type_rename_dialog = nullptr; + LineEdit *theme_type_rename_line_edit = nullptr; + Button *remove_type_button = nullptr; CheckButton *show_default_items_button = nullptr; @@ -380,6 +386,9 @@ class ThemeTypeEditor : public MarginContainer { void _list_type_selected(int p_index); void _add_type_button_cbk(); + void _rename_type_button_cbk(); + void _theme_type_rename_dialog_confirmed(); + void _remove_type_button_cbk(); void _add_default_type_items(); void _update_add_button(const String &p_text, LineEdit *p_for_edit); diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp index d4d489cfaed..fc9562c8618 100644 --- a/scene/resources/theme.cpp +++ b/scene/resources/theme.cpp @@ -361,6 +361,17 @@ void Theme::remove_icon_type(const StringName &p_theme_type) { _unfreeze_and_propagate_changes(); } +void Theme::rename_icon_type(const StringName &p_old_theme_type, const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); + + if (!icon_map.has(p_old_theme_type) || icon_map.has(p_theme_type)) { + return; + } + + icon_map[p_theme_type] = icon_map[p_old_theme_type]; + icon_map.erase(p_old_theme_type); +} + void Theme::get_icon_type_list(List *p_list) const { ERR_FAIL_NULL(p_list); @@ -471,6 +482,17 @@ void Theme::remove_stylebox_type(const StringName &p_theme_type) { _unfreeze_and_propagate_changes(); } +void Theme::rename_stylebox_type(const StringName &p_old_theme_type, const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); + + if (!style_map.has(p_old_theme_type) || style_map.has(p_theme_type)) { + return; + } + + style_map[p_theme_type] = style_map[p_old_theme_type]; + style_map.erase(p_old_theme_type); +} + void Theme::get_stylebox_type_list(List *p_list) const { ERR_FAIL_NULL(p_list); @@ -587,6 +609,17 @@ void Theme::remove_font_type(const StringName &p_theme_type) { _unfreeze_and_propagate_changes(); } +void Theme::rename_font_type(const StringName &p_old_theme_type, const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); + + if (!font_map.has(p_old_theme_type) || font_map.has(p_theme_type)) { + return; + } + + font_map[p_theme_type] = font_map[p_old_theme_type]; + font_map.erase(p_old_theme_type); +} + void Theme::get_font_type_list(List *p_list) const { ERR_FAIL_NULL(p_list); @@ -679,6 +712,17 @@ void Theme::remove_font_size_type(const StringName &p_theme_type) { font_size_map.erase(p_theme_type); } +void Theme::rename_font_size_type(const StringName &p_old_theme_type, const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); + + if (!font_size_map.has(p_old_theme_type) || font_size_map.has(p_theme_type)) { + return; + } + + font_size_map[p_theme_type] = font_size_map[p_old_theme_type]; + font_size_map.erase(p_old_theme_type); +} + void Theme::get_font_size_type_list(List *p_list) const { ERR_FAIL_NULL(p_list); @@ -765,6 +809,17 @@ void Theme::remove_color_type(const StringName &p_theme_type) { color_map.erase(p_theme_type); } +void Theme::rename_color_type(const StringName &p_old_theme_type, const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); + + if (!color_map.has(p_old_theme_type) || color_map.has(p_theme_type)) { + return; + } + + color_map[p_theme_type] = color_map[p_old_theme_type]; + color_map.erase(p_old_theme_type); +} + void Theme::get_color_type_list(List *p_list) const { ERR_FAIL_NULL(p_list); @@ -851,6 +906,17 @@ void Theme::remove_constant_type(const StringName &p_theme_type) { constant_map.erase(p_theme_type); } +void Theme::rename_constant_type(const StringName &p_old_theme_type, const StringName &p_theme_type) { + ERR_FAIL_COND_MSG(!is_valid_type_name(p_theme_type), vformat("Invalid type name: '%s'", p_theme_type)); + + if (!constant_map.has(p_old_theme_type) || constant_map.has(p_theme_type)) { + return; + } + + constant_map[p_theme_type] = constant_map[p_old_theme_type]; + constant_map.erase(p_old_theme_type); +} + void Theme::get_constant_type_list(List *p_list) const { ERR_FAIL_NULL(p_list); @@ -1099,6 +1165,31 @@ void Theme::remove_theme_item_type(DataType p_data_type, const StringName &p_the } } +void Theme::rename_theme_item_type(DataType p_data_type, const StringName &p_old_theme_type, const StringName &p_theme_type) { + switch (p_data_type) { + case DATA_TYPE_COLOR: + rename_color_type(p_old_theme_type, p_theme_type); + break; + case DATA_TYPE_CONSTANT: + rename_constant_type(p_old_theme_type, p_theme_type); + break; + case DATA_TYPE_FONT: + rename_font_type(p_old_theme_type, p_theme_type); + break; + case DATA_TYPE_FONT_SIZE: + rename_font_size_type(p_old_theme_type, p_theme_type); + break; + case DATA_TYPE_ICON: + rename_icon_type(p_old_theme_type, p_theme_type); + break; + case DATA_TYPE_STYLEBOX: + rename_stylebox_type(p_old_theme_type, p_theme_type); + break; + case DATA_TYPE_MAX: + break; // Can't happen, but silences warning. + } +} + void Theme::get_theme_item_type_list(DataType p_data_type, List *p_list) const { switch (p_data_type) { case DATA_TYPE_COLOR: @@ -1217,6 +1308,35 @@ void Theme::remove_type(const StringName &p_theme_type) { _emit_theme_changed(true); } +void Theme::rename_type(const StringName &p_old_theme_type, const StringName &p_theme_type) { + // Gracefully rename the record in every data type map. + for (int i = 0; i < Theme::DATA_TYPE_MAX; i++) { + Theme::DataType dt = (Theme::DataType)i; + rename_theme_item_type(dt, p_old_theme_type, p_theme_type); + } + + // If type is a variation, replace that connection. + const StringName base_type = get_type_variation_base(p_old_theme_type); + if (base_type != StringName()) { + clear_type_variation(p_old_theme_type); + if (p_theme_type != StringName() && !ClassDB::class_exists(p_theme_type)) { + set_type_variation(p_theme_type, base_type); + } + } + + // If type is a variation base, replace all those connections. + List names; + get_type_variation_list(p_old_theme_type, &names); + for (const StringName &E : names) { + clear_type_variation(E); + if (p_theme_type != StringName()) { + set_type_variation(E, p_theme_type); + } + } + + _emit_theme_changed(true); +} + void Theme::get_type_list(List *p_list) const { ERR_FAIL_NULL(p_list); @@ -1769,6 +1889,7 @@ void Theme::_bind_methods() { ClassDB::bind_method(D_METHOD("add_type", "theme_type"), &Theme::add_type); ClassDB::bind_method(D_METHOD("remove_type", "theme_type"), &Theme::remove_type); + ClassDB::bind_method(D_METHOD("rename_type", "old_theme_type", "theme_type"), &Theme::rename_type); ClassDB::bind_method(D_METHOD("get_type_list"), &Theme::_get_type_list); ClassDB::bind_method(D_METHOD("merge_with", "other"), &Theme::merge_with); diff --git a/scene/resources/theme.h b/scene/resources/theme.h index 91575d46a4d..c5c5234d26e 100644 --- a/scene/resources/theme.h +++ b/scene/resources/theme.h @@ -138,6 +138,7 @@ public: void get_icon_list(const StringName &p_theme_type, List *p_list) const; void add_icon_type(const StringName &p_theme_type); void remove_icon_type(const StringName &p_theme_type); + void rename_icon_type(const StringName &p_old_theme_type, const StringName &p_theme_type); void get_icon_type_list(List *p_list) const; void set_stylebox(const StringName &p_name, const StringName &p_theme_type, const Ref &p_style); @@ -149,6 +150,7 @@ public: void get_stylebox_list(const StringName &p_theme_type, List *p_list) const; void add_stylebox_type(const StringName &p_theme_type); void remove_stylebox_type(const StringName &p_theme_type); + void rename_stylebox_type(const StringName &p_old_theme_type, const StringName &p_theme_type); void get_stylebox_type_list(List *p_list) const; void set_font(const StringName &p_name, const StringName &p_theme_type, const Ref &p_font); @@ -161,6 +163,7 @@ public: void get_font_list(const StringName &p_theme_type, List *p_list) const; void add_font_type(const StringName &p_theme_type); void remove_font_type(const StringName &p_theme_type); + void rename_font_type(const StringName &p_old_theme_type, const StringName &p_theme_type); void get_font_type_list(List *p_list) const; void set_font_size(const StringName &p_name, const StringName &p_theme_type, int p_font_size); @@ -173,6 +176,7 @@ public: void get_font_size_list(const StringName &p_theme_type, List *p_list) const; void add_font_size_type(const StringName &p_theme_type); void remove_font_size_type(const StringName &p_theme_type); + void rename_font_size_type(const StringName &p_old_theme_type, const StringName &p_theme_type); void get_font_size_type_list(List *p_list) const; void set_color(const StringName &p_name, const StringName &p_theme_type, const Color &p_color); @@ -184,6 +188,7 @@ public: void get_color_list(const StringName &p_theme_type, List *p_list) const; void add_color_type(const StringName &p_theme_type); void remove_color_type(const StringName &p_theme_type); + void rename_color_type(const StringName &p_old_theme_type, const StringName &p_theme_type); void get_color_type_list(List *p_list) const; void set_constant(const StringName &p_name, const StringName &p_theme_type, int p_constant); @@ -195,6 +200,7 @@ public: void get_constant_list(const StringName &p_theme_type, List *p_list) const; void add_constant_type(const StringName &p_theme_type); void remove_constant_type(const StringName &p_theme_type); + void rename_constant_type(const StringName &p_old_theme_type, const StringName &p_theme_type); void get_constant_type_list(List *p_list) const; void set_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type, const Variant &p_value); @@ -206,6 +212,7 @@ public: void get_theme_item_list(DataType p_data_type, const StringName &p_theme_type, List *p_list) const; void add_theme_item_type(DataType p_data_type, const StringName &p_theme_type); void remove_theme_item_type(DataType p_data_type, const StringName &p_theme_type); + void rename_theme_item_type(DataType p_data_type, const StringName &p_old_theme_type, const StringName &p_theme_type); void get_theme_item_type_list(DataType p_data_type, List *p_list) const; void set_type_variation(const StringName &p_theme_type, const StringName &p_base_type); @@ -216,6 +223,7 @@ public: void add_type(const StringName &p_theme_type); void remove_type(const StringName &p_theme_type); + void rename_type(const StringName &p_old_theme_type, const StringName &p_theme_type); void get_type_list(List *p_list) const; void get_type_dependencies(const StringName &p_base_type, const StringName &p_type_variant, Vector &r_result);