diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml
index baa85b3701..bce4f8454e 100644
--- a/doc/classes/Control.xml
+++ b/doc/classes/Control.xml
@@ -1115,10 +1115,10 @@
The node's global position, relative to the world (usually to the [CanvasLayer]).
- Controls the direction on the horizontal axis in which the control should grow or shrink if its horizontal size is changed.
+ Controls the direction on the horizontal axis in which the control should grow if its horizontal minimum size is changed to be greater than its current size, as the control always has to be at least the minimum size.
- Controls the direction on the vertical axis in which the control should grow or shrink if its vertical size is changed.
+ Controls the direction on the vertical axis in which the control should grow if its vertical minimum size is changed to be greater than its current size, as the control always has to be at least the minimum size.
Controls layout direction and text writing direction. Right-to-left layouts are necessary for certain languages (e.g. Arabic and Hebrew). See also [method is_layout_rtl].
@@ -1568,13 +1568,13 @@
[b]Note:[/b] If the control has received [signal mouse_entered] but not [signal mouse_exited], changing the [member mouse_filter] to [constant MOUSE_FILTER_IGNORE] will cause [signal mouse_exited] to be emitted.
- The control will grow/shrink to the left or top if its size is changed to be larger/smaller than its current size on the respective axis.
+ The control will grow to the left or top to make up if its minimum size is changed to be greater than its current size on the respective axis.
- The control will grow/shrink to the right or bottom if its size is changed to be larger/smaller than its current size on the respective axis.
+ The control will grow to the right or bottom to make up if its minimum size is changed to be greater than its current size on the respective axis.
- The control will grow/shrink in both directions equally if its size is changed to be larger/smaller than its current size.
+ The control will grow in both directions equally to make up if its minimum size is changed to be greater than its current size.
Snaps one of the 4 anchor's sides to the origin of the node's [code]Rect[/code], in the top left. Use it with one of the [code]anchor_*[/code] member variables, like [member anchor_left]. To change all 4 anchors at once, use [method set_anchors_preset].
diff --git a/editor/debugger/editor_debugger_inspector.cpp b/editor/debugger/editor_debugger_inspector.cpp
index c73308213d..a497affca3 100644
--- a/editor/debugger/editor_debugger_inspector.cpp
+++ b/editor/debugger/editor_debugger_inspector.cpp
@@ -446,7 +446,13 @@ void EditorDebuggerInspector::add_stack_variable(const Array &p_array, int p_off
v = Object::cast_to(v)->get_object_id();
h = PROPERTY_HINT_OBJECT_ID;
hs = var.type_hint;
+
+ // Makes the call stack select the node in the remote tree. See https://github.com/godotengine/godot/issues/79477
+ if (n == "self") {
+ _object_selected(v);
+ }
}
+
String type;
switch (var.type) {
case 0:
diff --git a/editor/docks/import_dock.cpp b/editor/docks/import_dock.cpp
index ae86cd22ac..d641dfebe4 100644
--- a/editor/docks/import_dock.cpp
+++ b/editor/docks/import_dock.cpp
@@ -691,7 +691,7 @@ void ImportDock::_reimport() {
}
EditorFileSystem::get_singleton()->reimport_files(params->paths);
- EditorFileSystem::get_singleton()->emit_signal(SNAME("filesystem_changed")); //it changed, so force emitting the signal
+ EditorFileSystem::get_singleton()->filesystem_changed(); // It changed, so force emitting the signal.
_set_dirty(false);
}
diff --git a/editor/docks/scene_tree_dock.cpp b/editor/docks/scene_tree_dock.cpp
index d149f55b03..74432fb92f 100644
--- a/editor/docks/scene_tree_dock.cpp
+++ b/editor/docks/scene_tree_dock.cpp
@@ -1398,14 +1398,19 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
break;
}
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+
placeholder = !placeholder;
+ undo_redo->create_action(TTR("Toggle Load as Placeholder"));
+ undo_redo->add_undo_method(node, "set_scene_instance_load_placeholder", !placeholder);
+ undo_redo->add_do_method(node, "set_scene_instance_load_placeholder", placeholder);
+
if (placeholder) {
EditorNode::get_singleton()->get_edited_scene()->set_editable_instance(node, false);
}
- node->set_scene_instance_load_placeholder(placeholder);
- scene_tree->update_tree();
+ undo_redo->commit_action();
}
}
} break;
@@ -1955,6 +1960,21 @@ void SceneTreeDock::_node_renamed() {
_node_selected();
}
+void SceneTreeDock::_make_owners_map(Node *p_node, Dictionary &r_owners) {
+ r_owners[p_node] = p_node->get_owner();
+ for (Node *child : p_node->iterate_children()) {
+ _make_owners_map(child, r_owners);
+ }
+}
+
+void SceneTreeDock::_apply_owners_map(Node *p_node, const Dictionary &p_owners) {
+ Node *owner = Object::cast_to(p_owners[p_node]);
+ p_node->set_owner(owner);
+ for (Node *child : p_node->iterate_children()) {
+ _apply_owners_map(child, p_owners);
+ }
+}
+
void SceneTreeDock::_set_owners(Node *p_owner, const Array &p_nodes) {
for (int i = 0; i < p_nodes.size(); i++) {
Node *n = Object::cast_to(p_nodes[i]);
@@ -4498,7 +4518,7 @@ List SceneTreeDock::paste_nodes(bool p_paste_as_sibling) {
// When copying, all nodes that should have an owner assigned here were given nullptr as an owner
// and added to the node_clipboard_edited_scene_owned list.
if (d != dup && E2.key->get_owner() == nullptr) {
- if (node_clipboard_edited_scene_owned.find(const_cast(E2.key))) {
+ if (node_clipboard_edited_scene_owned.has(const_cast(E2.key))) {
ur->add_do_method(d, "set_owner", owner);
}
}
@@ -4536,14 +4556,20 @@ List SceneTreeDock::paste_nodes(bool p_paste_as_sibling) {
void SceneTreeDock::paste_node_as_replacement() {
List selected_node_list = editor_selection->get_top_selected_node_list();
+ Node *clipboard_node = node_clipboard.front()->get();
+
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
+ ur->create_action(TTR("Paste Node(s) as Replacement"), UndoRedo::MERGE_DISABLE, edited_scene);
+
for (Node *selected : selected_node_list) {
- Node *clipboard_node = node_clipboard.front()->get();
HashMap duplimap;
Node *new_node = clipboard_node->duplicate_from_editor(duplimap);
if (!new_node) {
continue;
}
- EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
+ ur->add_do_reference(new_node);
+ ur->add_undo_reference(selected);
+
String old_scene_file_path = selected->get_scene_file_path();
String new_scene_file_path = clipboard_node->get_scene_file_path();
@@ -4551,34 +4577,16 @@ void SceneTreeDock::paste_node_as_replacement() {
bool new_is_scene = !new_scene_file_path.is_empty();
LocalVector old_children;
+ Dictionary owners_map;
- ur->create_action(TTR("Paste Node(s) as Replacement"), UndoRedo::MERGE_ALL, selected);
+ int child_count = selected->get_child_count();
+ for (int i = 0; i < child_count; i++) {
+ Node *child = selected->get_child(0);
+ _make_owners_map(child, owners_map);
+ old_children.push_back(child);
- if (old_is_scene) {
- for (int i = 0; i < selected->get_child_count(); i++) {
- Node *child = selected->get_child(i);
- if (child->get_owner() == selected) {
- ur->add_do_method(selected, "remove_child", child);
-
- selected->remove_child(child);
- old_children.push_back(child);
- }
- }
- }
-
- ur->add_do_method(this, "replace_node", selected, new_node, false);
- ur->add_do_method(new_node, "set_scene_file_path", new_scene_file_path);
- ur->add_do_reference(new_node);
-
- if (new_is_scene) {
- for (int i = 0; i < new_node->get_child_count(); i++) {
- Node *child = new_node->get_child(i);
- if (child->get_owner() == new_node) {
- ur->add_undo_method(new_node, "add_child", child, true);
- ur->add_undo_method(child, "set_owner", new_node);
- ur->add_undo_reference(child);
- }
- }
+ ur->add_do_method(selected, "remove_child", child);
+ selected->remove_child(child);
}
const bool is_missing_node_selected = Object::cast_to(selected) != nullptr;
@@ -4592,9 +4600,6 @@ void SceneTreeDock::paste_node_as_replacement() {
selected_transform_3d = selected_node_3d->get_transform();
}
- _replace_node(selected, new_node, is_missing_node_selected);
- new_node->set_scene_file_path(new_scene_file_path);
-
if (selected_canvas_item) {
Node2D *new_node_2d = Object::cast_to(new_node);
Control *new_control = Object::cast_to(new_node);
@@ -4612,29 +4617,47 @@ void SceneTreeDock::paste_node_as_replacement() {
}
}
- if (new_is_scene) {
- for (int i = 0; i < new_node->get_child_count(); i++) {
- Node *child = new_node->get_child(i);
- if (child->get_owner() == new_node) {
- ur->add_undo_method(new_node, "remove_child", child);
+ for (Node *child : new_node->iterate_children()) {
+ ur->add_do_reference(child);
+ ur->add_do_method(new_node, "add_child", child);
+ ur->add_undo_method(new_node, "remove_child", child);
+ }
+
+ // Note that due to the new node possibly having child nodes, it's not possible to unify the operations to use only do methods.
+ _replace_node(selected, new_node, is_missing_node_selected);
+ ur->add_do_method(this, "replace_node", selected, new_node, false);
+ if (old_is_scene || new_is_scene) {
+ new_node->set_scene_file_path(new_scene_file_path);
+ ur->add_do_method(new_node, "set_scene_file_path", new_scene_file_path);
+ }
+
+ for (KeyValue &E2 : duplimap) {
+ Node *d = E2.value;
+ // When copying, all nodes that should have an owner assigned here were given nullptr as an owner
+ // and added to the node_clipboard_edited_scene_owned list.
+ if (d != new_node && E2.key->get_owner() == nullptr) {
+ if (node_clipboard_edited_scene_owned.has(const_cast(E2.key))) {
+ d->set_owner(edited_scene);
+ ur->add_do_method(d, "set_owner", edited_scene);
}
}
}
ur->add_undo_method(this, "replace_node", new_node, selected, false);
- ur->add_undo_method(selected, "set_scene_file_path", old_scene_file_path);
- ur->add_undo_reference(selected);
-
- if (old_is_scene) {
- for (Node *child : old_children) {
- ur->add_undo_method(selected, "add_child", child, true);
- ur->add_undo_method(child, "set_owner", selected);
- ur->add_undo_reference(child);
- }
+ if (old_is_scene || new_is_scene) {
+ ur->add_undo_method(selected, "set_scene_file_path", old_scene_file_path);
}
- ur->commit_action(false);
+ for (Node *child : old_children) {
+ ur->add_undo_method(selected, "add_child", child);
+ ur->add_undo_method(this, "_apply_owners_map", child, owners_map);
+ ur->add_undo_reference(child);
+ }
}
+ ur->commit_action(false);
+ // FIXME: This shouldn't be needed, but children of the pasted node do not appear immediately for some reason.
+ scene_tree->clear_cache();
+ scene_tree->update_tree();
}
List SceneTreeDock::get_node_clipboard() const {
@@ -4945,6 +4968,7 @@ void SceneTreeDock::_edit_subresource(int p_idx, const PopupMenu *p_from_menu) {
void SceneTreeDock::_bind_methods() {
ClassDB::bind_method(D_METHOD("_post_do_create"), &SceneTreeDock::_post_do_create);
+ ClassDB::bind_method(D_METHOD("_apply_owners_map"), &SceneTreeDock::_apply_owners_map);
ClassDB::bind_method(D_METHOD("_set_owners"), &SceneTreeDock::_set_owners);
ClassDB::bind_method(D_METHOD("_reparent_nodes_to_root"), &SceneTreeDock::_reparent_nodes_to_root);
ClassDB::bind_method(D_METHOD("_reparent_nodes_to_paths_with_transform_and_name"), &SceneTreeDock::_reparent_nodes_to_paths_with_transform_and_name);
diff --git a/editor/docks/scene_tree_dock.h b/editor/docks/scene_tree_dock.h
index d659b3763b..adc04d153f 100644
--- a/editor/docks/scene_tree_dock.h
+++ b/editor/docks/scene_tree_dock.h
@@ -195,6 +195,8 @@ class SceneTreeDock : public EditorDock {
void _node_reparent(NodePath p_path, bool p_keep_global_xform);
void _do_reparent(Node *p_new_parent, int p_position_in_parent, Vector p_nodes, bool p_keep_global_xform);
+ void _make_owners_map(Node *p_node, Dictionary &r_owners);
+ void _apply_owners_map(Node *p_node, const Dictionary &p_owners);
void _set_owners(Node *p_owner, const Array &p_nodes);
enum ReplaceOwnerMode {
diff --git a/editor/editor_interface.cpp b/editor/editor_interface.cpp
index c5191bdffe..dcb1c6151a 100644
--- a/editor/editor_interface.cpp
+++ b/editor/editor_interface.cpp
@@ -368,7 +368,7 @@ void EditorInterface::make_scene_preview(const String &p_path, Node *p_scene, in
}
EditorResourcePreview::get_singleton()->check_for_invalidation(p_path);
- EditorFileSystem::get_singleton()->emit_signal(SNAME("filesystem_changed"));
+ EditorFileSystem::get_singleton()->filesystem_changed();
}
void EditorInterface::add_root_node(Node *p_node) {
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 4e9971c429..5bc4649f86 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -4720,7 +4720,6 @@ void EditorNode::_set_current_scene_nocheck(int p_idx, bool p_ignore_state) {
Node *old_scene = get_editor_data().get_edited_scene_root();
- resource_count.clear();
editor_selection->clear();
SceneTreeDock::get_singleton()->clear_previous_node_selection();
editor_data.set_edited_scene(p_idx);
@@ -4752,6 +4751,7 @@ void EditorNode::_set_current_scene_nocheck(int p_idx, bool p_ignore_state) {
EditorUndoRedoManager::get_singleton()->clear_history(editor_data.get_scene_history_id(p_idx), false);
}
+ resource_count.clear();
SceneTreeDock::get_singleton()->get_tree_editor()->update_tree();
_update_title();
diff --git a/editor/file_system/editor_file_system.cpp b/editor/file_system/editor_file_system.cpp
index 9b3448d65e..1a9e609fb0 100644
--- a/editor/file_system/editor_file_system.cpp
+++ b/editor/file_system/editor_file_system.cpp
@@ -2552,16 +2552,16 @@ void EditorFileSystem::update_files(const Vector &p_script_paths) {
if (!is_scanning()) {
_process_update_pending();
}
- if (!filesystem_changed_queued) {
- filesystem_changed_queued = true;
+ if (!filesystem_changed_queued.is_set()) {
+ filesystem_changed_queued.set();
callable_mp(this, &EditorFileSystem::_notify_filesystem_changed).call_deferred();
}
}
}
void EditorFileSystem::_notify_filesystem_changed() {
- emit_signal("filesystem_changed");
- filesystem_changed_queued = false;
+ emit_signal(SNAME("filesystem_changed"));
+ filesystem_changed_queued.clear();
}
HashSet EditorFileSystem::get_valid_extensions() const {
@@ -2602,6 +2602,18 @@ void EditorFileSystem::register_global_class_script(const String &p_search_path,
}
}
+void EditorFileSystem::filesystem_changed() {
+ if (Thread::is_main_thread()) {
+ _notify_filesystem_changed();
+ return;
+ }
+ // If not already queued, queue a deferred call to notify about filesystem changes.
+ if (!filesystem_changed_queued.is_set()) {
+ filesystem_changed_queued.set();
+ callable_mp(this, &EditorFileSystem::_notify_filesystem_changed).call_deferred();
+ }
+}
+
Error EditorFileSystem::_reimport_group(const String &p_group_file, const Vector &p_files) {
String importer_name;
@@ -3720,7 +3732,7 @@ void EditorFileSystem::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_file_type", "path"), &EditorFileSystem::get_file_type);
ClassDB::bind_method(D_METHOD("reimport_files", "files"), &EditorFileSystem::reimport_files);
- ADD_SIGNAL(MethodInfo("filesystem_changed"));
+ ADD_SIGNAL(MethodInfo("filesystem_changed")); // May only be emitted on the main thread.
ADD_SIGNAL(MethodInfo("script_classes_updated"));
ADD_SIGNAL(MethodInfo("sources_changed", PropertyInfo(Variant::BOOL, "exist")));
ADD_SIGNAL(MethodInfo("resources_reimporting", PropertyInfo(Variant::PACKED_STRING_ARRAY, "resources")));
diff --git a/editor/file_system/editor_file_system.h b/editor/file_system/editor_file_system.h
index 42fd6c994a..e4924ef89b 100644
--- a/editor/file_system/editor_file_system.h
+++ b/editor/file_system/editor_file_system.h
@@ -182,7 +182,7 @@ class EditorFileSystem : public Node {
EditorFileSystemDirectory *new_filesystem = nullptr;
static ScannedDirectory *first_scan_root_dir;
- bool filesystem_changed_queued = false;
+ SafeFlag filesystem_changed_queued;
bool scanning = false;
bool importing = false;
bool first_scan = true;
@@ -392,6 +392,8 @@ public:
HashSet get_valid_extensions() const;
void register_global_class_script(const String &p_search_path, const String &p_target_path);
+ void filesystem_changed();
+
EditorFileSystemDirectory *get_filesystem_path(const String &p_path);
String get_file_type(const String &p_file) const;
EditorFileSystemDirectory *find_file(const String &p_file, int *r_index) const;
diff --git a/editor/gui/editor_quick_open_dialog.cpp b/editor/gui/editor_quick_open_dialog.cpp
index 24e8f7915d..c93a45b0bb 100644
--- a/editor/gui/editor_quick_open_dialog.cpp
+++ b/editor/gui/editor_quick_open_dialog.cpp
@@ -209,15 +209,14 @@ bool EditorQuickOpenDialog::_is_instant_preview_active() const {
}
void EditorQuickOpenDialog::selection_changed() {
- if (!_is_instant_preview_active()) {
- return;
- }
-
// This prevents the property from being changed the first time the Quick Open
// window is opened.
if (!initial_selection_performed) {
initial_selection_performed = true;
- } else {
+ return;
+ }
+
+ if (_is_instant_preview_active()) {
preview_property();
}
}
diff --git a/editor/import/3d/resource_importer_scene.cpp b/editor/import/3d/resource_importer_scene.cpp
index 814f52fa0b..c407bfb81f 100644
--- a/editor/import/3d/resource_importer_scene.cpp
+++ b/editor/import/3d/resource_importer_scene.cpp
@@ -1613,12 +1613,35 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap<
continue;
}
const String mat_id = mat->get_meta("import_id", mat->get_name());
- if (mat_id.is_empty() || !p_material_data.has(mat_id)) {
+ if (mat_id.is_empty()) {
continue;
}
- Dictionary matdata = p_material_data[mat_id];
+ // Start with per-material import settings, if any. This is populated by the
+ // Advanced Import Settings dialog, but may be empty when using only the Import dock.
+ Dictionary matdata;
+ if (p_material_data.has(mat_id)) {
+ matdata = p_material_data[mat_id];
+ }
+ String file_path;
+ // Read any existing "use_external" settings to get the enabled status and file path.
+ if (matdata.has("use_external/enabled")) {
+ const bool enabled = bool(matdata["use_external/enabled"]);
+ // If the user has explicitly overwrote this material to not be external, skip it.
+ if (!enabled) {
+ continue;
+ }
+ // Read any existing file path from this material's import settings, if present.
+ if (matdata.has("use_external/fallback_path")) {
+ file_path = matdata["use_external/fallback_path"];
+ } else if (matdata.has("use_external/path")) {
+ file_path = matdata["use_external/path"];
+ if (file_path.begins_with("uid://")) {
+ file_path = ResourceUID::get_singleton()->uid_to_path(file_path);
+ }
+ }
+ }
+ // For any material settings that are missing, fill them with default values.
{
- //fill node settings for this node with default values
List iopts;
get_internal_import_options(INTERNAL_IMPORT_CATEGORY_MATERIAL, &iopts);
for (const ImportOption &E : iopts) {
@@ -1631,18 +1654,21 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap<
post_importer_plugins.write[j]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_MATERIAL, p_root, p_node, mat, matdata);
}
if (extract_mat != 0) {
- const String ext = material_extension[p_options.has("materials/extract_format") ? (int)p_options["materials/extract_format"] : 0];
- const String path = spath.path_join(mat_id.validate_filename() + ext);
- const String uid_path = ResourceUID::path_to_uid(path);
-
- matdata["use_external/enabled"] = true;
- matdata["use_external/path"] = uid_path;
- matdata["use_external/fallback_path"] = path;
- if (!FileAccess::exists(path) || extract_mat == 2 /*overwrite*/) {
- ResourceSaver::save(mat, path);
+ // If no file path was specified, generate one based on the material name and the selected format.
+ if (file_path.is_empty()) {
+ const String ext = material_extension[p_options.has("materials/extract_format") ? (int)p_options["materials/extract_format"] : 0];
+ file_path = spath.path_join(mat_id.validate_filename() + ext);
+ const String uid_path = ResourceUID::path_to_uid(file_path);
+ matdata["use_external/enabled"] = true;
+ matdata["use_external/path"] = uid_path;
+ matdata["use_external/fallback_path"] = file_path;
+ }
+ // Write the file if it doesn't exist, or if the user has selected to overwrite existing files.
+ if (!FileAccess::exists(file_path) || extract_mat == 2 /*overwrite*/) {
+ ResourceSaver::save(mat, file_path);
}
- Ref external_mat = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_REPLACE);
+ Ref external_mat = ResourceLoader::load(file_path, "", ResourceFormatLoader::CACHE_MODE_REPLACE);
if (external_mat.is_valid()) {
m->set_surface_material(i, external_mat);
}
diff --git a/editor/inspector/editor_resource_picker.cpp b/editor/inspector/editor_resource_picker.cpp
index eb00ad23d3..904bc539fd 100644
--- a/editor/inspector/editor_resource_picker.cpp
+++ b/editor/inspector/editor_resource_picker.cpp
@@ -1771,7 +1771,7 @@ void EditorAudioStreamPicker::_preview_draw() {
float text_width = size.width - 4 * EDSCALE - icon->get_width();
if (text_width > 0) {
- stream_preview_rect->draw_string(font, Point2i(EDSCALE * 4 + icon->get_width(), rect.position.y + font->get_ascent(font_size) + (rect.size.height - font->get_height(font_size)) / 2), text, HORIZONTAL_ALIGNMENT_CENTER, text_width, font_size, get_theme_color(SceneStringName(font_color), EditorStringName(Editor)));
+ stream_preview_rect->draw_string(font, Point2i(size.width - text_width, rect.position.y + font->get_ascent(font_size) + (rect.size.height - font->get_height(font_size)) / 2), text, HORIZONTAL_ALIGNMENT_CENTER, text_width, font_size, get_theme_color(SceneStringName(font_color), EditorStringName(Editor)));
}
}
diff --git a/editor/run/game_view_plugin.cpp b/editor/run/game_view_plugin.cpp
index 9c137bdfd2..80b8af9bd7 100644
--- a/editor/run/game_view_plugin.cpp
+++ b/editor/run/game_view_plugin.cpp
@@ -46,6 +46,7 @@
#include "editor/run/editor_run_bar.h"
#include "editor/run/embedded_process.h"
#include "editor/run/run_instances_dialog.h"
+#include "editor/script/script_editor_plugin.h"
#include "editor/settings/editor_feature_profile.h"
#include "editor/settings/editor_settings.h"
#include "editor/themes/editor_scale.h"
@@ -1769,7 +1770,7 @@ void GameViewPluginBase::_save_last_editor(const String &p_editor) {
void GameViewPluginBase::_focus_another_editor() {
if (_is_window_wrapper_enabled()) {
- if (last_editor.is_empty()) {
+ if (last_editor.is_empty() || (last_editor == "Script" && ScriptEditor::get_singleton()->is_editor_floating())) {
EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_2D);
} else {
EditorInterface::get_singleton()->set_main_screen_editor(last_editor);
diff --git a/editor/scene/canvas_item_editor_plugin.cpp b/editor/scene/canvas_item_editor_plugin.cpp
index 75d3f81938..c939e3a142 100644
--- a/editor/scene/canvas_item_editor_plugin.cpp
+++ b/editor/scene/canvas_item_editor_plugin.cpp
@@ -6290,42 +6290,59 @@ bool CanvasItemEditorViewport::_cyclical_dependency_exists(const String &p_targe
return false;
}
+void CanvasItemEditorViewport::_add_node_to_scene(Node *p_parent, Node *p_child, const Vector2 &p_target_position) {
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ if (p_parent) {
+ undo_redo->add_do_method(p_parent, "add_child", p_child, true);
+ undo_redo->add_do_method(p_child, "set_owner", EditorNode::get_singleton()->get_edited_scene());
+ undo_redo->add_do_reference(p_child);
+ undo_redo->add_undo_method(p_parent, "remove_child", p_child);
+
+ const String new_name = p_parent->validate_child_name(p_child);
+ EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
+ undo_redo->add_do_method(ed, "live_debug_create_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent), p_child->get_class(), new_name);
+ undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent)) + "/" + new_name));
+ } else { // If no parent is selected, set as root node of the scene.
+ undo_redo->add_do_method(EditorNode::get_singleton(), "set_edited_scene", p_child);
+ undo_redo->add_do_reference(p_child);
+ undo_redo->add_undo_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr);
+ }
+ // There's nothing to be used as source position, so snapping will work as absolute if enabled.
+ Vector2 target_position = canvas_item_editor->snap_point(p_target_position);
+ CanvasItem *parent_ci = Object::cast_to(p_parent);
+ // Set position via undo_redo for proper live editing support.
+ undo_redo->add_do_method(p_child, "set_position", parent_ci ? parent_ci->get_global_transform().affine_inverse().xform(target_position) : target_position);
+
+ EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection();
+ undo_redo->add_do_method(editor_selection, "add_node", p_child);
+}
+
void CanvasItemEditorViewport::_create_texture_node(Node *p_parent, Node *p_child, const String &p_path, const Point2 &p_point) {
// Adjust casing according to project setting. The file name is expected to be in snake_case, but will work for others.
const String &node_name = Node::adjust_name_casing(p_path.get_file().get_basename());
if (!node_name.is_empty()) {
p_child->set_name(node_name);
}
+ // Compute the global position.
+ Transform2D xform = canvas_item_editor->get_canvas_transform();
+ Point2 target_position = xform.affine_inverse().xform(p_point);
- EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ // Adjust position for Control and TouchScreenButton.
Ref texture = ResourceCache::get_ref(p_path);
-
- if (p_parent) {
- undo_redo->add_do_method(p_parent, "add_child", p_child, true);
- undo_redo->add_do_method(p_child, "set_owner", EditorNode::get_singleton()->get_edited_scene());
- undo_redo->add_do_reference(p_child);
- undo_redo->add_undo_method(p_parent, "remove_child", p_child);
- } else { // If no parent is selected, set as root node of the scene.
- undo_redo->add_do_method(EditorNode::get_singleton(), "set_edited_scene", p_child);
- undo_redo->add_do_method(p_child, "set_owner", EditorNode::get_singleton()->get_edited_scene());
- undo_redo->add_do_reference(p_child);
- undo_redo->add_undo_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr);
- }
-
- if (p_parent) {
- String new_name = p_parent->validate_child_name(p_child);
- EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
- undo_redo->add_do_method(ed, "live_debug_create_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent), p_child->get_class(), new_name);
- undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent)) + "/" + new_name));
+ if (Object::cast_to(p_child) || Object::cast_to(p_child) || Object::cast_to(p_child)) {
+ target_position -= texture->get_size() / 2;
}
+ _add_node_to_scene(p_parent, p_child, target_position);
+ // Use undo_redo for properties to support live editing.
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (Object::cast_to(p_child) || Object::cast_to(p_child)) {
undo_redo->add_do_property(p_child, "texture_normal", texture);
} else {
undo_redo->add_do_property(p_child, "texture", texture);
}
- // make visible for certain node type
+ // Make visible for certain node type.
if (Object::cast_to(p_child)) {
Size2 texture_size = texture->get_size();
undo_redo->add_do_property(p_child, "size", texture_size);
@@ -6339,28 +6356,10 @@ void CanvasItemEditorViewport::_create_texture_node(Node *p_parent, Node *p_chil
};
undo_redo->add_do_property(p_child, "polygon", list);
}
-
- // Compute the global position
- Transform2D xform = canvas_item_editor->get_canvas_transform();
- Point2 target_position = xform.affine_inverse().xform(p_point);
-
- // Adjust position for Control and TouchScreenButton
- if (Object::cast_to(p_child) || Object::cast_to(p_child)) {
- target_position -= texture->get_size() / 2;
- }
-
- // There's nothing to be used as source position, so snapping will work as absolute if enabled.
- target_position = canvas_item_editor->snap_point(target_position);
-
- CanvasItem *parent_ci = Object::cast_to(p_parent);
- Point2 local_target_pos = parent_ci ? parent_ci->get_global_transform().affine_inverse().xform(target_position) : target_position;
-
- undo_redo->add_do_method(p_child, "set_position", local_target_pos);
}
void CanvasItemEditorViewport::_create_audio_node(Node *p_parent, const String &p_path, const Point2 &p_point) {
AudioStreamPlayer2D *child = memnew(AudioStreamPlayer2D);
- child->set_stream(ResourceCache::get_ref(p_path));
// Adjust casing according to project setting. The file name is expected to be in snake_case, but will work for others.
const String &node_name = Node::adjust_name_casing(p_path.get_file().get_basename());
@@ -6368,41 +6367,14 @@ void CanvasItemEditorViewport::_create_audio_node(Node *p_parent, const String &
child->set_name(node_name);
}
- EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
-
- if (p_parent) {
- undo_redo->add_do_method(p_parent, "add_child", child, true);
- undo_redo->add_do_method(child, "set_owner", EditorNode::get_singleton()->get_edited_scene());
- undo_redo->add_do_reference(child);
- undo_redo->add_undo_method(p_parent, "remove_child", child);
- } else { // If no parent is selected, set as root node of the scene.
- undo_redo->add_do_method(EditorNode::get_singleton(), "set_edited_scene", child);
- undo_redo->add_do_method(child, "set_owner", EditorNode::get_singleton()->get_edited_scene());
- undo_redo->add_do_reference(child);
- undo_redo->add_undo_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr);
- }
-
- if (p_parent) {
- String new_name = p_parent->validate_child_name(child);
- EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton();
- undo_redo->add_do_method(ed, "live_debug_create_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent), child->get_class(), new_name);
- undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent)) + "/" + new_name));
- }
-
// Compute the global position
Transform2D xform = canvas_item_editor->get_canvas_transform();
Point2 target_position = xform.affine_inverse().xform(p_point);
- // There's nothing to be used as source position, so snapping will work as absolute if enabled.
- target_position = canvas_item_editor->snap_point(target_position);
+ _add_node_to_scene(p_parent, child, target_position);
- CanvasItem *parent_ci = Object::cast_to(p_parent);
- Point2 local_target_pos = parent_ci ? parent_ci->get_global_transform().affine_inverse().xform(target_position) : target_position;
-
- undo_redo->add_do_method(child, "set_position", local_target_pos);
-
- EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection();
- undo_redo->add_do_method(editor_selection, "add_node", child);
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->add_do_property(child, "stream", ResourceCache::get_ref(p_path));
}
bool CanvasItemEditorViewport::_create_instance(Node *p_parent, const String &p_path, const Point2 &p_point) {
@@ -6512,7 +6484,6 @@ void CanvasItemEditorViewport::_perform_drop_data() {
if (texture.is_valid()) {
Node *child = Object::cast_to(ClassDB::instantiate(default_texture_node_type));
_create_texture_node(target_node, child, path, drop_pos);
- undo_redo->add_do_method(editor_selection, "add_node", child);
}
Ref audio = res;
@@ -6567,9 +6538,9 @@ bool CanvasItemEditorViewport::can_drop_data(const Point2 &p_point, const Varian
String error_message;
for (const String &path : files) {
- const String &res_type = ResourceLoader::get_resource_type(path);
+ const StringName res_type = ResourceLoader::get_resource_type(path);
- if (ClassDB::is_parent_class(res_type, "PackedScene")) {
+ if (ClassDB::is_parent_class(res_type, SNAME("PackedScene"))) {
Ref scn = ResourceLoader::load(path);
ERR_CONTINUE(scn.is_null());
@@ -6583,11 +6554,9 @@ bool CanvasItemEditorViewport::can_drop_data(const Point2 &p_point, const Varian
}
memdelete(instantiated_scene);
instantiate_type |= SCENE;
- }
- if (ClassDB::is_parent_class(res_type, "Texture2D")) {
+ } else if (ClassDB::is_parent_class(res_type, SNAME("Texture2D"))) {
instantiate_type |= TEXTURE;
- }
- if (ClassDB::is_parent_class(res_type, "AudioStream")) {
+ } else if (ClassDB::is_parent_class(res_type, SNAME("AudioStream"))) {
instantiate_type |= AUDIO;
}
}
diff --git a/editor/scene/canvas_item_editor_plugin.h b/editor/scene/canvas_item_editor_plugin.h
index 29ea7e5fc1..1982fda3a4 100644
--- a/editor/scene/canvas_item_editor_plugin.h
+++ b/editor/scene/canvas_item_editor_plugin.h
@@ -685,6 +685,7 @@ class CanvasItemEditorViewport : public Control {
bool _cyclical_dependency_exists(const String &p_target_scene_path, Node *p_desired_node) const;
bool _is_any_texture_selected() const;
+ void _add_node_to_scene(Node *p_parent, Node *p_child, const Vector2 &p_target_position);
void _create_texture_node(Node *p_parent, Node *p_child, const String &p_path, const Point2 &p_point);
void _create_audio_node(Node *p_parent, const String &p_path, const Point2 &p_point);
bool _create_instance(Node *p_parent, const String &p_path, const Point2 &p_point);
diff --git a/editor/scene/editor_scene_tabs.cpp b/editor/scene/editor_scene_tabs.cpp
index c4789eacda..3878784d85 100644
--- a/editor/scene/editor_scene_tabs.cpp
+++ b/editor/scene/editor_scene_tabs.cpp
@@ -78,7 +78,7 @@ void EditorSceneTabs::_notification(int p_what) {
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
case NOTIFICATION_TRANSLATION_CHANGED: {
- _scene_tabs_resized();
+ _update_tab_titles();
} break;
}
}
diff --git a/editor/settings/editor_autoload_settings.cpp b/editor/settings/editor_autoload_settings.cpp
index 2f6d6ab187..20c5bfd9cd 100644
--- a/editor/settings/editor_autoload_settings.cpp
+++ b/editor/settings/editor_autoload_settings.cpp
@@ -502,7 +502,7 @@ void EditorAutoloadSettings::update_autoload() {
if (old_info.path == info.path) {
// Still the same resource, check status
info.node = old_info.node;
- if (info.node) {
+ if (info.node && info.node->is_instance()) {
Ref