Fix quick open dialog recursive problem

This commit is contained in:
kobewi 2026-01-12 13:12:06 +01:00
parent 788beb36dc
commit 3b9c77b614
4 changed files with 81 additions and 69 deletions

View file

@ -5392,6 +5392,73 @@ void EditorNode::add_io_warning(const String &p_warning) {
}
}
bool EditorNode::find_recursive_resources(const Variant &p_variant, HashSet<Resource *> &r_resources_found) {
switch (p_variant.get_type()) {
case Variant::ARRAY: {
Array a = p_variant;
for (int i = 0; i < a.size(); i++) {
Variant v2 = a[i];
if (v2.get_type() != Variant::ARRAY && v2.get_type() != Variant::DICTIONARY && v2.get_type() != Variant::OBJECT) {
continue;
}
if (find_recursive_resources(v2, r_resources_found)) {
return true;
}
}
} break;
case Variant::DICTIONARY: {
Dictionary d = p_variant;
for (const KeyValue<Variant, Variant> &kv : d) {
const Variant &k = kv.key;
const Variant &v2 = kv.value;
if (k.get_type() == Variant::ARRAY || k.get_type() == Variant::DICTIONARY || k.get_type() == Variant::OBJECT) {
if (find_recursive_resources(k, r_resources_found)) {
return true;
}
}
if (v2.get_type() == Variant::ARRAY || v2.get_type() == Variant::DICTIONARY || v2.get_type() == Variant::OBJECT) {
if (find_recursive_resources(v2, r_resources_found)) {
return true;
}
}
}
} break;
case Variant::OBJECT: {
Ref<Resource> r = p_variant;
if (r.is_null()) {
return false;
}
if (r_resources_found.has(r.ptr())) {
return true;
}
r_resources_found.insert(r.ptr());
List<PropertyInfo> plist;
r->get_property_list(&plist);
for (const PropertyInfo &pinfo : plist) {
if (!(pinfo.usage & PROPERTY_USAGE_STORAGE)) {
continue;
}
if (pinfo.type != Variant::ARRAY && pinfo.type != Variant::DICTIONARY && pinfo.type != Variant::OBJECT) {
continue;
}
if (find_recursive_resources(r->get(pinfo.name), r_resources_found)) {
return true;
}
}
r_resources_found.erase(r.ptr());
} break;
default: {
}
}
return false;
}
bool EditorNode::_find_scene_in_use(Node *p_node, const String &p_path) const {
if (p_node->get_scene_file_path() == p_path) {
return true;

View file

@ -767,6 +767,7 @@ public:
static void disambiguate_filenames(const Vector<String> p_full_paths, Vector<String> &r_filenames);
static void add_io_error(const String &p_error);
static void add_io_warning(const String &p_warning);
static bool find_recursive_resources(const Variant &p_variant, HashSet<Resource *> &r_resources_found);
static void progress_add_task(const String &p_task, const String &p_label, int p_steps, bool p_can_cancel = false);
static bool progress_task_step(const String &p_task, const String &p_state, int p_step = -1, bool p_force_refresh = true);

View file

@ -38,6 +38,7 @@
#include "editor/editor_undo_redo_manager.h"
#include "editor/file_system/editor_file_system.h"
#include "editor/file_system/editor_paths.h"
#include "editor/gui/editor_toaster.h"
#include "editor/inspector/editor_resource_preview.h"
#include "editor/inspector/multi_node_edit.h"
#include "editor/settings/editor_settings.h"
@ -232,6 +233,16 @@ void EditorQuickOpenDialog::preview_property() {
Ref<Resource> loaded_resource = ResourceLoader::load(container->get_selected());
ERR_FAIL_COND_MSG(loaded_resource.is_null(), "Cannot load resource from path '" + container->get_selected() + "'.");
Resource *res = Object::cast_to<Resource>(property_object);
if (res) {
HashSet<Resource *> resources_found;
resources_found.insert(res);
if (EditorNode::find_recursive_resources(loaded_resource, resources_found)) {
EditorToaster::get_singleton()->popup_str(TTR("Recursion detected, quick preview failed."), EditorToaster::SEVERITY_ERROR);
loaded_resource = Ref<Resource>();
}
}
// MultiNodeEdit has adding to the undo/redo stack baked into its set function.
// As such, we have to specifically call a version of its setter that doesn't
// create undo/redo actions.

View file

@ -3339,82 +3339,15 @@ void EditorPropertyResource::_resource_selected(const Ref<Resource> &p_resource,
}
}
static bool _find_recursive_resources(const Variant &v, HashSet<Resource *> &resources_found) {
switch (v.get_type()) {
case Variant::ARRAY: {
Array a = v;
for (int i = 0; i < a.size(); i++) {
Variant v2 = a[i];
if (v2.get_type() != Variant::ARRAY && v2.get_type() != Variant::DICTIONARY && v2.get_type() != Variant::OBJECT) {
continue;
}
if (_find_recursive_resources(v2, resources_found)) {
return true;
}
}
} break;
case Variant::DICTIONARY: {
Dictionary d = v;
for (const KeyValue<Variant, Variant> &kv : d) {
const Variant &k = kv.key;
const Variant &v2 = kv.value;
if (k.get_type() == Variant::ARRAY || k.get_type() == Variant::DICTIONARY || k.get_type() == Variant::OBJECT) {
if (_find_recursive_resources(k, resources_found)) {
return true;
}
}
if (v2.get_type() == Variant::ARRAY || v2.get_type() == Variant::DICTIONARY || v2.get_type() == Variant::OBJECT) {
if (_find_recursive_resources(v2, resources_found)) {
return true;
}
}
}
} break;
case Variant::OBJECT: {
Ref<Resource> r = v;
if (r.is_null()) {
return false;
}
if (resources_found.has(r.ptr())) {
return true;
}
resources_found.insert(r.ptr());
List<PropertyInfo> plist;
r->get_property_list(&plist);
for (const PropertyInfo &pinfo : plist) {
if (!(pinfo.usage & PROPERTY_USAGE_STORAGE)) {
continue;
}
if (pinfo.type != Variant::ARRAY && pinfo.type != Variant::DICTIONARY && pinfo.type != Variant::OBJECT) {
continue;
}
if (_find_recursive_resources(r->get(pinfo.name), resources_found)) {
return true;
}
}
resources_found.erase(r.ptr());
} break;
default: {
}
}
return false;
}
void EditorPropertyResource::_resource_changed(const Ref<Resource> &p_resource) {
Resource *r = Object::cast_to<Resource>(get_edited_object());
if (r) {
// Check for recursive setting of resource
HashSet<Resource *> resources_found;
resources_found.insert(r);
bool found = _find_recursive_resources(p_resource, resources_found);
bool found = EditorNode::find_recursive_resources(p_resource, resources_found);
if (found) {
EditorNode::get_singleton()->show_warning(TTR("Recursion detected, unable to assign resource to property."));
callable_mp(EditorNode::get_singleton(), &EditorNode::show_warning).call_deferred(TTR("Recursion detected, unable to assign resource to property."), TTR("Warning!"));
emit_changed(get_edited_property(), Ref<Resource>());
update_property();
return;