Browse Source

Merge pull request #57973 from pycbouh/editor-remove-theme-type

Rémi Verschelde 3 years ago
parent
commit
882ef8284a

+ 15 - 0
doc/classes/Theme.xml

@@ -13,6 +13,14 @@
 		<link title="Using the theme editor">$DOCS_URL/tutorials/ui/gui_using_theme_editor.html</link>
 	</tutorials>
 	<methods>
+		<method name="add_type">
+			<return type="void" />
+			<argument index="0" name="theme_type" type="StringName" />
+			<description>
+				Adds an empty theme type for every valid data type.
+				[b]Note:[/b] Empty types are not saved with the theme. This method only exists to perform in-memory changes to the resource. Use available [code]set_*[/code] methods to add theme items.
+			</description>
+		</method>
 		<method name="clear">
 			<return type="void" />
 			<description>
@@ -375,6 +383,13 @@
 				[b]Note:[/b] This modifies the current theme. If you want to merge two themes together without modifying either one, create a new empty theme and merge the other two into it one after another.
 			</description>
 		</method>
+		<method name="remove_type">
+			<return type="void" />
+			<argument index="0" name="theme_type" type="StringName" />
+			<description>
+				Removes the theme type, gracefully discarding defined theme items. If the type is a variation, this information is also erased. If the type is a base for type variations, those variations lose their base.
+			</description>
+		</method>
 		<method name="rename_color">
 			<return type="void" />
 			<argument index="0" name="old_name" type="StringName" />

+ 64 - 22
editor/plugins/theme_editor_plugin.cpp

@@ -1211,7 +1211,8 @@ void ThemeItemEditorDialog::_update_edit_types() {
 
 	bool item_reselected = false;
 	edit_type_list->clear();
-	int e_idx = 0;
+	TreeItem *list_root = edit_type_list->create_item();
+
 	for (const StringName &E : theme_types) {
 		Ref<Texture2D> item_icon;
 		if (E == "") {
@@ -1219,19 +1220,21 @@ void ThemeItemEditorDialog::_update_edit_types() {
 		} else {
 			item_icon = EditorNode::get_singleton()->get_class_icon(E, "NodeDisabled");
 		}
-		edit_type_list->add_item(E, item_icon);
+		TreeItem *list_item = edit_type_list->create_item(list_root);
+		list_item->set_text(0, E);
+		list_item->set_icon(0, item_icon);
+		list_item->add_button(0, get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), TYPES_TREE_REMOVE_ITEM, false, TTR("Remove Type"));
 
 		if (E == edited_item_type) {
-			edit_type_list->select(e_idx);
+			list_item->select(0);
 			item_reselected = true;
 		}
-		e_idx++;
 	}
 	if (!item_reselected) {
 		edited_item_type = "";
 
-		if (edit_type_list->get_item_count() > 0) {
-			edit_type_list->select(0);
+		if (list_root->get_child_count() > 0) {
+			list_root->get_child(0)->select(0);
 		}
 	}
 
@@ -1240,9 +1243,9 @@ void ThemeItemEditorDialog::_update_edit_types() {
 	default_types.sort_custom<StringName::AlphCompare>();
 
 	String selected_type = "";
-	Vector<int> selected_ids = edit_type_list->get_selected_items();
-	if (selected_ids.size() > 0) {
-		selected_type = edit_type_list->get_item_text(selected_ids[0]);
+	TreeItem *selected_item = edit_type_list->get_selected();
+	if (selected_item) {
+		selected_type = selected_item->get_text(0);
 
 		edit_items_add_color->set_disabled(false);
 		edit_items_add_constant->set_disabled(false);
@@ -1276,11 +1279,26 @@ void ThemeItemEditorDialog::_update_edit_types() {
 	_update_edit_item_tree(selected_type);
 }
 
-void ThemeItemEditorDialog::_edited_type_selected(int p_item_idx) {
-	String selected_type = edit_type_list->get_item_text(p_item_idx);
+void ThemeItemEditorDialog::_edited_type_selected() {
+	TreeItem *selected_item = edit_type_list->get_selected();
+	String selected_type = selected_item->get_text(0);
 	_update_edit_item_tree(selected_type);
 }
 
+void ThemeItemEditorDialog::_edited_type_button_pressed(Object *p_item, int p_column, int p_id) {
+	TreeItem *item = Object::cast_to<TreeItem>(p_item);
+	if (!item) {
+		return;
+	}
+
+	switch (p_id) {
+		case TYPES_TREE_REMOVE_ITEM: {
+			String type_name = item->get_text(0);
+			_remove_theme_type(type_name);
+		} break;
+	}
+}
+
 void ThemeItemEditorDialog::_update_edit_item_tree(String p_item_type) {
 	edited_item_type = p_item_type;
 
@@ -1429,8 +1447,8 @@ void ThemeItemEditorDialog::_update_edit_item_tree(String p_item_type) {
 	}
 
 	// If some type is selected, but it doesn't seem to have any items, show a guiding message.
-	Vector<int> selected_ids = edit_type_list->get_selected_items();
-	if (selected_ids.size() > 0) {
+	TreeItem *selected_item = edit_type_list->get_selected();
+	if (selected_item) {
 		if (!has_any_items) {
 			edit_items_message->set_text(TTR("This theme type is empty.\nAdd more items to it manually or by importing from another theme."));
 			edit_items_message->show();
@@ -1477,16 +1495,15 @@ void ThemeItemEditorDialog::_add_theme_type(const String &p_new_text) {
 	const String new_type = edit_add_type_value->get_text().strip_edges();
 	edit_add_type_value->clear();
 
-	edited_theme->add_icon_type(new_type);
-	edited_theme->add_stylebox_type(new_type);
-	edited_theme->add_font_type(new_type);
-	edited_theme->add_font_size_type(new_type);
-	edited_theme->add_color_type(new_type);
-	edited_theme->add_constant_type(new_type);
+	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	ur->create_action(TTR("Add Theme Type"));
 
-	_update_edit_types();
+	ur->add_do_method(*edited_theme, "add_type", new_type);
+	ur->add_undo_method(*edited_theme, "remove_type", new_type);
+	ur->add_do_method(this, "_update_edit_types");
+	ur->add_undo_method(this, "_update_edit_types");
 
-	edited_theme->emit_changed();
+	ur->commit_action();
 }
 
 void ThemeItemEditorDialog::_add_theme_item(Theme::DataType p_data_type, String p_item_name, String p_item_type) {
@@ -1531,6 +1548,27 @@ void ThemeItemEditorDialog::_add_theme_item(Theme::DataType p_data_type, String
 	ur->commit_action();
 }
 
+void ThemeItemEditorDialog::_remove_theme_type(const String &p_theme_type) {
+	Ref<Theme> old_snapshot = edited_theme->duplicate();
+	Ref<Theme> new_snapshot = edited_theme->duplicate();
+
+	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+	ur->create_action(TTR("Remove Theme Type"));
+
+	new_snapshot->remove_type(p_theme_type);
+
+	ur->add_do_method(*edited_theme, "clear");
+	ur->add_do_method(*edited_theme, "merge_with", new_snapshot);
+	// 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", p_theme_type);
+	ur->add_undo_method(*edited_theme, "merge_with", old_snapshot);
+
+	ur->add_do_method(this, "_update_edit_types");
+	ur->add_undo_method(this, "_update_edit_types");
+
+	ur->commit_action();
+}
+
 void ThemeItemEditorDialog::_remove_data_type_items(Theme::DataType p_data_type, String p_item_type) {
 	List<StringName> names;
 
@@ -1863,10 +1901,14 @@ ThemeItemEditorDialog::ThemeItemEditorDialog(ThemeTypeEditor *p_theme_type_edito
 	edit_type_label->set_text(TTR("Types:"));
 	edit_dialog_side_vb->add_child(edit_type_label);
 
-	edit_type_list = memnew(ItemList);
+	edit_type_list = memnew(Tree);
+	edit_type_list->set_hide_root(true);
+	edit_type_list->set_hide_folding(true);
+	edit_type_list->set_columns(1);
 	edit_type_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
 	edit_dialog_side_vb->add_child(edit_type_list);
 	edit_type_list->connect("item_selected", callable_mp(this, &ThemeItemEditorDialog::_edited_type_selected));
+	edit_type_list->connect("button_pressed", callable_mp(this, &ThemeItemEditorDialog::_edited_type_button_pressed));
 
 	Label *edit_add_type_label = memnew(Label);
 	edit_add_type_label->set_text(TTR("Add Type:"));

+ 8 - 2
editor/plugins/theme_editor_plugin.h

@@ -188,7 +188,11 @@ class ThemeItemEditorDialog : public AcceptDialog {
 
 	TabContainer *tc;
 
-	ItemList *edit_type_list;
+	enum TypesTreeAction {
+		TYPES_TREE_REMOVE_ITEM,
+	};
+
+	Tree *edit_type_list;
 	LineEdit *edit_add_type_value;
 	String edited_item_type;
 
@@ -240,13 +244,15 @@ class ThemeItemEditorDialog : public AcceptDialog {
 
 	void _dialog_about_to_show();
 	void _update_edit_types();
-	void _edited_type_selected(int p_item_idx);
+	void _edited_type_selected();
+	void _edited_type_button_pressed(Object *p_item, int p_column, int p_id);
 
 	void _update_edit_item_tree(String p_item_type);
 	void _item_tree_button_pressed(Object *p_item, int p_column, int p_id);
 
 	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 _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();
 	void _remove_custom_items();

+ 143 - 0
scene/resources/theme.cpp

@@ -401,6 +401,26 @@ void Theme::add_icon_type(const StringName &p_theme_type) {
 	icon_map[p_theme_type] = HashMap<StringName, Ref<Texture2D>>();
 }
 
+void Theme::remove_icon_type(const StringName &p_theme_type) {
+	if (!icon_map.has(p_theme_type)) {
+		return;
+	}
+
+	_freeze_change_propagation();
+
+	const StringName *L = nullptr;
+	while ((L = icon_map[p_theme_type].next(L))) {
+		Ref<Texture2D> icon = icon_map[p_theme_type][*L];
+		if (icon.is_valid()) {
+			icon->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+		}
+	}
+
+	icon_map.erase(p_theme_type);
+
+	_unfreeze_and_propagate_changes();
+}
+
 void Theme::get_icon_type_list(List<StringName> *p_list) const {
 	ERR_FAIL_NULL(p_list);
 
@@ -488,6 +508,26 @@ void Theme::add_stylebox_type(const StringName &p_theme_type) {
 	style_map[p_theme_type] = HashMap<StringName, Ref<StyleBox>>();
 }
 
+void Theme::remove_stylebox_type(const StringName &p_theme_type) {
+	if (!style_map.has(p_theme_type)) {
+		return;
+	}
+
+	_freeze_change_propagation();
+
+	const StringName *L = nullptr;
+	while ((L = style_map[p_theme_type].next(L))) {
+		Ref<StyleBox> style = style_map[p_theme_type][*L];
+		if (style.is_valid()) {
+			style->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+		}
+	}
+
+	style_map.erase(p_theme_type);
+
+	_unfreeze_and_propagate_changes();
+}
+
 void Theme::get_stylebox_type_list(List<StringName> *p_list) const {
 	ERR_FAIL_NULL(p_list);
 
@@ -577,6 +617,26 @@ void Theme::add_font_type(const StringName &p_theme_type) {
 	font_map[p_theme_type] = HashMap<StringName, Ref<Font>>();
 }
 
+void Theme::remove_font_type(const StringName &p_theme_type) {
+	if (!font_map.has(p_theme_type)) {
+		return;
+	}
+
+	_freeze_change_propagation();
+
+	const StringName *L = nullptr;
+	while ((L = font_map[p_theme_type].next(L))) {
+		Ref<Font> font = font_map[p_theme_type][*L];
+		if (font.is_valid()) {
+			font->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+		}
+	}
+
+	font_map.erase(p_theme_type);
+
+	_unfreeze_and_propagate_changes();
+}
+
 void Theme::get_font_type_list(List<StringName> *p_list) const {
 	ERR_FAIL_NULL(p_list);
 
@@ -653,6 +713,14 @@ void Theme::add_font_size_type(const StringName &p_theme_type) {
 	font_size_map[p_theme_type] = HashMap<StringName, int>();
 }
 
+void Theme::remove_font_size_type(const StringName &p_theme_type) {
+	if (!font_size_map.has(p_theme_type)) {
+		return;
+	}
+
+	font_size_map.erase(p_theme_type);
+}
+
 void Theme::get_font_size_type_list(List<StringName> *p_list) const {
 	ERR_FAIL_NULL(p_list);
 
@@ -727,6 +795,14 @@ void Theme::add_color_type(const StringName &p_theme_type) {
 	color_map[p_theme_type] = HashMap<StringName, Color>();
 }
 
+void Theme::remove_color_type(const StringName &p_theme_type) {
+	if (!color_map.has(p_theme_type)) {
+		return;
+	}
+
+	color_map.erase(p_theme_type);
+}
+
 void Theme::get_color_type_list(List<StringName> *p_list) const {
 	ERR_FAIL_NULL(p_list);
 
@@ -801,6 +877,14 @@ void Theme::add_constant_type(const StringName &p_theme_type) {
 	constant_map[p_theme_type] = HashMap<StringName, int>();
 }
 
+void Theme::remove_constant_type(const StringName &p_theme_type) {
+	if (!constant_map.has(p_theme_type)) {
+		return;
+	}
+
+	constant_map.erase(p_theme_type);
+}
+
 void Theme::get_constant_type_list(List<StringName> *p_list) const {
 	ERR_FAIL_NULL(p_list);
 
@@ -1017,6 +1101,31 @@ void Theme::add_theme_item_type(DataType p_data_type, const StringName &p_theme_
 	}
 }
 
+void Theme::remove_theme_item_type(DataType p_data_type, const StringName &p_theme_type) {
+	switch (p_data_type) {
+		case DATA_TYPE_COLOR:
+			remove_color_type(p_theme_type);
+			break;
+		case DATA_TYPE_CONSTANT:
+			remove_constant_type(p_theme_type);
+			break;
+		case DATA_TYPE_FONT:
+			remove_font_type(p_theme_type);
+			break;
+		case DATA_TYPE_FONT_SIZE:
+			remove_font_size_type(p_theme_type);
+			break;
+		case DATA_TYPE_ICON:
+			remove_icon_type(p_theme_type);
+			break;
+		case DATA_TYPE_STYLEBOX:
+			remove_stylebox_type(p_theme_type);
+			break;
+		case DATA_TYPE_MAX:
+			break; // Can't happen, but silences warning.
+	}
+}
+
 void Theme::get_theme_item_type_list(DataType p_data_type, List<StringName> *p_list) const {
 	switch (p_data_type) {
 		case DATA_TYPE_COLOR:
@@ -1101,6 +1210,38 @@ void Theme::get_type_variation_list(const StringName &p_base_type, List<StringNa
 }
 
 // Theme types.
+void Theme::add_type(const StringName &p_theme_type) {
+	// Add a record to every data type map.
+	for (int i = 0; i < Theme::DATA_TYPE_MAX; i++) {
+		Theme::DataType dt = (Theme::DataType)i;
+		add_theme_item_type(dt, p_theme_type);
+	}
+
+	_emit_theme_changed(true);
+}
+
+void Theme::remove_type(const StringName &p_theme_type) {
+	// Gracefully remove the record from every data type map.
+	for (int i = 0; i < Theme::DATA_TYPE_MAX; i++) {
+		Theme::DataType dt = (Theme::DataType)i;
+		remove_theme_item_type(dt, p_theme_type);
+	}
+
+	// If type is a variation, remove that connection.
+	if (get_type_variation_base(p_theme_type) != StringName()) {
+		clear_type_variation(p_theme_type);
+	}
+
+	// If type is a variation base, remove all those connections.
+	List<StringName> names;
+	get_type_variation_list(p_theme_type, &names);
+	for (const StringName &E : names) {
+		clear_type_variation(E);
+	}
+
+	_emit_theme_changed(true);
+}
+
 void Theme::get_type_list(List<StringName> *p_list) const {
 	ERR_FAIL_NULL(p_list);
 
@@ -1668,6 +1809,8 @@ void Theme::_bind_methods() {
 	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("add_type", "theme_type"), &Theme::add_type);
+	ClassDB::bind_method(D_METHOD("remove_type", "theme_type"), &Theme::remove_type);
 	ClassDB::bind_method(D_METHOD("get_type_list"), &Theme::_get_type_list);
 
 	ClassDB::bind_method(D_METHOD("merge_with", "other"), &Theme::merge_with);

+ 9 - 0
scene/resources/theme.h

@@ -158,6 +158,7 @@ public:
 	void clear_icon(const StringName &p_name, const StringName &p_theme_type);
 	void get_icon_list(StringName p_theme_type, List<StringName> *p_list) const;
 	void add_icon_type(const StringName &p_theme_type);
+	void remove_icon_type(const StringName &p_theme_type);
 	void get_icon_type_list(List<StringName> *p_list) const;
 
 	void set_stylebox(const StringName &p_name, const StringName &p_theme_type, const Ref<StyleBox> &p_style);
@@ -168,6 +169,7 @@ public:
 	void clear_stylebox(const StringName &p_name, const StringName &p_theme_type);
 	void get_stylebox_list(StringName p_theme_type, List<StringName> *p_list) const;
 	void add_stylebox_type(const StringName &p_theme_type);
+	void remove_stylebox_type(const StringName &p_theme_type);
 	void get_stylebox_type_list(List<StringName> *p_list) const;
 
 	void set_font(const StringName &p_name, const StringName &p_theme_type, const Ref<Font> &p_font);
@@ -178,6 +180,7 @@ public:
 	void clear_font(const StringName &p_name, const StringName &p_theme_type);
 	void get_font_list(StringName p_theme_type, List<StringName> *p_list) const;
 	void add_font_type(const StringName &p_theme_type);
+	void remove_font_type(const StringName &p_theme_type);
 	void get_font_type_list(List<StringName> *p_list) const;
 
 	void set_font_size(const StringName &p_name, const StringName &p_theme_type, int p_font_size);
@@ -188,6 +191,7 @@ public:
 	void clear_font_size(const StringName &p_name, const StringName &p_theme_type);
 	void get_font_size_list(StringName p_theme_type, List<StringName> *p_list) const;
 	void add_font_size_type(const StringName &p_theme_type);
+	void remove_font_size_type(const StringName &p_theme_type);
 	void get_font_size_type_list(List<StringName> *p_list) const;
 
 	void set_color(const StringName &p_name, const StringName &p_theme_type, const Color &p_color);
@@ -198,6 +202,7 @@ public:
 	void clear_color(const StringName &p_name, const StringName &p_theme_type);
 	void get_color_list(StringName p_theme_type, List<StringName> *p_list) const;
 	void add_color_type(const StringName &p_theme_type);
+	void remove_color_type(const StringName &p_theme_type);
 	void get_color_type_list(List<StringName> *p_list) const;
 
 	void set_constant(const StringName &p_name, const StringName &p_theme_type, int p_constant);
@@ -208,6 +213,7 @@ public:
 	void clear_constant(const StringName &p_name, const StringName &p_theme_type);
 	void get_constant_list(StringName p_theme_type, List<StringName> *p_list) const;
 	void add_constant_type(const StringName &p_theme_type);
+	void remove_constant_type(const StringName &p_theme_type);
 	void get_constant_type_list(List<StringName> *p_list) const;
 
 	void set_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type, const Variant &p_value);
@@ -218,6 +224,7 @@ public:
 	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<StringName> *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 get_theme_item_type_list(DataType p_data_type, List<StringName> *p_list) const;
 
 	void set_type_variation(const StringName &p_theme_type, const StringName &p_base_type);
@@ -226,6 +233,8 @@ public:
 	StringName get_type_variation_base(const StringName &p_theme_type) const;
 	void get_type_variation_list(const StringName &p_base_type, List<StringName> *p_list) const;
 
+	void add_type(const StringName &p_theme_type);
+	void remove_type(const StringName &p_theme_type);
 	void get_type_list(List<StringName> *p_list) const;
 	void get_type_dependencies(const StringName &p_base_type, const StringName &p_type_variant, List<StringName> *p_list);