Merge pull request #119418 from KoBeWi/what_even_is_this

Improve 2D editor dropping code
This commit is contained in:
Thaddeus Crews
2026-06-19 12:05:51 -05:00
2 changed files with 45 additions and 75 deletions
+44 -75
View File
@@ -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<CanvasItem>(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<Texture2D> 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<Control>(p_child) || Object::cast_to<TouchScreenButton>(p_child) || Object::cast_to<Polygon2D>(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<TouchScreenButton>(p_child) || Object::cast_to<TextureButton>(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<Control>(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<Control>(p_child) || Object::cast_to<TouchScreenButton>(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<CanvasItem>(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<CanvasItem>(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<Node>(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<AudioStream> 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<PackedScene> 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;
}
}
+1
View File
@@ -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);