diff --git a/editor/gui/scene_tree_editor.cpp b/editor/gui/scene_tree_editor.cpp index a06dd310ea9..fa0dad41dc1 100644 --- a/editor/gui/scene_tree_editor.cpp +++ b/editor/gui/scene_tree_editor.cpp @@ -1368,23 +1368,36 @@ bool SceneTreeEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_d } bool scene_drop = true; + bool audio_drop = true; for (int i = 0; i < files.size(); i++) { String ftype = EditorFileSystem::get_singleton()->get_file_type(files[i]); if (ftype != "PackedScene") { scene_drop = false; - break; + } + if (audio_drop && !ClassDB::is_parent_class(ftype, "AudioStream")) { + audio_drop = false; } } if (scene_drop) { tree->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN | Tree::DROP_MODE_ON_ITEM); - } else { - if (files.size() > 1) { - return false; - } - tree->set_drop_mode_flags(Tree::DROP_MODE_ON_ITEM); + return true; } + if (audio_drop) { + if (files.size() > 1) { + tree->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN); + } else { + tree->set_drop_mode_flags(Tree::DROP_MODE_INBETWEEN | Tree::DROP_MODE_ON_ITEM); + } + return true; + } + + if (files.size() > 1) { + return false; + } + tree->set_drop_mode_flags(Tree::DROP_MODE_ON_ITEM); + return true; } diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index a44430ca7f0..2d478870278 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -46,10 +46,12 @@ #include "editor/scene_tree_dock.h" #include "editor/themes/editor_scale.h" #include "editor/themes/editor_theme_manager.h" +#include "scene/2d/audio_stream_player_2d.h" #include "scene/2d/polygon_2d.h" #include "scene/2d/skeleton_2d.h" #include "scene/2d/sprite_2d.h" #include "scene/2d/touch_screen_button.h" +#include "scene/gui/base_button.h" #include "scene/gui/flow_container.h" #include "scene/gui/grid_container.h" #include "scene/gui/separator.h" @@ -966,7 +968,7 @@ void CanvasItemEditor::_add_node_pressed(int p_result) { } } -void CanvasItemEditor::_node_created(Node *p_node) { +void CanvasItemEditor::_adjust_new_node_position(Node *p_node) { if (node_create_position == Point2()) { return; } @@ -5161,7 +5163,7 @@ CanvasItemEditor::CanvasItemEditor() { editor_selection->connect("selection_changed", callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw)); editor_selection->connect("selection_changed", callable_mp(this, &CanvasItemEditor::_selection_changed)); - SceneTreeDock::get_singleton()->connect("node_created", callable_mp(this, &CanvasItemEditor::_node_created)); + SceneTreeDock::get_singleton()->connect("node_created", callable_mp(this, &CanvasItemEditor::_adjust_new_node_position)); SceneTreeDock::get_singleton()->connect("add_node_used", callable_mp(this, &CanvasItemEditor::_reset_create_position)); // Add some margin to the sides for better esthetics. @@ -5697,15 +5699,15 @@ CanvasItemEditorPlugin::~CanvasItemEditorPlugin() { } void CanvasItemEditorViewport::_on_mouse_exit() { - if (!selector->is_visible()) { + if (!texture_node_type_selector->is_visible()) { _remove_preview(); } } -void CanvasItemEditorViewport::_on_select_type(Object *selected) { +void CanvasItemEditorViewport::_on_select_texture_node_type(Object *selected) { CheckBox *check = Object::cast_to(selected); String type = check->get_text(); - selector->set_title(vformat(TTR("Add %s"), type)); + texture_node_type_selector->set_title(vformat(TTR("Add %s"), type)); label->set_text(vformat(TTR("Adding %s..."), type)); } @@ -5717,7 +5719,7 @@ void CanvasItemEditorViewport::_on_change_type_confirmed() { CheckBox *check = Object::cast_to(button_group->get_pressed_button()); default_texture_node_type = check->get_text(); _perform_drop_data(); - selector->hide(); + texture_node_type_selector->hide(); } void CanvasItemEditorViewport::_on_change_type_closed() { @@ -5728,35 +5730,35 @@ void CanvasItemEditorViewport::_create_preview(const Vector &files) cons bool add_preview = false; for (int i = 0; i < files.size(); i++) { Ref res = ResourceLoader::load(files[i]); - ERR_FAIL_COND(res.is_null()); - Ref texture = Ref(Object::cast_to(*res)); - Ref scene = Ref(Object::cast_to(*res)); - if (texture != nullptr || scene != nullptr) { - String desc = TTR("Drag and drop to add as sibling of selected node (except when root is selected).") + - "\n" + TTR("Hold Shift when dropping to add as child of selected node.") + - "\n" + TTR("Hold Alt when dropping to add as child of root node."); + ERR_CONTINUE(res.is_null()); - if (texture != nullptr) { - Sprite2D *sprite = memnew(Sprite2D); - sprite->set_texture(texture); - sprite->set_modulate(Color(1, 1, 1, 0.7f)); - preview_node->add_child(sprite); - label->show(); - label_desc->show(); - desc += "\n" + TTR("Hold Alt + Shift when dropping to add as a different node type."); - label_desc->set_text(desc); - } else { - if (scene.is_valid()) { - Node *instance = scene->instantiate(); - if (instance) { - preview_node->add_child(instance); - label_desc->show(); - label_desc->set_text(desc); - } - } + Ref texture = res; + if (texture.is_valid()) { + Sprite2D *sprite = memnew(Sprite2D); + sprite->set_texture(texture); + sprite->set_modulate(Color(1, 1, 1, 0.7f)); + preview_node->add_child(sprite); + add_preview = true; + } + + Ref scene = res; + if (scene.is_valid()) { + Node *instance = scene->instantiate(); + if (instance) { + preview_node->add_child(instance); } add_preview = true; } + + Ref audio = res; + if (audio.is_valid()) { + Sprite2D *sprite = memnew(Sprite2D); + sprite->set_texture(get_editor_theme_icon(SNAME("AudioStreamPlayer2D"))); + sprite->set_modulate(Color(1, 1, 1, 0.7f)); + sprite->set_position(Vector2(0, -sprite->get_texture()->get_size().height) * EDSCALE); + preview_node->add_child(sprite); + add_preview = true; + } } if (add_preview) { @@ -5797,44 +5799,46 @@ bool CanvasItemEditorViewport::_cyclical_dependency_exists(const String &p_targe return false; } -void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String &path, const Point2 &p_point) { +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. - String name = path.get_file().get_basename(); - child->set_name(Node::adjust_name_casing(name)); + 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); + } EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - Ref texture = ResourceCache::get_ref(path); + Ref texture = ResourceCache::get_ref(p_path); - if (parent) { - undo_redo->add_do_method(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(parent, "remove_child", child); + 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", child); - undo_redo->add_do_method(child, "set_owner", EditorNode::get_singleton()->get_edited_scene()); - undo_redo->add_do_reference(child); + 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 (parent) { - String new_name = parent->validate_child_name(child); + 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(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(parent)) + "/" + new_name)); + 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(child) || Object::cast_to(child)) { - undo_redo->add_do_property(child, "texture_normal", texture); + 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(child, "texture", texture); + undo_redo->add_do_property(p_child, "texture", texture); } // make visible for certain node type - if (Object::cast_to(child)) { + if (Object::cast_to(p_child)) { Size2 texture_size = texture->get_size(); - undo_redo->add_do_property(child, "size", texture_size); - } else if (Object::cast_to(child)) { + undo_redo->add_do_property(p_child, "size", texture_size); + } else if (Object::cast_to(p_child)) { Size2 texture_size = texture->get_size(); Vector list = { Vector2(0, 0), @@ -5842,7 +5846,7 @@ void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String & Vector2(texture_size.width, texture_size.height), Vector2(0, texture_size.height) }; - undo_redo->add_do_property(child, "polygon", list); + undo_redo->add_do_property(p_child, "polygon", list); } // Compute the global position @@ -5850,21 +5854,68 @@ void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String & Point2 target_position = xform.affine_inverse().xform(p_point); // Adjust position for Control and TouchScreenButton - if (Object::cast_to(child) || Object::cast_to(child)) { + 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 + // 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(parent); + 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()); + if (!node_name.is_empty()) { + 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); + + 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); } -bool CanvasItemEditorViewport::_create_instance(Node *parent, String &path, const Point2 &p_point) { - Ref sdata = ResourceLoader::load(path); +bool CanvasItemEditorViewport::_create_instance(Node *p_parent, const String &p_path, const Point2 &p_point) { + Ref sdata = ResourceLoader::load(p_path); if (!sdata.is_valid()) { // invalid scene return false; } @@ -5883,27 +5934,27 @@ bool CanvasItemEditorViewport::_create_instance(Node *parent, String &path, cons } } - instantiated_scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(path)); + instantiated_scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(p_path)); EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection(); - undo_redo->add_do_method(parent, "add_child", instantiated_scene, true); + undo_redo->add_do_method(p_parent, "add_child", instantiated_scene, true); undo_redo->add_do_method(instantiated_scene, "set_owner", edited_scene); undo_redo->add_do_reference(instantiated_scene); - undo_redo->add_undo_method(parent, "remove_child", instantiated_scene); + undo_redo->add_undo_method(p_parent, "remove_child", instantiated_scene); undo_redo->add_do_method(editor_selection, "add_node", instantiated_scene); - String new_name = parent->validate_child_name(instantiated_scene); + String new_name = p_parent->validate_child_name(instantiated_scene); EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); - undo_redo->add_do_method(ed, "live_debug_instantiate_node", edited_scene->get_path_to(parent), path, new_name); - undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)) + "/" + new_name)); + undo_redo->add_do_method(ed, "live_debug_instantiate_node", edited_scene->get_path_to(p_parent), p_path, new_name); + undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(p_parent)) + "/" + new_name)); CanvasItem *instance_ci = Object::cast_to(instantiated_scene); if (instance_ci) { Vector2 target_pos = canvas_item_editor->get_canvas_transform().affine_inverse().xform(p_point); target_pos = canvas_item_editor->snap_point(target_pos); - CanvasItem *parent_ci = Object::cast_to(parent); + CanvasItem *parent_ci = Object::cast_to(p_parent); if (parent_ci) { target_pos = parent_ci->get_global_transform_with_canvas().affine_inverse().xform(target_pos); } @@ -5922,12 +5973,8 @@ void CanvasItemEditorViewport::_perform_drop_data() { _remove_preview(); if (!target_node) { - // Without root dropping multiple files is not allowed - if (selected_files.size() > 1) { - accept->set_text(TTR("Cannot instantiate multiple nodes without root.")); - accept->popup_centered(); - return; - } + // Should already be handled by `can_drop_data`. + ERR_FAIL_COND_MSG(selected_files.size() > 1, "Can't instantiate multiple nodes without root."); const String &path = selected_files[0]; Ref res = ResourceLoader::load(path); @@ -5973,9 +6020,14 @@ void CanvasItemEditorViewport::_perform_drop_data() { Ref texture = res; if (texture.is_valid()) { Node *child = Object::cast_to(ClassDB::instantiate(default_texture_node_type)); - _create_nodes(target_node, child, path, drop_pos); + _create_texture_node(target_node, child, path, drop_pos); undo_redo->add_do_method(editor_selection, "add_node", child); } + + Ref audio = res; + if (audio.is_valid()) { + _create_audio_node(target_node, path, drop_pos); + } } undo_redo->commit_action(); @@ -5994,71 +6046,108 @@ bool CanvasItemEditorViewport::can_drop_data(const Point2 &p_point, const Varian } Vector files = d["files"]; - bool can_instantiate = false; - bool is_cyclical_dep = false; - String error_file; - // Check if at least one of the dragged files is a texture or scene. - for (int i = 0; i < files.size(); i++) { - bool is_scene = ClassDB::is_parent_class(ResourceLoader::get_resource_type(files[i]), "PackedScene"); - bool is_texture = ClassDB::is_parent_class(ResourceLoader::get_resource_type(files[i]), "Texture2D"); - - if (is_scene || is_texture) { - Ref res = ResourceLoader::load(files[i]); - if (res.is_null()) { - continue; - } - Ref scn = res; - if (scn.is_valid()) { - Node *instantiated_scene = scn->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE); - if (!instantiated_scene) { - continue; - } - - Node *edited_scene = EditorNode::get_singleton()->get_edited_scene(); - if (edited_scene && !edited_scene->get_scene_file_path().is_empty() && _cyclical_dependency_exists(edited_scene->get_scene_file_path(), instantiated_scene)) { - memdelete(instantiated_scene); - can_instantiate = false; - is_cyclical_dep = true; - error_file = files[i].get_file(); - break; - } - memdelete(instantiated_scene); - } - can_instantiate = true; - } - } - if (is_cyclical_dep) { - canvas_item_editor->message = vformat(TTR("Can't instantiate: %s."), vformat(TTR("Circular dependency found at %s"), error_file)); + const Node *edited_scene = EditorNode::get_singleton()->get_edited_scene(); + if (!edited_scene && files.size() > 1) { + canvas_item_editor->message = TTR("Can't instantiate multiple nodes without root."); canvas_item_editor->update_viewport(); return false; } - if (can_instantiate) { - if (!preview_node->get_parent()) { // create preview only once - _create_preview(files); - } - ERR_FAIL_COND_V(preview_node->get_child_count() == 0, false); - Transform2D trans = canvas_item_editor->get_canvas_transform(); - preview_node->set_position((p_point - trans.get_origin()) / trans.get_scale().x); - String scene_file_path = preview_node->get_child(0)->get_scene_file_path(); - if (scene_file_path.is_empty() || preview_node->get_tree()->get_edited_scene_root()) { - double snap = EDITOR_GET("interface/inspector/default_float_step"); - int snap_step_decimals = Math::range_step_decimals(snap); -#define FORMAT(value) (TS->format_number(String::num(value, snap_step_decimals))) - Vector2 preview_node_pos = preview_node->get_global_position(); - canvas_item_editor->message = TTR("Instantiating:") + " (" + FORMAT(preview_node_pos.x) + ", " + FORMAT(preview_node_pos.y) + ") px"; - label->set_text(vformat(TTR("Adding %s..."), default_texture_node_type)); - } else { - canvas_item_editor->message = TTR("Creating inherited scene from: ") + scene_file_path; + enum { + SCENE = 1 << 0, + TEXTURE = 1 << 1, + AUDIO = 1 << 2, + }; + int instantiate_type = 0; + + for (const String &path : files) { + const String &res_type = ResourceLoader::get_resource_type(path); + String error_message; + + if (ClassDB::is_parent_class(res_type, "PackedScene")) { + Ref scn = ResourceLoader::load(path); + ERR_CONTINUE(scn.is_null()); + + Node *instantiated_scene = scn->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE); + if (!instantiated_scene) { + continue; + } + if (edited_scene && !edited_scene->get_scene_file_path().is_empty() && _cyclical_dependency_exists(edited_scene->get_scene_file_path(), instantiated_scene)) { + error_message = vformat(TTR("Circular dependency found at %s."), path.get_file()); + } + memdelete(instantiated_scene); + instantiate_type |= SCENE; + } + if (ClassDB::is_parent_class(res_type, "Texture2D")) { + instantiate_type |= TEXTURE; + } + if (ClassDB::is_parent_class(res_type, "AudioStream")) { + instantiate_type |= AUDIO; } - canvas_item_editor->update_viewport(); + if (!error_message.is_empty()) { + // TRANSLATORS: The placeholder is the error message. + canvas_item_editor->message = vformat(TTR("Can't instantiate: %s"), error_message); + canvas_item_editor->update_viewport(); + return false; + } } - return can_instantiate; + if (instantiate_type == 0) { + return false; + } + + if (!preview_node->get_parent()) { // create preview only once + _create_preview(files); + } + ERR_FAIL_COND_V(preview_node->get_child_count() == 0, false); + + const Transform2D trans = canvas_item_editor->get_canvas_transform(); + preview_node->set_position((p_point - trans.get_origin()) / trans.get_scale().x); + + if (!edited_scene && instantiate_type & SCENE) { + String scene_file_path = preview_node->get_child(0)->get_scene_file_path(); + // TRANSLATORS: The placeholder is the file path of the scene being instantiated. + canvas_item_editor->message = vformat(TTR("Creating inherited scene from: %s"), scene_file_path); + } else { + double snap = EDITOR_GET("interface/inspector/default_float_step"); + int snap_step_decimals = Math::range_step_decimals(snap); +#define FORMAT(value) (TS->format_number(String::num(value, snap_step_decimals))) + Vector2 preview_node_pos = preview_node->get_global_position(); + canvas_item_editor->message = TTR("Instantiating: ") + "(" + FORMAT(preview_node_pos.x) + ", " + FORMAT(preview_node_pos.y) + ") px"; + } + canvas_item_editor->update_viewport(); + + if (instantiate_type & TEXTURE && instantiate_type & AUDIO) { + // TRANSLATORS: The placeholders are the types of nodes being instantiated. + label->set_text(vformat(TTR("Adding %s and %s..."), default_texture_node_type, "AudioStreamPlayer2D")); + } else { + String node_type; + if (instantiate_type & TEXTURE) { + node_type = default_texture_node_type; + } else if (instantiate_type & AUDIO) { + node_type = "AudioStreamPlayer2D"; + } + if (!node_type.is_empty()) { + // TRANSLATORS: The placeholder is the type of node being instantiated. + label->set_text(vformat(TTR("Adding %s..."), node_type)); + } + } + label->set_visible(instantiate_type & ~SCENE); + + String desc = TTR("Drag and drop to add as sibling of selected node (except when root is selected).") + + "\n" + TTR("Hold Shift when dropping to add as child of selected node.") + + "\n" + TTR("Hold Alt when dropping to add as child of root node."); + if (instantiate_type & TEXTURE) { + desc += "\n" + TTR("Hold Alt + Shift when dropping to add as different node type."); + } + label_desc->set_text(desc); + label_desc->show(); + + return true; } -void CanvasItemEditorViewport::_show_resource_type_selector() { +void CanvasItemEditorViewport::_show_texture_node_type_selector() { _remove_preview(); List btn_list; button_group->get_buttons(&btn_list); @@ -6067,18 +6156,17 @@ void CanvasItemEditorViewport::_show_resource_type_selector() { CheckBox *check = Object::cast_to(btn); check->set_pressed(check->get_text() == default_texture_node_type); } - selector->set_title(vformat(TTR("Add %s"), default_texture_node_type)); - selector->popup_centered(); + texture_node_type_selector->set_title(vformat(TTR("Add %s"), default_texture_node_type)); + texture_node_type_selector->popup_centered(); } -bool CanvasItemEditorViewport::_only_packed_scenes_selected() const { +bool CanvasItemEditorViewport::_is_any_texture_selected() const { for (int i = 0; i < selected_files.size(); ++i) { - if (ResourceLoader::load(selected_files[i])->get_class() != "PackedScene") { - return false; + if (ClassDB::is_parent_class(ResourceLoader::get_resource_type(selected_files[i]), "Texture2D")) { + return true; } } - - return true; + return false; } void CanvasItemEditorViewport::drop_data(const Point2 &p_point, const Variant &p_data) { @@ -6115,8 +6203,8 @@ void CanvasItemEditorViewport::drop_data(const Point2 &p_point, const Variant &p drop_pos = p_point; - if (is_alt && is_shift && !_only_packed_scenes_selected()) { - _show_resource_type_selector(); + if (is_alt && is_shift && _is_any_texture_selected()) { + _show_texture_node_type_selector(); } else { _perform_drop_data(); } @@ -6176,19 +6264,19 @@ CanvasItemEditorViewport::CanvasItemEditorViewport(CanvasItemEditor *p_canvas_it accept = memnew(AcceptDialog); EditorNode::get_singleton()->get_gui_base()->add_child(accept); - selector = memnew(AcceptDialog); - EditorNode::get_singleton()->get_gui_base()->add_child(selector); - selector->set_title(TTR("Change Default Type")); - selector->connect("confirmed", callable_mp(this, &CanvasItemEditorViewport::_on_change_type_confirmed)); - selector->connect("canceled", callable_mp(this, &CanvasItemEditorViewport::_on_change_type_closed)); + texture_node_type_selector = memnew(AcceptDialog); + EditorNode::get_singleton()->get_gui_base()->add_child(texture_node_type_selector); + texture_node_type_selector->set_title(TTR("Change Default Type")); + texture_node_type_selector->connect("confirmed", callable_mp(this, &CanvasItemEditorViewport::_on_change_type_confirmed)); + texture_node_type_selector->connect("canceled", callable_mp(this, &CanvasItemEditorViewport::_on_change_type_closed)); VBoxContainer *vbc = memnew(VBoxContainer); - selector->add_child(vbc); + texture_node_type_selector->add_child(vbc); vbc->set_h_size_flags(Control::SIZE_EXPAND_FILL); vbc->set_v_size_flags(Control::SIZE_EXPAND_FILL); vbc->set_custom_minimum_size(Size2(240, 260) * EDSCALE); - btn_group = memnew(VBoxContainer); + VBoxContainer *btn_group = memnew(VBoxContainer); vbc->add_child(btn_group); btn_group->set_h_size_flags(SIZE_EXPAND_FILL); @@ -6197,7 +6285,7 @@ CanvasItemEditorViewport::CanvasItemEditorViewport(CanvasItemEditor *p_canvas_it CheckBox *check = memnew(CheckBox); btn_group->add_child(check); check->set_text(texture_node_types[i]); - check->connect("button_down", callable_mp(this, &CanvasItemEditorViewport::_on_select_type).bind(check)); + check->connect("button_down", callable_mp(this, &CanvasItemEditorViewport::_on_select_texture_node_type).bind(check)); check->set_button_group(button_group); } diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index 6d951a77ec0..a9de5e9a0b1 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -32,10 +32,11 @@ #define CANVAS_ITEM_EDITOR_PLUGIN_H #include "editor/plugins/editor_plugin.h" -#include "scene/gui/base_button.h" #include "scene/gui/box_container.h" class AcceptDialog; +class Button; +class ButtonGroup; class CanvasItemEditorViewport; class ConfirmationDialog; class EditorData; @@ -409,7 +410,7 @@ private: void _selection_result_pressed(int); void _selection_menu_hide(); void _add_node_pressed(int p_result); - void _node_created(Node *p_node); + void _adjust_new_node_position(Node *p_node); void _reset_create_position(); void _update_editor_settings(); bool _is_grid_visible() const; @@ -634,15 +635,13 @@ class CanvasItemEditorViewport : public Control { CanvasItemEditor *canvas_item_editor = nullptr; Control *preview_node = nullptr; AcceptDialog *accept = nullptr; - AcceptDialog *selector = nullptr; - Label *selector_label = nullptr; + AcceptDialog *texture_node_type_selector = nullptr; Label *label = nullptr; Label *label_desc = nullptr; - VBoxContainer *btn_group = nullptr; Ref button_group; void _on_mouse_exit(); - void _on_select_type(Object *selected); + void _on_select_texture_node_type(Object *selected); void _on_change_type_confirmed(); void _on_change_type_closed(); @@ -650,11 +649,12 @@ class CanvasItemEditorViewport : public Control { void _remove_preview(); bool _cyclical_dependency_exists(const String &p_target_scene_path, Node *p_desired_node) const; - bool _only_packed_scenes_selected() const; - void _create_nodes(Node *parent, Node *child, String &path, const Point2 &p_point); - bool _create_instance(Node *parent, String &path, const Point2 &p_point); + bool _is_any_texture_selected() const; + 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); void _perform_drop_data(); - void _show_resource_type_selector(); + void _show_texture_node_type_selector(); void _update_theme(); protected: diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index ca7ea821e87..c8b229c62a3 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -78,12 +78,14 @@ #include "editor/plugins/gizmos/voxel_gi_gizmo_plugin.h" #include "editor/plugins/node_3d_editor_gizmos.h" #include "editor/scene_tree_dock.h" +#include "scene/3d/audio_stream_player_3d.h" #include "scene/3d/camera_3d.h" #include "scene/3d/decal.h" #include "scene/3d/light_3d.h" #include "scene/3d/mesh_instance_3d.h" #include "scene/3d/physics/collision_shape_3d.h" #include "scene/3d/physics/physics_body_3d.h" +#include "scene/3d/sprite_3d.h" #include "scene/3d/visual_instance_3d.h" #include "scene/3d/world_environment.h" #include "scene/gui/center_container.h" @@ -4192,27 +4194,37 @@ Node *Node3DEditorViewport::_sanitize_preview_node(Node *p_node) const { void Node3DEditorViewport::_create_preview_node(const Vector &files) const { bool add_preview = false; - for (int i = 0; i < files.size(); i++) { - Ref res = ResourceLoader::load(files[i]); + for (const String &path : files) { + Ref res = ResourceLoader::load(path); ERR_CONTINUE(res.is_null()); - Ref scene = Ref(Object::cast_to(*res)); - Ref mesh = Ref(Object::cast_to(*res)); - if (mesh != nullptr || scene != nullptr) { - if (mesh != nullptr) { - MeshInstance3D *mesh_instance = memnew(MeshInstance3D); - mesh_instance->set_mesh(mesh); - preview_node->add_child(mesh_instance); - } else { - if (scene.is_valid()) { - Node *instance = scene->instantiate(); - if (instance) { - instance = _sanitize_preview_node(instance); - preview_node->add_child(instance); - } - } + + Ref scene = res; + if (scene.is_valid()) { + Node *instance = scene->instantiate(); + if (instance) { + instance = _sanitize_preview_node(instance); + preview_node->add_child(instance); } add_preview = true; } + + Ref mesh = res; + if (mesh.is_valid()) { + MeshInstance3D *mesh_instance = memnew(MeshInstance3D); + mesh_instance->set_mesh(mesh); + preview_node->add_child(mesh_instance); + add_preview = true; + } + + Ref audio = res; + if (audio.is_valid()) { + Sprite3D *sprite = memnew(Sprite3D); + sprite->set_texture(get_editor_theme_icon(SNAME("Gizmo3DSamplePlayer"))); + sprite->set_billboard_mode(StandardMaterial3D::BILLBOARD_ENABLED); + sprite->set_pixel_size(0.005); + preview_node->add_child(sprite); + add_preview = true; + } } if (add_preview) { EditorNode::get_singleton()->get_scene_root()->add_child(preview_node); @@ -4346,12 +4358,12 @@ bool Node3DEditorViewport::_cyclical_dependency_exists(const String &p_target_sc return false; } -bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Point2 &p_point) { - Ref res = ResourceLoader::load(path); +bool Node3DEditorViewport::_create_instance(Node *p_parent, const String &p_path, const Point2 &p_point) { + Ref res = ResourceLoader::load(p_path); ERR_FAIL_COND_V(res.is_null(), false); - Ref scene = Ref(Object::cast_to(*res)); - Ref mesh = Ref(Object::cast_to(*res)); + Ref scene = res; + Ref mesh = res; Node *instantiated_scene = nullptr; @@ -4361,8 +4373,10 @@ bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Po mesh_instance->set_mesh(mesh); // Adjust casing according to project setting. The file name is expected to be in snake_case, but will work for others. - String name = path.get_file().get_basename(); - mesh_instance->set_name(Node::adjust_name_casing(name)); + const String &node_name = Node::adjust_name_casing(p_path.get_file().get_basename()); + if (!node_name.is_empty()) { + mesh_instance->set_name(node_name); + } instantiated_scene = mesh_instance; } else { @@ -4386,25 +4400,25 @@ bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Po } if (scene != nullptr) { - instantiated_scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(path)); + instantiated_scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(p_path)); } EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->add_do_method(parent, "add_child", instantiated_scene, true); + undo_redo->add_do_method(p_parent, "add_child", instantiated_scene, true); undo_redo->add_do_method(instantiated_scene, "set_owner", EditorNode::get_singleton()->get_edited_scene()); undo_redo->add_do_reference(instantiated_scene); - undo_redo->add_undo_method(parent, "remove_child", instantiated_scene); + undo_redo->add_undo_method(p_parent, "remove_child", instantiated_scene); undo_redo->add_do_method(editor_selection, "add_node", instantiated_scene); - String new_name = parent->validate_child_name(instantiated_scene); + String new_name = p_parent->validate_child_name(instantiated_scene); EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); - undo_redo->add_do_method(ed, "live_debug_instantiate_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent), path, new_name); - undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent)) + "/" + new_name)); + undo_redo->add_do_method(ed, "live_debug_instantiate_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(p_parent), p_path, 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)); Node3D *node3d = Object::cast_to(instantiated_scene); if (node3d) { Transform3D parent_tf; - Node3D *parent_node3d = Object::cast_to(parent); + Node3D *parent_node3d = Object::cast_to(p_parent); if (parent_node3d) { parent_tf = parent_node3d->get_global_gizmo_transform(); } @@ -4419,6 +4433,46 @@ bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Po return true; } +bool Node3DEditorViewport::_create_audio_node(Node *p_parent, const String &p_path, const Point2 &p_point) { + Ref audio = ResourceLoader::load(p_path); + ERR_FAIL_COND_V(audio.is_null(), false); + + AudioStreamPlayer3D *audio_player = memnew(AudioStreamPlayer3D); + audio_player->set_stream(audio); + + // 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()) { + audio_player->set_name(node_name); + } + + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + undo_redo->add_do_method(p_parent, "add_child", audio_player, true); + undo_redo->add_do_method(audio_player, "set_owner", EditorNode::get_singleton()->get_edited_scene()); + undo_redo->add_do_reference(audio_player); + undo_redo->add_undo_method(p_parent, "remove_child", audio_player); + undo_redo->add_do_method(editor_selection, "add_node", audio_player); + + const String new_name = p_parent->validate_child_name(audio_player); + 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), audio_player->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)); + + Transform3D parent_tf; + Node3D *parent_node3d = Object::cast_to(p_parent); + if (parent_node3d) { + parent_tf = parent_node3d->get_global_gizmo_transform(); + } + + Transform3D new_tf = audio_player->get_transform(); + new_tf.origin = parent_tf.affine_inverse().xform(preview_node_pos + audio_player->get_position()); + new_tf.basis = parent_tf.affine_inverse().basis * new_tf.basis; + + undo_redo->add_do_method(audio_player, "set_transform", new_tf); + + return true; +} + void Node3DEditorViewport::_perform_drop_data() { EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); if (spatial_editor->get_preview_material_target().is_valid()) { @@ -4453,11 +4507,18 @@ void Node3DEditorViewport::_perform_drop_data() { if (res.is_null()) { continue; } - Ref scene = Ref(Object::cast_to(*res)); - Ref mesh = Ref(Object::cast_to(*res)); - if (mesh != nullptr || scene != nullptr) { - bool success = _create_instance(target_node, path, drop_pos); - if (!success) { + + Ref scene = res; + Ref mesh = res; + if (mesh.is_valid() || scene.is_valid()) { + if (!_create_instance(target_node, path, drop_pos)) { + error_files.push_back(path.get_file()); + } + } + + Ref audio = res; + if (audio.is_valid()) { + if (!_create_audio_node(target_node, path, drop_pos)) { error_files.push_back(path.get_file()); } } @@ -4488,12 +4549,14 @@ bool Node3DEditorViewport::can_drop_data_fw(const Point2 &p_point, const Variant bool is_other_valid = false; // Check if at least one of the dragged files is a mesh, material, texture or scene. for (int i = 0; i < files.size(); i++) { - bool is_scene = ClassDB::is_parent_class(ResourceLoader::get_resource_type(files[i]), "PackedScene"); - bool is_mesh = ClassDB::is_parent_class(ResourceLoader::get_resource_type(files[i]), "Mesh"); - bool is_material = ClassDB::is_parent_class(ResourceLoader::get_resource_type(files[i]), "Material"); - bool is_texture = ClassDB::is_parent_class(ResourceLoader::get_resource_type(files[i]), "Texture"); + const String &res_type = ResourceLoader::get_resource_type(files[i]); + bool is_scene = ClassDB::is_parent_class(res_type, "PackedScene"); + bool is_mesh = ClassDB::is_parent_class(res_type, "Mesh"); + bool is_material = ClassDB::is_parent_class(res_type, "Material"); + bool is_texture = ClassDB::is_parent_class(res_type, "Texture"); + bool is_audio = ClassDB::is_parent_class(res_type, "AudioStream"); - if (is_mesh || is_scene || is_material || is_texture) { + if (is_mesh || is_scene || is_material || is_texture || is_audio) { Ref res = ResourceLoader::load(files[i]); if (res.is_null()) { continue; @@ -4502,6 +4565,7 @@ bool Node3DEditorViewport::can_drop_data_fw(const Point2 &p_point, const Variant Ref mesh = res; Ref mat = res; Ref tex = res; + Ref audio = res; if (scn.is_valid()) { Node *instantiated_scene = scn->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE); if (!instantiated_scene) { @@ -4537,6 +4601,8 @@ bool Node3DEditorViewport::can_drop_data_fw(const Point2 &p_point, const Variant spatial_editor->set_preview_material(new_mat); is_other_valid = true; continue; + } else if (!is_other_valid && audio.is_valid()) { + is_other_valid = true; } else { continue; } diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index a3e1224cb8b..4990b11a47e 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -443,7 +443,8 @@ private: void _reset_preview_material() const; void _remove_preview_material(); bool _cyclical_dependency_exists(const String &p_target_scene_path, Node *p_desired_node) const; - bool _create_instance(Node *parent, String &path, const Point2 &p_point); + bool _create_instance(Node *p_parent, const String &p_path, const Point2 &p_point); + bool _create_audio_node(Node *p_parent, const String &p_path, const Point2 &p_point); void _perform_drop_data(); bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 53145c3450e..66d26c0a6cf 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -56,6 +56,7 @@ #include "editor/shader_create_dialog.h" #include "editor/themes/editor_scale.h" #include "scene/animation/animation_tree.h" +#include "scene/audio/audio_stream_player.h" #include "scene/gui/check_box.h" #include "scene/property_utils.h" #include "scene/resources/packed_scene.h" @@ -254,8 +255,8 @@ void SceneTreeDock::instantiate_scenes(const Vector &p_files, Node *p_pa _perform_instantiate_scenes(p_files, parent, -1); } -void SceneTreeDock::_perform_instantiate_scenes(const Vector &p_files, Node *parent, int p_pos) { - ERR_FAIL_NULL(parent); +void SceneTreeDock::_perform_instantiate_scenes(const Vector &p_files, Node *p_parent, int p_pos) { + ERR_FAIL_NULL(p_parent); Vector instances; @@ -302,25 +303,25 @@ void SceneTreeDock::_perform_instantiate_scenes(const Vector &p_files, N } EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("Instantiate Scene(s)")); + undo_redo->create_action_for_history(TTRN("Instantiate Scene", "Instantiate Scenes", instances.size()), editor_data->get_current_edited_scene_history_id()); + undo_redo->add_do_method(editor_selection, "clear"); for (int i = 0; i < instances.size(); i++) { Node *instantiated_scene = instances[i]; - undo_redo->add_do_method(parent, "add_child", instantiated_scene, true); + undo_redo->add_do_method(p_parent, "add_child", instantiated_scene, true); if (p_pos >= 0) { - undo_redo->add_do_method(parent, "move_child", instantiated_scene, p_pos + i); + undo_redo->add_do_method(p_parent, "move_child", instantiated_scene, p_pos + i); } undo_redo->add_do_method(instantiated_scene, "set_owner", edited_scene); - undo_redo->add_do_method(editor_selection, "clear"); undo_redo->add_do_method(editor_selection, "add_node", instantiated_scene); undo_redo->add_do_reference(instantiated_scene); - undo_redo->add_undo_method(parent, "remove_child", instantiated_scene); + undo_redo->add_undo_method(p_parent, "remove_child", instantiated_scene); - String new_name = parent->validate_child_name(instantiated_scene); + String new_name = p_parent->validate_child_name(instantiated_scene); EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); - undo_redo->add_do_method(ed, "live_debug_instantiate_node", edited_scene->get_path_to(parent), p_files[i], new_name); - undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)).path_join(new_name))); + undo_redo->add_do_method(ed, "live_debug_instantiate_node", edited_scene->get_path_to(p_parent), p_files[i], new_name); + undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(p_parent)).path_join(new_name))); } undo_redo->commit_action(); @@ -330,6 +331,75 @@ void SceneTreeDock::_perform_instantiate_scenes(const Vector &p_files, N } } +void SceneTreeDock::_perform_create_audio_stream_players(const Vector &p_files, Node *p_parent, int p_pos) { + ERR_FAIL_NULL(p_parent); + + StringName node_type = "AudioStreamPlayer"; + if (Input::get_singleton()->is_key_pressed(Key::SHIFT)) { + if (Object::cast_to(p_parent)) { + node_type = "AudioStreamPlayer2D"; + } else if (Object::cast_to(p_parent)) { + node_type = "AudioStreamPlayer3D"; + } + } + + Vector nodes; + bool error = false; + + for (const String &path : p_files) { + Ref stream = ResourceLoader::load(path); + if (stream.is_null()) { + current_option = -1; + accept->set_text(vformat(TTR("Error loading audio stream from %s"), path)); + accept->popup_centered(); + error = true; + break; + } + + Node *player = Object::cast_to(ClassDB::instantiate(node_type)); + player->set("stream", stream); + + // 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(path.get_file().get_basename()); + if (!node_name.is_empty()) { + player->set_name(node_name); + } + + nodes.push_back(player); + } + + if (error) { + for (Node *node : nodes) { + memdelete(node); + } + return; + } + + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + undo_redo->create_action_for_history(TTRN("Create AudioStreamPlayer", "Create AudioStreamPlayers", nodes.size()), editor_data->get_current_edited_scene_history_id()); + undo_redo->add_do_method(editor_selection, "clear"); + + for (int i = 0; i < nodes.size(); i++) { + Node *node = nodes[i]; + + undo_redo->add_do_method(p_parent, "add_child", node, true); + if (p_pos >= 0) { + undo_redo->add_do_method(p_parent, "move_child", node, p_pos + i); + } + undo_redo->add_do_method(node, "set_owner", edited_scene); + undo_redo->add_do_method(editor_selection, "add_node", node); + undo_redo->add_do_reference(node); + undo_redo->add_undo_method(p_parent, "remove_child", node); + + String new_name = p_parent->validate_child_name(node); + EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); + undo_redo->add_do_method(ed, "live_debug_create_node", edited_scene->get_path_to(p_parent), node->get_class(), new_name); + undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(p_parent)).path_join(new_name))); + } + + undo_redo->commit_action(); +} + void SceneTreeDock::_replace_with_branch_scene(const String &p_file, Node *base) { // `move_child` + `get_index` doesn't really work for internal nodes. ERR_FAIL_COND_MSG(base->get_internal_mode() != INTERNAL_MODE_DISABLED, "Trying to replace internal node, this is not supported."); @@ -3196,15 +3266,13 @@ void SceneTreeDock::_normalize_drop(Node *&to_node, int &to_pos, int p_type) { void SceneTreeDock::_files_dropped(const Vector &p_files, NodePath p_to, int p_type) { Node *node = get_node(p_to); ERR_FAIL_NULL(node); + ERR_FAIL_COND(p_files.is_empty()); - if (scene_tree->get_scene_tree()->get_drop_mode_flags() & Tree::DROP_MODE_INBETWEEN) { - // Dropped PackedScene, instance it. - int to_pos = -1; - _normalize_drop(node, to_pos, p_type); - _perform_instantiate_scenes(p_files, node, to_pos); - } else { - const String &res_path = p_files[0]; - StringName res_type = EditorFileSystem::get_singleton()->get_file_type(res_path); + const String &res_path = p_files[0]; + const StringName res_type = EditorFileSystem::get_singleton()->get_file_type(res_path); + + // Dropping as property when possible. + if (p_type == 0 && p_files.size() == 1) { List valid_properties; List pinfo; @@ -3238,9 +3306,21 @@ void SceneTreeDock::_files_dropped(const Vector &p_files, NodePath p_to, menu_properties->reset_size(); menu_properties->set_position(get_screen_position() + get_local_mouse_position()); menu_properties->popup(); - } else if (!valid_properties.is_empty()) { - _perform_property_drop(node, valid_properties.front()->get(), ResourceLoader::load(res_path)); + return; } + if (!valid_properties.is_empty()) { + _perform_property_drop(node, valid_properties.front()->get(), ResourceLoader::load(res_path)); + return; + } + } + + // Either instantiate scenes or create AudioStreamPlayers. + int to_pos = -1; + _normalize_drop(node, to_pos, p_type); + if (res_type == "PackedScene") { + _perform_instantiate_scenes(p_files, node, to_pos); + } else { + _perform_create_audio_stream_players(p_files, node, to_pos); } } diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index 21e1b00f931..abef9909955 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -272,7 +272,8 @@ class SceneTreeDock : public VBoxContainer { void _filter_option_selected(int option); void _append_filter_options_to(PopupMenu *p_menu, bool p_include_separator = true); - void _perform_instantiate_scenes(const Vector &p_files, Node *parent, int p_pos); + void _perform_instantiate_scenes(const Vector &p_files, Node *p_parent, int p_pos); + void _perform_create_audio_stream_players(const Vector &p_files, Node *p_parent, int p_pos); void _replace_with_branch_scene(const String &p_file, Node *base); void _remote_tree_selected();