Parcourir la source

Improve Variant type menus in the editor

Haoyu Qiu il y a 2 mois
Parent
commit
19226d77ed

+ 5 - 16
editor/add_metadata_dialog.cpp

@@ -31,9 +31,9 @@
 #include "add_metadata_dialog.h"
 
 #include "editor/gui/editor_validation_panel.h"
+#include "editor/gui/editor_variant_type_selectors.h"
 #include "editor/themes/editor_scale.h"
 #include "scene/gui/line_edit.h"
-#include "scene/gui/option_button.h"
 
 AddMetadataDialog::AddMetadataDialog() {
 	VBoxContainer *vbc = memnew(VBoxContainer);
@@ -49,7 +49,7 @@ AddMetadataDialog::AddMetadataDialog() {
 	hbc->add_child(add_meta_name);
 	hbc->add_child(memnew(Label(TTR("Type:"))));
 
-	add_meta_type = memnew(OptionButton);
+	add_meta_type = memnew(EditorVariantTypeOptionButton);
 	add_meta_type->set_accessibility_name(TTRC("Type:"));
 
 	hbc->add_child(add_meta_type);
@@ -76,19 +76,8 @@ void AddMetadataDialog::_complete_init(const StringName &p_title) {
 
 	set_title(vformat(TTR("Add Metadata Property for \"%s\""), p_title));
 
-	// Skip if we already completed the initialization.
-	if (add_meta_type->get_item_count()) {
-		return;
-	}
-
-	// Theme icons can be retrieved only the Window has been initialized.
-	for (int i = 0; i < Variant::VARIANT_MAX; i++) {
-		if (i == Variant::NIL || i == Variant::RID || i == Variant::CALLABLE || i == Variant::SIGNAL) {
-			continue; //not editable by inspector.
-		}
-		String type = i == Variant::OBJECT ? String("Resource") : Variant::get_type_name(Variant::Type(i));
-
-		add_meta_type->add_icon_item(get_editor_theme_icon(type), type, i);
+	if (add_meta_type->get_item_count() == 0) {
+		add_meta_type->populate({ Variant::NIL }, { { Variant::OBJECT, "Resource" } });
 	}
 }
 
@@ -106,7 +95,7 @@ StringName AddMetadataDialog::get_meta_name() {
 Variant AddMetadataDialog::get_meta_defval() {
 	Variant defval;
 	Callable::CallError ce;
-	Variant::construct(Variant::Type(add_meta_type->get_selected_id()), defval, nullptr, 0, ce);
+	Variant::construct(add_meta_type->get_selected_type(), defval, nullptr, 0, ce);
 	return defval;
 }
 

+ 2 - 2
editor/add_metadata_dialog.h

@@ -33,8 +33,8 @@
 #include "scene/gui/dialogs.h"
 
 class EditorValidationPanel;
+class EditorVariantTypeOptionButton;
 class LineEdit;
-class OptionButton;
 
 class AddMetadataDialog : public ConfirmationDialog {
 	GDCLASS(AddMetadataDialog, ConfirmationDialog);
@@ -53,6 +53,6 @@ private:
 	void _complete_init(const StringName &p_label);
 
 	LineEdit *add_meta_name = nullptr;
-	OptionButton *add_meta_type = nullptr;
+	EditorVariantTypeOptionButton *add_meta_type = nullptr;
 	EditorValidationPanel *validation_panel = nullptr;
 };

+ 5 - 16
editor/connections_dialog.cpp

@@ -39,6 +39,7 @@
 #include "editor/editor_settings.h"
 #include "editor/editor_string_names.h"
 #include "editor/editor_undo_redo_manager.h"
+#include "editor/gui/editor_variant_type_selectors.h"
 #include "editor/gui/scene_tree_editor.h"
 #include "editor/node_dock.h"
 #include "editor/plugins/script_editor_plugin.h"
@@ -46,11 +47,11 @@
 #include "editor/themes/editor_scale.h"
 #include "scene/gui/button.h"
 #include "scene/gui/check_box.h"
+#include "scene/gui/check_button.h"
 #include "scene/gui/flow_container.h"
 #include "scene/gui/label.h"
 #include "scene/gui/line_edit.h"
 #include "scene/gui/margin_container.h"
-#include "scene/gui/option_button.h"
 #include "scene/gui/popup_menu.h"
 #include "scene/gui/spin_box.h"
 
@@ -209,7 +210,7 @@ void ConnectDialog::_method_selected() {
  * Adds a new parameter bind to connection.
  */
 void ConnectDialog::_add_bind() {
-	Variant::Type type = (Variant::Type)type_list->get_item_id(type_list->get_selected());
+	Variant::Type type = type_list->get_selected_type();
 
 	Variant value;
 	Callable::CallError err;
@@ -493,11 +494,6 @@ void ConnectDialog::_notification(int p_what) {
 			[[fallthrough]];
 		}
 		case NOTIFICATION_THEME_CHANGED: {
-			for (int i = 0; i < type_list->get_item_count(); i++) {
-				String type_name = Variant::get_type_name((Variant::Type)type_list->get_item_id(i));
-				type_list->set_item_icon(i, get_editor_theme_icon(type_name));
-			}
-
 			method_search->set_right_icon(get_editor_theme_icon("Search"));
 			open_method_tree->set_button_icon(get_editor_theme_icon("Edit"));
 		} break;
@@ -839,18 +835,11 @@ ConnectDialog::ConnectDialog() {
 
 	HBoxContainer *add_bind_hb = memnew(HBoxContainer);
 
-	type_list = memnew(OptionButton);
+	type_list = memnew(EditorVariantTypeOptionButton);
 	type_list->set_accessibility_name(TTRC("Type"));
 	type_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+	type_list->populate({ Variant::NIL, Variant::OBJECT });
 	add_bind_hb->add_child(type_list);
-	for (int i = 0; i < Variant::VARIANT_MAX; i++) {
-		if (i == Variant::NIL || i == Variant::OBJECT || i == Variant::CALLABLE || i == Variant::SIGNAL || i == Variant::RID) {
-			// These types can't be constructed or serialized properly, so skip them.
-			continue;
-		}
-
-		type_list->add_item(Variant::get_type_name(Variant::Type(i)), i);
-	}
 	bind_controls.push_back(type_list);
 
 	Button *add_bind = memnew(Button);

+ 3 - 2
editor/connections_dialog.h

@@ -30,14 +30,15 @@
 
 #pragma once
 
-#include "scene/gui/check_button.h"
 #include "scene/gui/dialogs.h"
 #include "scene/gui/tree.h"
 
 class Button;
 class CheckBox;
+class CheckButton;
 class ConnectDialogBinds;
 class EditorInspector;
+class EditorVariantTypeOptionButton;
 class Label;
 class LineEdit;
 class OptionButton;
@@ -133,7 +134,7 @@ private:
 
 	SpinBox *unbind_count = nullptr;
 	EditorInspector *bind_editor = nullptr;
-	OptionButton *type_list = nullptr;
+	EditorVariantTypeOptionButton *type_list = nullptr;
 	CheckBox *deferred = nullptr;
 	CheckBox *one_shot = nullptr;
 	CheckBox *append_source = nullptr;

+ 20 - 20
editor/editor_properties.cpp

@@ -41,6 +41,7 @@
 #include "editor/editor_string_names.h"
 #include "editor/gui/editor_file_dialog.h"
 #include "editor/gui/editor_spin_slider.h"
+#include "editor/gui/editor_variant_type_selectors.h"
 #include "editor/gui/scene_tree_editor.h"
 #include "editor/inspector_dock.h"
 #include "editor/plugins/script_editor_plugin.h"
@@ -80,8 +81,20 @@ void EditorPropertyVariant::_change_type(int p_to_type) {
 	emit_changed(get_edited_property(), zero);
 }
 
+void EditorPropertyVariant::_popup_edit_menu() {
+	if (change_type == nullptr) {
+		change_type = memnew(EditorVariantTypePopupMenu(false));
+		change_type->connect(SceneStringName(id_pressed), callable_mp(this, &EditorPropertyVariant::_change_type));
+		content->add_child(change_type);
+	}
+
+	Rect2 rect = edit_button->get_screen_rect();
+	change_type->set_position(rect.get_end() - Vector2(change_type->get_contents_minimum_size().x, 0));
+	change_type->popup();
+}
+
 void EditorPropertyVariant::_set_read_only(bool p_read_only) {
-	change_type->set_disabled(p_read_only);
+	edit_button->set_disabled(p_read_only);
 	if (sub_property) {
 		sub_property->set_read_only(p_read_only);
 	}
@@ -89,12 +102,7 @@ void EditorPropertyVariant::_set_read_only(bool p_read_only) {
 
 void EditorPropertyVariant::_notification(int p_what) {
 	if (p_what == NOTIFICATION_THEME_CHANGED) {
-		change_type->set_button_icon(get_editor_theme_icon("Edit"));
-
-		PopupMenu *popup = change_type->get_popup();
-		for (int i = 0; i < popup->get_item_count(); i++) {
-			popup->set_item_icon(i, get_editor_theme_icon(Variant::get_type_name(Variant::Type(popup->get_item_id(i)))));
-		}
+		edit_button->set_button_icon(get_editor_theme_icon(SNAME("Edit")));
 	}
 }
 
@@ -139,19 +147,11 @@ EditorPropertyVariant::EditorPropertyVariant() {
 	content = memnew(HBoxContainer);
 	add_child(content);
 
-	change_type = memnew(MenuButton);
-	change_type->set_flat(false);
-
-	PopupMenu *popup = change_type->get_popup();
-	for (int i = 0; i < Variant::VARIANT_MAX; i++) {
-		if (i == Variant::CALLABLE || i == Variant::SIGNAL || i == Variant::RID) {
-			// These types can't be constructed or serialized properly, so skip them.
-			continue;
-		}
-		popup->add_item(Variant::get_type_name(Variant::Type(i)), i);
-	}
-	popup->connect(SceneStringName(id_pressed), callable_mp(this, &EditorPropertyVariant::_change_type));
-	content->add_child(change_type);
+	edit_button = memnew(Button);
+	edit_button->set_flat(true);
+	edit_button->set_accessibility_name(TTRC("Edit"));
+	edit_button->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyVariant::_popup_edit_menu));
+	content->add_child(edit_button);
 }
 
 ///////////////////// TEXT /////////////////////////

+ 4 - 1
editor/editor_properties.h

@@ -39,6 +39,7 @@ class EditorFileDialog;
 class EditorLocaleDialog;
 class EditorResourcePicker;
 class EditorSpinSlider;
+class EditorVariantTypePopupMenu;
 class MenuButton;
 class PropertySelector;
 class SceneTreeDialog;
@@ -59,12 +60,14 @@ class EditorPropertyVariant : public EditorProperty {
 
 	HBoxContainer *content = nullptr;
 	EditorProperty *sub_property = nullptr;
-	MenuButton *change_type = nullptr;
+	Button *edit_button = nullptr;
+	EditorVariantTypePopupMenu *change_type = nullptr;
 
 	Variant::Type current_type = Variant::VARIANT_MAX;
 	Variant::Type new_type = Variant::VARIANT_MAX;
 
 	void _change_type(int p_to_type);
+	void _popup_edit_menu();
 
 protected:
 	virtual void _set_read_only(bool p_read_only) override;

+ 3 - 28
editor/editor_properties_array_dict.cpp

@@ -39,6 +39,7 @@
 #include "editor/editor_settings.h"
 #include "editor/editor_string_names.h"
 #include "editor/gui/editor_spin_slider.h"
+#include "editor/gui/editor_variant_type_selectors.h"
 #include "editor/inspector_dock.h"
 #include "editor/themes/editor_scale.h"
 #include "scene/gui/button.h"
@@ -750,19 +751,6 @@ Node *EditorPropertyArray::get_base_node() {
 void EditorPropertyArray::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_THEME_CHANGED: {
-			change_type->clear();
-			change_type->add_icon_item(get_editor_theme_icon(SNAME("Remove")), TTR("Remove Item"), Variant::VARIANT_MAX);
-			change_type->add_separator();
-			for (int i = 0; i < Variant::VARIANT_MAX; i++) {
-				if (i == Variant::CALLABLE || i == Variant::SIGNAL || i == Variant::RID) {
-					// These types can't be constructed or serialized properly, so skip them.
-					continue;
-				}
-
-				String type = Variant::get_type_name(Variant::Type(i));
-				change_type->add_icon_item(get_editor_theme_icon(type), type, i);
-			}
-
 			if (button_add_item) {
 				button_add_item->set_button_icon(get_editor_theme_icon(SNAME("Add")));
 			}
@@ -977,7 +965,7 @@ EditorPropertyArray::EditorPropertyArray() {
 	add_child(edit);
 	add_focusable(edit);
 
-	change_type = memnew(PopupMenu);
+	change_type = memnew(EditorVariantTypePopupMenu(true));
 	add_child(change_type);
 	change_type->connect(SceneStringName(id_pressed), callable_mp(this, &EditorPropertyArray::_change_type_menu));
 	changing_type_index = -1;
@@ -1465,19 +1453,6 @@ void EditorPropertyDictionary::_object_id_selected(const StringName &p_property,
 void EditorPropertyDictionary::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_THEME_CHANGED: {
-			change_type->clear();
-			change_type->add_icon_item(get_editor_theme_icon(SNAME("Remove")), TTR("Remove Item"), Variant::VARIANT_MAX);
-			change_type->add_separator();
-			for (int i = 0; i < Variant::VARIANT_MAX; i++) {
-				if (i == Variant::CALLABLE || i == Variant::SIGNAL || i == Variant::RID) {
-					// These types can't be constructed or serialized properly, so skip them.
-					continue;
-				}
-
-				String type = Variant::get_type_name(Variant::Type(i));
-				change_type->add_icon_item(get_editor_theme_icon(type), type, i);
-			}
-
 			if (button_add_item) {
 				button_add_item->set_button_icon(get_editor_theme_icon(SNAME("Add")));
 				add_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("DictionaryAddItem")));
@@ -1532,7 +1507,7 @@ EditorPropertyDictionary::EditorPropertyDictionary() {
 	container = nullptr;
 	button_add_item = nullptr;
 	paginator = nullptr;
-	change_type = memnew(PopupMenu);
+	change_type = memnew(EditorVariantTypePopupMenu(true));
 	add_child(change_type);
 	change_type->connect(SceneStringName(id_pressed), callable_mp(this, &EditorPropertyDictionary::_change_type_menu));
 	changing_type_index = EditorPropertyDictionaryObject::NOT_CHANGING_TYPE;

+ 3 - 2
editor/editor_properties_array_dict.h

@@ -35,6 +35,7 @@
 
 class Button;
 class EditorSpinSlider;
+class EditorVariantTypePopupMenu;
 class MarginContainer;
 
 class EditorPropertyArrayObject : public RefCounted {
@@ -108,7 +109,7 @@ class EditorPropertyArray : public EditorProperty {
 		}
 	};
 
-	PopupMenu *change_type = nullptr;
+	EditorVariantTypePopupMenu *change_type = nullptr;
 
 	bool preview_value = false;
 	int page_length = 20;
@@ -221,7 +222,7 @@ class EditorPropertyDictionary : public EditorProperty {
 		}
 	};
 
-	PopupMenu *change_type = nullptr;
+	EditorVariantTypePopupMenu *change_type = nullptr;
 	bool updating = false;
 
 	bool preview_value = false;

+ 179 - 0
editor/gui/editor_variant_type_selectors.cpp

@@ -0,0 +1,179 @@
+/**************************************************************************/
+/*  editor_variant_type_selectors.cpp                                     */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* Permission is hereby granted, free of charge, to any person obtaining  */
+/* a copy of this software and associated documentation files (the        */
+/* "Software"), to deal in the Software without restriction, including    */
+/* without limitation the rights to use, copy, modify, merge, publish,    */
+/* distribute, sublicense, and/or sell copies of the Software, and to     */
+/* permit persons to whom the Software is furnished to do so, subject to  */
+/* the following conditions:                                              */
+/*                                                                        */
+/* The above copyright notice and this permission notice shall be         */
+/* included in all copies or substantial portions of the Software.        */
+/*                                                                        */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
+/**************************************************************************/
+
+#include "editor_variant_type_selectors.h"
+
+struct CompareVariantTypeNames {
+	bool operator()(const String &p_lhs, const String &p_rhs) const {
+		// Variant type names should not be empty, but just in case.
+		DEV_ASSERT(!p_lhs.is_empty() && !p_rhs.is_empty());
+
+		// Variant type names are ascii strings.
+		const bool lhs_lower = is_ascii_lower_case(p_lhs[0]);
+		const bool rhs_lower = is_ascii_lower_case(p_rhs[0]);
+		if (lhs_lower != rhs_lower) {
+			// Lowercase types like `int` and `float` come first.
+			return lhs_lower > rhs_lower;
+		}
+
+		return p_lhs < p_rhs;
+	}
+};
+
+// EditorVariantTypeOptionButton
+
+void EditorVariantTypeOptionButton::_update_menu_icons() {
+	for (int i = 0; i < get_item_count(); i++) {
+		const Variant::Type type = Variant::Type(get_item_id(i));
+		const String &type_name = Variant::get_type_name(type);
+		set_item_icon(i, get_editor_theme_icon(type_name));
+	}
+}
+
+void EditorVariantTypeOptionButton::_notification(int p_what) {
+	switch (p_what) {
+		case NOTIFICATION_POSTINITIALIZE: {
+			set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
+		} break;
+
+		case NOTIFICATION_THEME_CHANGED: {
+			_update_menu_icons();
+		} break;
+	}
+}
+
+Variant::Type EditorVariantTypeOptionButton::get_selected_type() const {
+	int selected = get_selected();
+	if (selected == -1) {
+		return Variant::NIL;
+	}
+	return Variant::Type(get_item_id(selected));
+}
+
+void EditorVariantTypeOptionButton::populate(const LocalVector<Variant::Type> &p_disabled_types, const HashMap<Variant::Type, String> &p_renames) {
+	LocalVector<String> names;
+	HashMap<String, Variant::Type> name_to_type;
+	names.reserve(Variant::VARIANT_MAX);
+	name_to_type.reserve(Variant::VARIANT_MAX);
+
+	for (int i = 0; i < Variant::VARIANT_MAX; i++) {
+		const Variant::Type type = Variant::Type(i);
+
+		if (p_disabled_types.has(type) || type == Variant::RID || type == Variant::CALLABLE || type == Variant::SIGNAL) {
+			continue;
+		}
+
+		const String &type_name = Variant::get_type_name(type);
+		const String &display_name = p_renames.has(type) ? p_renames[type] : type_name;
+		names.push_back(display_name);
+		name_to_type[display_name] = type;
+	}
+
+	names.sort_custom<CompareVariantTypeNames>();
+
+	for (const String &name : names) {
+		add_item(name, name_to_type[name]);
+	}
+
+	_update_menu_icons();
+}
+
+// EditorVariantTypeArrayItemMenu
+
+void EditorVariantTypePopupMenu::_populate() {
+	if (remove_item) {
+		add_item(TTRC("Remove Item"), Variant::VARIANT_MAX);
+		set_item_auto_translate_mode(-1, AUTO_TRANSLATE_MODE_ALWAYS);
+		add_separator();
+	}
+
+	LocalVector<String> names;
+	names.reserve(Variant::VARIANT_MAX);
+
+	for (int i = 0; i < Variant::VARIANT_MAX; i++) {
+		const Variant::Type type = Variant::Type(i);
+
+		if (type == Variant::RID || type == Variant::CALLABLE || type == Variant::SIGNAL) {
+			continue;
+		}
+
+		names.push_back(Variant::get_type_name(type));
+	}
+
+	names.sort_custom<CompareVariantTypeNames>();
+
+	for (const String &name : names) {
+		add_item(name, Variant::get_type_by_name(name));
+	}
+}
+
+void EditorVariantTypePopupMenu::_update_menu_icons() {
+	if (remove_item) {
+		set_item_icon(get_item_index(Variant::VARIANT_MAX), get_editor_theme_icon(SNAME("Remove")));
+	}
+
+	for (int i = 0; i < get_item_count(); i++) {
+		int id = get_item_id(i);
+
+		// Skip the Remove Item option and the separator without hardcoding the index.
+		if (id < 0 || id >= Variant::VARIANT_MAX) {
+			continue;
+		}
+
+		const Variant::Type type = Variant::Type(id);
+		const String &type_name = Variant::get_type_name(type);
+		set_item_icon(i, get_editor_theme_icon(type_name));
+	}
+}
+
+void EditorVariantTypePopupMenu::_notification(int p_what) {
+	switch (p_what) {
+		case NOTIFICATION_POSTINITIALIZE: {
+			set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
+			_populate();
+		} break;
+
+		case NOTIFICATION_THEME_CHANGED: {
+			icons_dirty = true;
+		} break;
+	}
+}
+
+void EditorVariantTypePopupMenu::popup(const Rect2i &p_bounds) {
+	if (icons_dirty) {
+		_update_menu_icons();
+		icons_dirty = false;
+	}
+	PopupMenu::popup(p_bounds);
+}
+
+EditorVariantTypePopupMenu::EditorVariantTypePopupMenu(bool p_remove_item) {
+	remove_item = p_remove_item;
+}

+ 67 - 0
editor/gui/editor_variant_type_selectors.h

@@ -0,0 +1,67 @@
+/**************************************************************************/
+/*  editor_variant_type_selectors.h                                       */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* Permission is hereby granted, free of charge, to any person obtaining  */
+/* a copy of this software and associated documentation files (the        */
+/* "Software"), to deal in the Software without restriction, including    */
+/* without limitation the rights to use, copy, modify, merge, publish,    */
+/* distribute, sublicense, and/or sell copies of the Software, and to     */
+/* permit persons to whom the Software is furnished to do so, subject to  */
+/* the following conditions:                                              */
+/*                                                                        */
+/* The above copyright notice and this permission notice shall be         */
+/* included in all copies or substantial portions of the Software.        */
+/*                                                                        */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
+/**************************************************************************/
+
+#pragma once
+
+#include "core/variant/variant.h"
+#include "scene/gui/option_button.h"
+#include "scene/gui/popup_menu.h"
+
+class EditorVariantTypeOptionButton : public OptionButton {
+	GDCLASS(EditorVariantTypeOptionButton, OptionButton);
+
+	void _update_menu_icons();
+
+protected:
+	void _notification(int p_what);
+
+public:
+	Variant::Type get_selected_type() const;
+
+	void populate(const LocalVector<Variant::Type> &p_disabled_types, const HashMap<Variant::Type, String> &p_renames = {});
+};
+
+class EditorVariantTypePopupMenu : public PopupMenu {
+	GDCLASS(EditorVariantTypePopupMenu, PopupMenu);
+
+	bool remove_item = false;
+	bool icons_dirty = true;
+
+	void _populate();
+	void _update_menu_icons();
+
+protected:
+	void _notification(int p_what);
+
+public:
+	virtual void popup(const Rect2i &p_bounds = Rect2i()) override;
+
+	EditorVariantTypePopupMenu(bool p_remove_item);
+};

+ 4 - 12
editor/project_settings_editor.cpp

@@ -38,6 +38,7 @@
 #include "editor/editor_string_names.h"
 #include "editor/editor_undo_redo_manager.h"
 #include "editor/export/editor_export.h"
+#include "editor/gui/editor_variant_type_selectors.h"
 #include "editor/themes/editor_scale.h"
 #include "scene/gui/check_button.h"
 #include "servers/movie_writer/movie_writer.h"
@@ -132,7 +133,7 @@ void ProjectSettingsEditor::_add_setting() {
 	// Initialize the property with the default value for the given type.
 	Callable::CallError ce;
 	Variant value;
-	Variant::construct(Variant::Type(type_box->get_selected_id()), value, nullptr, 0, ce);
+	Variant::construct(type_box->get_selected_type(), value, nullptr, 0, ce);
 
 	EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
 	undo_redo->create_action(TTR("Add Project Setting"));
@@ -605,16 +606,6 @@ void ProjectSettingsEditor::_update_theme() {
 	restart_container->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree")));
 	restart_icon->set_texture(get_editor_theme_icon(SNAME("StatusWarning")));
 	restart_label->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));
-
-	type_box->clear();
-	for (int i = 0; i < Variant::VARIANT_MAX; i++) {
-		if (i == Variant::NIL || i == Variant::OBJECT || i == Variant::CALLABLE || i == Variant::SIGNAL || i == Variant::RID) {
-			// These types can't be serialized properly, so skip them.
-			continue;
-		}
-		String type = Variant::get_type_name(Variant::Type(i));
-		type_box->add_icon_item(get_editor_theme_icon(type), type, i);
-	}
 }
 
 void ProjectSettingsEditor::_notification(int p_what) {
@@ -699,7 +690,8 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
 	feature_box->connect(SceneStringName(item_selected), callable_mp(this, &ProjectSettingsEditor::_feature_selected));
 	custom_properties->add_child(feature_box);
 
-	type_box = memnew(OptionButton);
+	type_box = memnew(EditorVariantTypeOptionButton);
+	type_box->populate({ Variant::NIL, Variant::OBJECT });
 	type_box->set_custom_minimum_size(Size2(120, 0) * EDSCALE);
 	type_box->set_accessibility_name(TTRC("Type"));
 	custom_properties->add_child(type_box);

+ 2 - 1
editor/project_settings_editor.h

@@ -44,6 +44,7 @@
 #include "scene/gui/tab_container.h"
 #include "scene/gui/texture_rect.h"
 
+class EditorVariantTypeOptionButton;
 class FileSystemDock;
 
 class ProjectSettingsEditor : public AcceptDialog {
@@ -76,7 +77,7 @@ class ProjectSettingsEditor : public AcceptDialog {
 	HBoxContainer *custom_properties = nullptr;
 	LineEdit *property_box = nullptr;
 	OptionButton *feature_box = nullptr;
-	OptionButton *type_box = nullptr;
+	EditorVariantTypeOptionButton *type_box = nullptr;
 	Button *add_button = nullptr;
 	Button *del_button = nullptr;