From 96c469a1388a430456465b602ea49bf69ef782c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20J=2E=20Est=C3=A9banez?= Date: Wed, 7 Jun 2023 13:44:37 +0200 Subject: [PATCH] Let editor workaround a case of inconsistency in compound scenes --- doc/classes/EditorInterface.xml | 6 ++++++ editor/editor_interface.cpp | 7 +++++++ editor/editor_interface.h | 1 + editor/editor_node.cpp | 12 ++++++++++++ editor/editor_node.h | 1 + scene/resources/packed_scene.cpp | 32 +++++++++++++++++++++++++++++++- scene/resources/packed_scene.h | 12 ++++++++++++ 7 files changed, 70 insertions(+), 1 deletion(-) diff --git a/doc/classes/EditorInterface.xml b/doc/classes/EditorInterface.xml index 418548a95f8..effce40b12e 100644 --- a/doc/classes/EditorInterface.xml +++ b/doc/classes/EditorInterface.xml @@ -179,6 +179,12 @@ Returns mesh previews rendered at the given size as an [Array] of [Texture2D]s. + + + + Marks the current scene tab as unsaved. + + diff --git a/editor/editor_interface.cpp b/editor/editor_interface.cpp index 50833c3a098..d9d9dc01c0d 100644 --- a/editor/editor_interface.cpp +++ b/editor/editor_interface.cpp @@ -36,6 +36,7 @@ #include "editor/editor_resource_preview.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/filesystem_dock.h" #include "editor/gui/editor_run_bar.h" #include "editor/inspector_dock.h" @@ -332,6 +333,10 @@ void EditorInterface::save_scene_as(const String &p_scene, bool p_with_preview) EditorNode::get_singleton()->save_scene_to_path(p_scene, p_with_preview); } +void EditorInterface::mark_scene_as_unsaved() { + EditorUndoRedoManager::get_singleton()->set_history_as_unsaved(EditorNode::get_editor_data().get_current_edited_scene_history_id()); +} + // Scene playback. void EditorInterface::play_main_scene() { @@ -430,6 +435,8 @@ void EditorInterface::_bind_methods() { ClassDB::bind_method(D_METHOD("save_scene"), &EditorInterface::save_scene); ClassDB::bind_method(D_METHOD("save_scene_as", "path", "with_preview"), &EditorInterface::save_scene_as, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("mark_scene_as_unsaved"), &EditorInterface::mark_scene_as_unsaved); + // Scene playback. ClassDB::bind_method(D_METHOD("play_main_scene"), &EditorInterface::play_main_scene); diff --git a/editor/editor_interface.h b/editor/editor_interface.h index 95fa0dd64f1..f7e8cf8d4c6 100644 --- a/editor/editor_interface.h +++ b/editor/editor_interface.h @@ -129,6 +129,7 @@ public: Error save_scene(); void save_scene_as(const String &p_scene, bool p_with_preview = true); + void mark_scene_as_unsaved(); // Scene playback. diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index bc61661a83f..4795fca03d9 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -4103,6 +4103,13 @@ void EditorNode::add_io_error(const String &p_error) { EditorInterface::get_singleton()->popup_dialog_centered_ratio(singleton->load_error_dialog, 0.5); } +void EditorNode::add_io_warning(const String &p_warning) { + DEV_ASSERT(Thread::get_caller_id() == Thread::get_main_id()); + singleton->load_errors->add_image(singleton->gui_base->get_theme_icon(SNAME("Warning"), SNAME("EditorIcons"))); + singleton->load_errors->add_text(p_warning + "\n"); + EditorInterface::get_singleton()->popup_dialog_centered_ratio(singleton->load_error_dialog, 0.5); +} + 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; @@ -6806,6 +6813,11 @@ EditorNode::EditorNode() { ResourceLoader::set_error_notify_func(&EditorNode::add_io_error); ResourceLoader::set_dependency_error_notify_func(&EditorNode::_dependency_error_report); + SceneState::set_instantiation_warning_notify_func([](const String &p_warning) { + add_io_warning(p_warning); + callable_mp(EditorInterface::get_singleton(), &EditorInterface::mark_scene_as_unsaved).call_deferred(); + }); + { // Register importers at the beginning, so dialogs are created with the right extensions. Ref import_texture; diff --git a/editor/editor_node.h b/editor/editor_node.h index edcad7e4d1b..65f85a76c98 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -731,6 +731,7 @@ public: static bool has_unsaved_changes() { return singleton->unsaved_cache; } static void disambiguate_filenames(const Vector p_full_paths, Vector &r_filenames); static void add_io_error(const String &p_error); + static void add_io_warning(const String &p_warning); 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); diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 4d690bd3b1c..6708c781779 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -45,6 +45,10 @@ #define PACKED_SCENE_VERSION 3 +#ifdef TOOLS_ENABLED +SceneState::InstantiationWarningNotify SceneState::instantiation_warn_notify = nullptr; +#endif + bool SceneState::can_instantiate() const { return nodes.size() > 0; } @@ -380,7 +384,33 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { //if node was not part of instance, must set its name, parenthood and ownership if (i > 0) { if (parent) { - parent->_add_child_nocheck(node, snames[n.name]); + bool pending_add = true; +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + Node *existing = parent->_get_child_by_name(snames[n.name]); + if (existing) { + // There's already a node in the same parent with the same name. + // This means that somehow the node was added both to the scene being + // loaded and another one instantiated in the former, maybe because of + // manual editing, or a bug in scene saving, or a loophole in the workflow + // (with any of the bugs possibly already fixed). + // Bring consistency back by letting it be assigned a non-clashing name. + // This simple workaround at least avoids leaks and helps the user realize + // something awkward has happened. + if (instantiation_warn_notify) { + instantiation_warn_notify(vformat( + TTR("An incoming node's name clashes with %s already in the scene (presumably, from a more nested instance).\nThe less nested node will be renamed. Please fix and re-save the scene."), + ret_nodes[0]->get_path_to(existing))); + } + node->set_name(snames[n.name]); + parent->add_child(node, true); + pending_add = false; + } + } +#endif + if (pending_add) { + parent->_add_child_nocheck(node, snames[n.name]); + } if (n.index >= 0 && n.index < parent->get_child_count() - 1) { parent->move_child(node, n.index); } diff --git a/scene/resources/packed_scene.h b/scene/resources/packed_scene.h index 5c53ffdb456..29df382c1fe 100644 --- a/scene/resources/packed_scene.h +++ b/scene/resources/packed_scene.h @@ -102,6 +102,14 @@ class SceneState : public RefCounted { int _find_base_scene_node_remap_key(int p_idx) const; +#ifdef TOOLS_ENABLED +public: + typedef void (*InstantiationWarningNotify)(const String &p_warning); + +private: + static InstantiationWarningNotify instantiation_warn_notify; +#endif + protected: static void _bind_methods(); @@ -201,6 +209,10 @@ public: // Used when saving pointers (saves a path property instead). static String get_meta_pointer_property(const String &p_property); +#ifdef TOOLS_ENABLED + static void set_instantiation_warning_notify_func(InstantiationWarningNotify p_warn_notify) { instantiation_warn_notify = p_warn_notify; } +#endif + SceneState(); };