Sfoglia il codice sorgente

Merge pull request #100135 from Lazy-Rabbit-2001/expose-create-dialog-new

Add `popup_create_dialog()` for `EditorInterface` to create custom create dialog
Rémi Verschelde 9 mesi fa
parent
commit
4cf1b0d94c

+ 29 - 0
doc/classes/EditorInterface.xml

@@ -270,6 +270,35 @@
 				Plays the main scene.
 				Plays the main scene.
 			</description>
 			</description>
 		</method>
 		</method>
+		<method name="popup_create_dialog">
+			<return type="void" />
+			<param index="0" name="callback" type="Callable" />
+			<param index="1" name="base_type" type="StringName" default="&quot;&quot;" />
+			<param index="2" name="current_type" type="String" default="&quot;&quot;" />
+			<param index="3" name="dialog_title" type="String" default="&quot;&quot;" />
+			<param index="4" name="type_blocklist" type="StringName[]" default="[]" />
+			<param index="5" name="type_suffixes" type="Dictionary" default="{}" />
+			<description>
+				Pops up an editor dialog for creating an object.
+				The [param callback] must take a single argument of type [StringName] which will contain the type name of the selected object or be empty if no item is selected.
+				The [param base_type] specifies the base type of objects to display. For example, if you set this to "Resource", all types derived from [Resource] will display in the create dialog.
+				The [param current_type] will be passed in the search box of the create dialog, and the specified type can be immediately selected when the dialog pops up. If the [param current_type] is not derived from [param base_type], there will be no result of the type in the dialog.
+				The [param dialog_title] allows you to define a custom title for the dialog. This is useful if you want to accurately hint the usage of the dialog. If the [param dialog_title] is an empty string, the dialog will use "Create New 'Base Type'" as the default title.
+				The [param type_blocklist] contains a list of type names, and the types in the blocklist will be hidden from the create dialog.
+				The [param type_suffixes] is a dictionary, with keys being [StringName]s and values being [String]s. Custom suffixes override the default suffixes which are file names of their scripts. For example, if you set a custom suffix as "Custom Suffix" for a global script type,
+				[codeblock lang=text]
+				Node
+				|- MyCustomNode (my_custom_node.gd)
+				[/codeblock]
+				will be
+				[codeblock lang=text]
+				Node
+				|- MyCustomNode (Custom Suffix)
+				[/codeblock]
+				Bear in mind that when a built-in type does not have any custom suffix, its suffix will be removed. The suffix of a type created from a script will fall back to its script file name. For global types by scripts, if you customize their suffixes to an empty string, their suffixes will be removed.
+				[b]Note:[/b] Trying to list the base type in the [param type_blocklist] will hide all types derived from the base type from the create dialog.
+			</description>
+		</method>
 		<method name="popup_dialog">
 		<method name="popup_dialog">
 			<return type="void" />
 			<return type="void" />
 			<param index="0" name="dialog" type="Window" />
 			<param index="0" name="dialog" type="Window" />

+ 25 - 4
editor/create_dialog.cpp

@@ -145,6 +145,11 @@ bool CreateDialog::_should_hide_type(const StringName &p_type) const {
 				return true; // Parent type is blacklisted.
 				return true; // Parent type is blacklisted.
 			}
 			}
 		}
 		}
+		for (const StringName &F : custom_type_blocklist) {
+			if (ClassDB::is_parent_class(p_type, F)) {
+				return true; // Parent type is excluded in custom type blocklist.
+			}
+		}
 	} else {
 	} else {
 		if (!ScriptServer::is_global_class(p_type)) {
 		if (!ScriptServer::is_global_class(p_type)) {
 			return true;
 			return true;
@@ -154,8 +159,12 @@ bool CreateDialog::_should_hide_type(const StringName &p_type) const {
 		}
 		}
 
 
 		StringName native_type = ScriptServer::get_global_class_native_base(p_type);
 		StringName native_type = ScriptServer::get_global_class_native_base(p_type);
-		if (ClassDB::class_exists(native_type) && !ClassDB::can_instantiate(native_type)) {
-			return true;
+		if (ClassDB::class_exists(native_type)) {
+			if (!ClassDB::can_instantiate(native_type)) {
+				return true;
+			} else if (custom_type_blocklist.has(p_type) || custom_type_blocklist.has(native_type)) {
+				return true;
+			}
 		}
 		}
 
 
 		String script_path = ScriptServer::get_global_class_path(p_type);
 		String script_path = ScriptServer::get_global_class_path(p_type);
@@ -283,15 +292,27 @@ void CreateDialog::_configure_search_option_item(TreeItem *r_item, const StringN
 	bool is_abstract = false;
 	bool is_abstract = false;
 	if (p_type_category == TypeCategory::CPP_TYPE) {
 	if (p_type_category == TypeCategory::CPP_TYPE) {
 		r_item->set_text(0, p_type);
 		r_item->set_text(0, p_type);
+		if (custom_type_suffixes.has(p_type)) {
+			String suffix = custom_type_suffixes.get(p_type);
+			if (!suffix.is_empty()) {
+				r_item->set_suffix(0, "(" + suffix + ")");
+			}
+		}
 	} else if (p_type_category == TypeCategory::PATH_TYPE) {
 	} else if (p_type_category == TypeCategory::PATH_TYPE) {
 		r_item->set_text(0, "\"" + p_type + "\"");
 		r_item->set_text(0, "\"" + p_type + "\"");
 	} else if (script_type) {
 	} else if (script_type) {
 		r_item->set_metadata(0, p_type);
 		r_item->set_metadata(0, p_type);
 		r_item->set_text(0, p_type);
 		r_item->set_text(0, p_type);
 		String script_path = ScriptServer::get_global_class_path(p_type);
 		String script_path = ScriptServer::get_global_class_path(p_type);
-		r_item->set_suffix(0, "(" + script_path.get_file() + ")");
-
 		Ref<Script> scr = ResourceLoader::load(script_path, "Script");
 		Ref<Script> scr = ResourceLoader::load(script_path, "Script");
+		String suffix = script_path.get_file();
+		if (scr.is_valid() && custom_type_suffixes.has(p_type)) {
+			suffix = custom_type_suffixes.get(p_type);
+		}
+		if (!suffix.is_empty()) {
+			r_item->set_suffix(0, "(" + suffix + ")");
+		}
+
 		ERR_FAIL_COND(!scr.is_valid());
 		ERR_FAIL_COND(!scr.is_valid());
 		is_abstract = scr->is_abstract();
 		is_abstract = scr->is_abstract();
 	} else {
 	} else {

+ 5 - 1
editor/create_dialog.h

@@ -66,6 +66,8 @@ class CreateDialog : public ConfirmationDialog {
 	HashMap<String, int> custom_type_indices;
 	HashMap<String, int> custom_type_indices;
 	List<StringName> type_list;
 	List<StringName> type_list;
 	HashSet<StringName> type_blacklist;
 	HashSet<StringName> type_blacklist;
+	HashSet<StringName> custom_type_blocklist;
+	HashMap<StringName, String> custom_type_suffixes;
 
 
 	void _update_search();
 	void _update_search();
 	bool _should_hide_type(const StringName &p_type) const;
 	bool _should_hide_type(const StringName &p_type) const;
@@ -115,8 +117,10 @@ public:
 	String get_base_type() const { return base_type; }
 	String get_base_type() const { return base_type; }
 	void select_base();
 	void select_base();
 
 
+	void set_type_blocklist(const HashSet<StringName> &p_blocklist) { custom_type_blocklist = p_blocklist; }
+	void set_type_suffixes(const HashMap<StringName, String> &p_suffixes) { custom_type_suffixes = p_suffixes; }
+
 	void set_preferred_search_result_type(const String &p_preferred_type) { preferred_search_result_type = p_preferred_type; }
 	void set_preferred_search_result_type(const String &p_preferred_type) { preferred_search_result_type = p_preferred_type; }
-	String get_preferred_search_result_type() { return preferred_search_result_type; }
 
 
 	void popup_create(bool p_dont_clear, bool p_replace_mode = false, const String &p_current_type = "", const String &p_current_name = "");
 	void popup_create(bool p_dont_clear, bool p_replace_mode = false, const String &p_current_type = "", const String &p_current_name = "");
 
 

+ 48 - 0
editor/editor_interface.cpp

@@ -32,6 +32,7 @@
 #include "editor_interface.compat.inc"
 #include "editor_interface.compat.inc"
 
 
 #include "core/config/project_settings.h"
 #include "core/config/project_settings.h"
+#include "editor/create_dialog.h"
 #include "editor/editor_command_palette.h"
 #include "editor/editor_command_palette.h"
 #include "editor/editor_feature_profile.h"
 #include "editor/editor_feature_profile.h"
 #include "editor/editor_main_screen.h"
 #include "editor/editor_main_screen.h"
@@ -512,6 +513,45 @@ void EditorInterface::popup_quick_open(const Callable &p_callback, const TypedAr
 	quick_open->popup_dialog(base_types, callable_mp(this, &EditorInterface::_quick_open).bind(p_callback));
 	quick_open->popup_dialog(base_types, callable_mp(this, &EditorInterface::_quick_open).bind(p_callback));
 }
 }
 
 
+void EditorInterface::popup_create_dialog(const Callable &p_callback, const StringName &p_base_type, const String &p_current_type, const String &p_dialog_title, const TypedArray<StringName> &p_custom_type_blocklist, const Dictionary &p_custom_suffix) {
+	if (!create_dialog) {
+		create_dialog = memnew(CreateDialog);
+		get_base_control()->add_child(create_dialog);
+	}
+
+	HashSet<StringName> blocklist;
+	for (const Variant &E : p_custom_type_blocklist) {
+		blocklist.insert(E);
+	}
+	create_dialog->set_type_blocklist(blocklist);
+
+	HashMap<StringName, String> suffix_map;
+	List<Variant> keys;
+	p_custom_suffix.get_key_list(&keys);
+	for (Variant &k : keys) {
+		const StringName key = k;
+		if (key.is_empty()) {
+			continue;
+		}
+		suffix_map.insert(key, p_custom_suffix[key]);
+	}
+	create_dialog->set_type_suffixes(suffix_map);
+
+	String safe_base_type = p_base_type;
+	if (p_base_type.is_empty() || (!ClassDB::class_exists(p_base_type) && !ScriptServer::is_global_class(p_base_type))) {
+		ERR_PRINT(vformat("Invalid base type '%s'. The base type has fallen back to 'Object'.", p_base_type));
+		safe_base_type = "Object";
+	}
+
+	create_dialog->set_base_type(safe_base_type);
+	create_dialog->popup_create(false, true, p_current_type, "");
+	create_dialog->set_title(p_dialog_title.is_empty() ? vformat(TTR("Create New %s"), p_base_type) : p_dialog_title);
+
+	const Callable callback = callable_mp(this, &EditorInterface::_create_dialog_item_selected);
+	create_dialog->connect(SNAME("create"), callback.bind(false, p_callback), CONNECT_DEFERRED);
+	create_dialog->connect(SNAME("canceled"), callback.bind(true, p_callback), CONNECT_DEFERRED);
+}
+
 void EditorInterface::_node_selected(const NodePath &p_node_path, const Callable &p_callback) {
 void EditorInterface::_node_selected(const NodePath &p_node_path, const Callable &p_callback) {
 	const Callable callback = callable_mp(this, &EditorInterface::_node_selected);
 	const Callable callback = callable_mp(this, &EditorInterface::_node_selected);
 	node_selector->disconnect(SNAME("selected"), callback);
 	node_selector->disconnect(SNAME("selected"), callback);
@@ -555,6 +595,13 @@ void EditorInterface::_quick_open(const String &p_file_path, const Callable &p_c
 	_call_dialog_callback(p_callback, p_file_path, "quick open");
 	_call_dialog_callback(p_callback, p_file_path, "quick open");
 }
 }
 
 
+void EditorInterface::_create_dialog_item_selected(bool p_is_canceled, const Callable &p_callback) {
+	const Callable callback = callable_mp(this, &EditorInterface::_create_dialog_item_selected);
+	create_dialog->disconnect(SNAME("create"), callback);
+	create_dialog->disconnect(SNAME("canceled"), callback);
+	_call_dialog_callback(p_callback, p_is_canceled ? "" : create_dialog->get_selected_type(), "create dialog");
+}
+
 void EditorInterface::_call_dialog_callback(const Callable &p_callback, const Variant &p_selected, const String &p_context) {
 void EditorInterface::_call_dialog_callback(const Callable &p_callback, const Variant &p_selected, const String &p_context) {
 	Callable::CallError ce;
 	Callable::CallError ce;
 	Variant ret;
 	Variant ret;
@@ -773,6 +820,7 @@ void EditorInterface::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("popup_property_selector", "object", "callback", "type_filter", "current_value"), &EditorInterface::popup_property_selector, DEFVAL(PackedInt32Array()), DEFVAL(String()));
 	ClassDB::bind_method(D_METHOD("popup_property_selector", "object", "callback", "type_filter", "current_value"), &EditorInterface::popup_property_selector, DEFVAL(PackedInt32Array()), DEFVAL(String()));
 	ClassDB::bind_method(D_METHOD("popup_method_selector", "object", "callback", "current_value"), &EditorInterface::popup_method_selector, DEFVAL(String()));
 	ClassDB::bind_method(D_METHOD("popup_method_selector", "object", "callback", "current_value"), &EditorInterface::popup_method_selector, DEFVAL(String()));
 	ClassDB::bind_method(D_METHOD("popup_quick_open", "callback", "base_types"), &EditorInterface::popup_quick_open, DEFVAL(TypedArray<StringName>()));
 	ClassDB::bind_method(D_METHOD("popup_quick_open", "callback", "base_types"), &EditorInterface::popup_quick_open, DEFVAL(TypedArray<StringName>()));
+	ClassDB::bind_method(D_METHOD("popup_create_dialog", "callback", "base_type", "current_type", "dialog_title", "type_blocklist", "type_suffixes"), &EditorInterface::popup_create_dialog, DEFVAL(""), DEFVAL(""), DEFVAL(""), DEFVAL(TypedArray<StringName>()), DEFVAL(Dictionary()));
 
 
 	// Editor docks.
 	// Editor docks.
 
 

+ 4 - 0
editor/editor_interface.h

@@ -37,6 +37,7 @@
 #include "core/object/script_language.h"
 #include "core/object/script_language.h"
 
 
 class Control;
 class Control;
+class CreateDialog;
 class EditorCommandPalette;
 class EditorCommandPalette;
 class EditorFileSystem;
 class EditorFileSystem;
 class EditorInspector;
 class EditorInspector;
@@ -69,11 +70,13 @@ class EditorInterface : public Object {
 	PropertySelector *property_selector = nullptr;
 	PropertySelector *property_selector = nullptr;
 	PropertySelector *method_selector = nullptr;
 	PropertySelector *method_selector = nullptr;
 	SceneTreeDialog *node_selector = nullptr;
 	SceneTreeDialog *node_selector = nullptr;
+	CreateDialog *create_dialog = nullptr;
 
 
 	void _node_selected(const NodePath &p_node_paths, const Callable &p_callback);
 	void _node_selected(const NodePath &p_node_paths, const Callable &p_callback);
 	void _property_selected(const String &p_property_name, const Callable &p_callback);
 	void _property_selected(const String &p_property_name, const Callable &p_callback);
 	void _method_selected(const String &p_property_name, const Callable &p_callback);
 	void _method_selected(const String &p_property_name, const Callable &p_callback);
 	void _quick_open(const String &p_file_path, const Callable &p_callback);
 	void _quick_open(const String &p_file_path, const Callable &p_callback);
+	void _create_dialog_item_selected(bool p_is_canceled, const Callable &p_callback);
 	void _call_dialog_callback(const Callable &p_callback, const Variant &p_selected, const String &p_context);
 	void _call_dialog_callback(const Callable &p_callback, const Variant &p_selected, const String &p_context);
 
 
 	// Editor tools.
 	// Editor tools.
@@ -145,6 +148,7 @@ public:
 	void popup_property_selector(Object *p_object, const Callable &p_callback, const PackedInt32Array &p_type_filter = PackedInt32Array(), const String &p_current_value = String());
 	void popup_property_selector(Object *p_object, const Callable &p_callback, const PackedInt32Array &p_type_filter = PackedInt32Array(), const String &p_current_value = String());
 	void popup_method_selector(Object *p_object, const Callable &p_callback, const String &p_current_value = String());
 	void popup_method_selector(Object *p_object, const Callable &p_callback, const String &p_current_value = String());
 	void popup_quick_open(const Callable &p_callback, const TypedArray<StringName> &p_base_types = TypedArray<StringName>());
 	void popup_quick_open(const Callable &p_callback, const TypedArray<StringName> &p_base_types = TypedArray<StringName>());
+	void popup_create_dialog(const Callable &p_callback, const StringName &p_base_type = "", const String &p_current_type = "", const String &p_dialog_title = "", const TypedArray<StringName> &p_custom_type_blocklist = TypedArray<String>(), const Dictionary &p_custom_suffix = Dictionary());
 
 
 	// Editor docks.
 	// Editor docks.