Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add popup_create_dialog() for EditorInterface to create custom create dialog #100135

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions doc/classes/EditorInterface.xml
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,35 @@
Plays the main scene.
</description>
</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">
<return type="void" />
<param index="0" name="dialog" type="Window" />
Expand Down
29 changes: 25 additions & 4 deletions editor/create_dialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,11 @@ bool CreateDialog::_should_hide_type(const StringName &p_type) const {
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 {
if (!ScriptServer::is_global_class(p_type)) {
return true;
Expand All @@ -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);
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);
Expand Down Expand Up @@ -283,15 +292,27 @@ void CreateDialog::_configure_search_option_item(TreeItem *r_item, const StringN
bool is_abstract = false;
if (p_type_category == TypeCategory::CPP_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) {
r_item->set_text(0, "\"" + p_type + "\"");
} else if (script_type) {
r_item->set_metadata(0, p_type);
r_item->set_text(0, 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");
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());
is_abstract = scr->is_abstract();
} else {
Expand Down
6 changes: 5 additions & 1 deletion editor/create_dialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ class CreateDialog : public ConfirmationDialog {
HashMap<String, int> custom_type_indices;
List<StringName> type_list;
HashSet<StringName> type_blacklist;
HashSet<StringName> custom_type_blocklist;
HashMap<StringName, String> custom_type_suffixes;

void _update_search();
bool _should_hide_type(const StringName &p_type) const;
Expand Down Expand Up @@ -115,8 +117,10 @@ class CreateDialog : public ConfirmationDialog {
String get_base_type() const { return base_type; }
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; }
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 = "");

Expand Down
48 changes: 48 additions & 0 deletions editor/editor_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "editor_interface.compat.inc"

#include "core/config/project_settings.h"
#include "editor/create_dialog.h"
#include "editor/editor_command_palette.h"
#include "editor/editor_feature_profile.h"
#include "editor/editor_main_screen.h"
Expand Down Expand Up @@ -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));
}

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) {
const Callable callback = callable_mp(this, &EditorInterface::_node_selected);
node_selector->disconnect(SNAME("selected"), callback);
Expand Down Expand Up @@ -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");
}

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) {
Callable::CallError ce;
Variant ret;
Expand Down Expand Up @@ -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_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_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.

Expand Down
4 changes: 4 additions & 0 deletions editor/editor_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "core/object/script_language.h"

class Control;
class CreateDialog;
class EditorCommandPalette;
class EditorFileSystem;
class EditorInspector;
Expand Down Expand Up @@ -69,11 +70,13 @@ class EditorInterface : public Object {
PropertySelector *property_selector = nullptr;
PropertySelector *method_selector = nullptr;
SceneTreeDialog *node_selector = nullptr;
CreateDialog *create_dialog = nullptr;

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 _method_selected(const String &p_property_name, 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);

// Editor tools.
Expand Down Expand Up @@ -145,6 +148,7 @@ class EditorInterface : public Object {
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_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.

Expand Down