diff --git a/doc/classes/EditorInterface.xml b/doc/classes/EditorInterface.xml
index d0de09e451..ec0eebae90 100644
--- a/doc/classes/EditorInterface.xml
+++ b/doc/classes/EditorInterface.xml
@@ -285,6 +285,47 @@
See also [method Window.set_unparent_when_invisible].
+
+
+
+
+
+ Pops up an editor dialog for selecting a [Node] from the edited scene. The [param callback] must take a single argument of type [NodePath]. It is called on the selected [NodePath] or the empty path [code]^""[/code] if the dialog is canceled. If [param valid_types] is provided, the dialog will only show Nodes that match one of the listed Node types.
+ [b]Example:[/b]
+ [codeblock]
+ func _ready():
+ if Engine.is_editor_hint():
+ EditorInterface.popup_node_selector(_on_node_selected, ["Button"])
+
+ func _on_node_selected(node_path):
+ if node_path.is_empty():
+ print("node selection canceled")
+ else:
+ print("selected ", node_path)
+ [/codeblock]
+
+
+
+
+
+
+
+
+ Pops up an editor dialog for selecting properties from [param object]. The [param callback] must take a single argument of type [NodePath]. It is called on the selected property path (see [method NodePath.get_as_property_path]) or the empty path [code]^""[/code] if the dialog is canceled. If [param type_filter] is provided, the dialog will only show properties that match one of the listed [enum Variant.Type] values.
+ [b]Example:[/b]
+ [codeblock]
+ func _ready():
+ if Engine.is_editor_hint():
+ EditorInterface.popup_property_selector(this, _on_property_selected, [TYPE_INT])
+
+ func _on_property_selected(property_path):
+ if property_path.is_empty():
+ print("property selection canceled")
+ else:
+ print("selected ", property_path)
+ [/codeblock]
+
+
diff --git a/editor/editor_interface.cpp b/editor/editor_interface.cpp
index bad28ff43d..191de3ffd2 100644
--- a/editor/editor_interface.cpp
+++ b/editor/editor_interface.cpp
@@ -39,8 +39,10 @@
#include "editor/editor_undo_redo_manager.h"
#include "editor/filesystem_dock.h"
#include "editor/gui/editor_run_bar.h"
+#include "editor/gui/scene_tree_editor.h"
#include "editor/inspector_dock.h"
#include "editor/plugins/node_3d_editor_plugin.h"
+#include "editor/property_selector.h"
#include "editor/themes/editor_scale.h"
#include "main/main.h"
#include "scene/gui/box_container.h"
@@ -263,6 +265,93 @@ void EditorInterface::set_current_feature_profile(const String &p_profile_name)
EditorFeatureProfileManager::get_singleton()->set_current_profile(p_profile_name, true);
}
+// Editor dialogs.
+
+void EditorInterface::popup_node_selector(const Callable &p_callback, const TypedArray &p_valid_types) {
+ // TODO: Should reuse dialog instance instead of creating a fresh one, but need to rework set_valid_types first.
+ if (node_selector) {
+ node_selector->disconnect(SNAME("selected"), callable_mp(this, &EditorInterface::_node_selected).bind(p_callback));
+ node_selector->disconnect(SNAME("canceled"), callable_mp(this, &EditorInterface::_node_selection_canceled).bind(p_callback));
+ get_base_control()->remove_child(node_selector);
+ node_selector->queue_free();
+ }
+ node_selector = memnew(SceneTreeDialog);
+
+ Vector valid_types;
+ int length = p_valid_types.size();
+ valid_types.resize(length);
+ for (int i = 0; i < length; i++) {
+ valid_types.write[i] = p_valid_types[i];
+ }
+ node_selector->set_valid_types(valid_types);
+
+ get_base_control()->add_child(node_selector);
+
+ node_selector->popup_scenetree_dialog();
+
+ const Callable selected_callback = callable_mp(this, &EditorInterface::_node_selected).bind(p_callback);
+ node_selector->connect(SNAME("selected"), selected_callback, CONNECT_DEFERRED);
+
+ const Callable canceled_callback = callable_mp(this, &EditorInterface::_node_selection_canceled).bind(p_callback);
+ node_selector->connect(SNAME("canceled"), canceled_callback, CONNECT_DEFERRED);
+}
+
+void EditorInterface::popup_property_selector(Object *p_object, const Callable &p_callback, const PackedInt32Array &p_type_filter) {
+ // TODO: Should reuse dialog instance instead of creating a fresh one, but need to rework set_type_filter first.
+ if (property_selector) {
+ property_selector->disconnect(SNAME("selected"), callable_mp(this, &EditorInterface::_property_selected).bind(p_callback));
+ property_selector->disconnect(SNAME("canceled"), callable_mp(this, &EditorInterface::_property_selection_canceled).bind(p_callback));
+ get_base_control()->remove_child(property_selector);
+ property_selector->queue_free();
+ }
+ property_selector = memnew(PropertySelector);
+
+ Vector type_filter;
+ int length = p_type_filter.size();
+ type_filter.resize(length);
+ for (int i = 0; i < length; i++) {
+ type_filter.write[i] = (Variant::Type)p_type_filter[i];
+ }
+ property_selector->set_type_filter(type_filter);
+
+ get_base_control()->add_child(property_selector);
+
+ property_selector->select_property_from_instance(p_object);
+
+ const Callable selected_callback = callable_mp(this, &EditorInterface::_property_selected).bind(p_callback);
+ property_selector->connect(SNAME("selected"), selected_callback, CONNECT_DEFERRED);
+
+ const Callable canceled_callback = callable_mp(this, &EditorInterface::_property_selection_canceled).bind(p_callback);
+ property_selector->connect(SNAME("canceled"), canceled_callback, CONNECT_DEFERRED);
+}
+
+void EditorInterface::_node_selected(const NodePath &p_node_path, const Callable &p_callback) {
+ const NodePath path = get_edited_scene_root()->get_path().rel_path_to(p_node_path);
+ _call_dialog_callback(p_callback, path, "node selected");
+}
+
+void EditorInterface::_node_selection_canceled(const Callable &p_callback) {
+ _call_dialog_callback(p_callback, NodePath(), "node selection canceled");
+}
+
+void EditorInterface::_property_selected(const String &p_property_name, const Callable &p_callback) {
+ _call_dialog_callback(p_callback, NodePath(p_property_name).get_as_property_path(), "property selected");
+}
+
+void EditorInterface::_property_selection_canceled(const Callable &p_callback) {
+ _call_dialog_callback(p_callback, NodePath(), "property selection canceled");
+}
+
+void EditorInterface::_call_dialog_callback(const Callable &p_callback, const Variant &p_selected, const String &p_context) {
+ Callable::CallError ce;
+ Variant ret;
+ const Variant *args[1] = { &p_selected };
+ p_callback.callp(args, 1, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat("Error calling %s callback: %s", p_context, Variant::get_callable_error_text(p_callback, args, 1, ce)));
+ }
+}
+
// Editor docks.
FileSystemDock *EditorInterface::get_file_system_dock() const {
@@ -458,6 +547,11 @@ void EditorInterface::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "distraction_free_mode"), "set_distraction_free_mode", "is_distraction_free_mode_enabled");
+ // Editor dialogs.
+
+ ClassDB::bind_method(D_METHOD("popup_node_selector", "callback", "valid_types"), &EditorInterface::popup_node_selector, DEFVAL(TypedArray()));
+ ClassDB::bind_method(D_METHOD("popup_property_selector", "object", "callback", "type_filter"), &EditorInterface::popup_property_selector, DEFVAL(PackedInt32Array()));
+
// Editor docks.
ClassDB::bind_method(D_METHOD("get_file_system_dock"), &EditorInterface::get_file_system_dock);
diff --git a/editor/editor_interface.h b/editor/editor_interface.h
index 73e89ae2f2..c77891c784 100644
--- a/editor/editor_interface.h
+++ b/editor/editor_interface.h
@@ -48,6 +48,8 @@ class EditorSettings;
class FileSystemDock;
class Mesh;
class Node;
+class PropertySelector;
+class SceneTreeDialog;
class ScriptEditor;
class SubViewport;
class Texture2D;
@@ -60,6 +62,17 @@ class EditorInterface : public Object {
static EditorInterface *singleton;
+ // Editor dialogs.
+
+ PropertySelector *property_selector = nullptr;
+ SceneTreeDialog *node_selector = nullptr;
+
+ void _node_selected(const NodePath &p_node_paths, const Callable &p_callback);
+ void _node_selection_canceled(const Callable &p_callback);
+ void _property_selected(const String &p_property_name, const Callable &p_callback);
+ void _property_selection_canceled(const Callable &p_callback);
+ void _call_dialog_callback(const Callable &p_callback, const Variant &p_selected, const String &p_context);
+
// Editor tools.
TypedArray _make_mesh_previews(const TypedArray &p_meshes, int p_preview_size);
@@ -110,6 +123,12 @@ public:
String get_current_feature_profile() const;
void set_current_feature_profile(const String &p_profile_name);
+ // Editor dialogs.
+
+ void popup_node_selector(const Callable &p_callback, const TypedArray &p_valid_types = TypedArray());
+ // Must use Vector because exposing Vector is not supported.
+ void popup_property_selector(Object *p_object, const Callable &p_callback, const PackedInt32Array &p_type_filter = PackedInt32Array());
+
// Editor docks.
FileSystemDock *get_file_system_dock() const;