Prompt to confirm anim track delete on node delete

This commit is contained in:
kobewi 2022-02-27 03:15:01 +01:00
parent 45cd5dcad3
commit 36b4ed4fa4
2 changed files with 96 additions and 29 deletions

View file

@ -53,6 +53,7 @@
#include "editor/plugins/script_editor_plugin.h" #include "editor/plugins/script_editor_plugin.h"
#include "editor/reparent_dialog.h" #include "editor/reparent_dialog.h"
#include "editor/shader_create_dialog.h" #include "editor/shader_create_dialog.h"
#include "scene/gui/check_box.h"
#include "scene/main/window.h" #include "scene/main/window.h"
#include "scene/property_utils.h" #include "scene/property_utils.h"
#include "scene/resources/packed_scene.h" #include "scene/resources/packed_scene.h"
@ -850,9 +851,10 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
break; break;
} }
if (p_confirm_override) { bool allow_ask_delete_tracks = EDITOR_GET("docks/scene_tree/ask_before_deleting_related_animation_tracks").operator bool();
bool has_tracks_to_delete = allow_ask_delete_tracks && _has_tracks_to_delete(edited_scene, remove_list);
if (p_confirm_override && !has_tracks_to_delete) {
_delete_confirm(); _delete_confirm();
} else { } else {
String msg; String msg;
if (remove_list.size() > 1) { if (remove_list.size() > 1) {
@ -863,18 +865,30 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
msg = vformat(any_children ? TTR("Delete %d nodes and any children?") : TTR("Delete %d nodes?"), remove_list.size()); msg = vformat(any_children ? TTR("Delete %d nodes and any children?") : TTR("Delete %d nodes?"), remove_list.size());
} else { } else {
Node *node = remove_list[0]; if (!p_confirm_override) {
if (node == editor_data->get_edited_scene_root()) { Node *node = remove_list[0];
msg = vformat(TTR("Delete the root node \"%s\"?"), node->get_name()); if (node == editor_data->get_edited_scene_root()) {
} else if (node->get_scene_file_path().is_empty() && node->get_child_count() > 0) { msg = vformat(TTR("Delete the root node \"%s\"?"), node->get_name());
// Display this message only for non-instantiated scenes } else if (node->get_scene_file_path().is_empty() && node->get_child_count() > 0) {
msg = vformat(TTR("Delete node \"%s\" and its children?"), node->get_name()); // Display this message only for non-instantiated scenes
msg = vformat(TTR("Delete node \"%s\" and its children?"), node->get_name());
} else {
msg = vformat(TTR("Delete node \"%s\"?"), node->get_name());
}
}
if (has_tracks_to_delete) {
if (!msg.is_empty()) {
msg += "\n";
}
msg += TTR("Some nodes are referenced by animation tracks.");
delete_tracks_checkbox->show();
} else { } else {
msg = vformat(TTR("Delete node \"%s\"?"), node->get_name()); delete_tracks_checkbox->hide();
} }
} }
delete_dialog->set_text(msg); delete_dialog_label->set_text(msg);
// Resize the dialog to its minimum size. // Resize the dialog to its minimum size.
// This prevents the dialog from being too wide after displaying // This prevents the dialog from being too wide after displaying
@ -1496,12 +1510,10 @@ void SceneTreeDock::_set_owners(Node *p_owner, const Array &p_nodes) {
void SceneTreeDock::_fill_path_renames(Vector<StringName> base_path, Vector<StringName> new_base_path, Node *p_node, HashMap<Node *, NodePath> *p_renames) { void SceneTreeDock::_fill_path_renames(Vector<StringName> base_path, Vector<StringName> new_base_path, Node *p_node, HashMap<Node *, NodePath> *p_renames) {
base_path.push_back(p_node->get_name()); base_path.push_back(p_node->get_name());
if (new_base_path.size()) {
new_base_path.push_back(p_node->get_name());
}
NodePath new_path; NodePath new_path;
if (new_base_path.size()) { if (!new_base_path.is_empty()) {
new_base_path.push_back(p_node->get_name());
new_path = NodePath(new_base_path, true); new_path = NodePath(new_base_path, true);
} }
@ -1512,6 +1524,43 @@ void SceneTreeDock::_fill_path_renames(Vector<StringName> base_path, Vector<Stri
} }
} }
bool SceneTreeDock::_has_tracks_to_delete(Node *p_node, List<Node *> &p_to_delete) const {
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node);
if (ap) {
Node *root = ap->get_node(ap->get_root());
if (root && !p_to_delete.find(root)) {
List<StringName> anims;
ap->get_animation_list(&anims);
for (const StringName &E : anims) {
Ref<Animation> anim = ap->get_animation(E);
if (anim.is_null()) {
continue;
}
for (int i = 0; i < anim->get_track_count(); i++) {
NodePath track_np = anim->track_get_path(i);
Node *n = root->get_node_or_null(track_np);
if (n) {
for (const Node *F : p_to_delete) {
if (F == n || F->is_ancestor_of(n)) {
return true;
}
}
}
}
}
}
}
for (int i = 0; i < p_node->get_child_count(); i++) {
if (_has_tracks_to_delete(p_node->get_child(i), p_to_delete)) {
return true;
}
}
return false;
}
void SceneTreeDock::fill_path_renames(Node *p_node, Node *p_new_parent, HashMap<Node *, NodePath> *p_renames) { void SceneTreeDock::fill_path_renames(Node *p_node, Node *p_new_parent, HashMap<Node *, NodePath> *p_renames) {
Vector<StringName> base_path; Vector<StringName> base_path;
Node *n = p_node->get_parent(); Node *n = p_node->get_parent();
@ -1701,7 +1750,7 @@ void SceneTreeDock::perform_node_renames(Node *p_base, HashMap<Node *, NodePath>
HashMap<Node *, NodePath>::Iterator found_path = p_renames->find(n); HashMap<Node *, NodePath>::Iterator found_path = p_renames->find(n);
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
if (found_path) { if (found_path) {
if (found_path->value == NodePath()) { if (found_path->value.is_empty()) {
//will be erased //will be erased
int idx = 0; int idx = 0;
@ -2094,11 +2143,6 @@ void SceneTreeDock::_delete_confirm(bool p_cut) {
return; return;
} }
EditorNode::get_singleton()->hide_unused_editors(this);
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(p_cut ? TTR("Cut Node(s)") : TTR("Remove Node(s)"), UndoRedo::MERGE_DISABLE, remove_list.front()->get());
bool entire_scene = false; bool entire_scene = false;
for (const Node *E : remove_list) { for (const Node *E : remove_list) {
@ -2108,27 +2152,34 @@ void SceneTreeDock::_delete_confirm(bool p_cut) {
} }
} }
EditorNode::get_singleton()->hide_unused_editors(this);
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(p_cut ? TTR("Cut Node(s)") : TTR("Remove Node(s)"), UndoRedo::MERGE_DISABLE, remove_list.front()->get());
if (entire_scene) { if (entire_scene) {
undo_redo->add_do_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr); undo_redo->add_do_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr);
undo_redo->add_undo_method(EditorNode::get_singleton(), "set_edited_scene", edited_scene); undo_redo->add_undo_method(EditorNode::get_singleton(), "set_edited_scene", edited_scene);
undo_redo->add_undo_method(edited_scene, "set_owner", edited_scene->get_owner()); undo_redo->add_undo_method(edited_scene, "set_owner", edited_scene->get_owner());
undo_redo->add_undo_method(scene_tree, "update_tree"); undo_redo->add_undo_method(scene_tree, "update_tree");
undo_redo->add_undo_reference(edited_scene); undo_redo->add_undo_reference(edited_scene);
} else { } else {
remove_list.sort_custom<Node::Comparator>(); //sort nodes to keep positions if (delete_tracks_checkbox->is_pressed() || p_cut) {
HashMap<Node *, NodePath> path_renames; remove_list.sort_custom<Node::Comparator>(); // Sort nodes to keep positions.
HashMap<Node *, NodePath> path_renames;
//delete from animation //delete from animation
for (Node *n : remove_list) { for (Node *n : remove_list) {
if (!n->is_inside_tree() || !n->get_parent()) { if (!n->is_inside_tree() || !n->get_parent()) {
continue; continue;
}
fill_path_renames(n, nullptr, &path_renames);
} }
fill_path_renames(n, nullptr, &path_renames); perform_node_renames(nullptr, &path_renames);
} }
perform_node_renames(nullptr, &path_renames);
//delete for read //delete for read
for (Node *n : remove_list) { for (Node *n : remove_list) {
if (!n->is_inside_tree() || !n->get_parent()) { if (!n->is_inside_tree() || !n->get_parent()) {
@ -3774,6 +3825,16 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec
add_child(delete_dialog); add_child(delete_dialog);
delete_dialog->connect("confirmed", callable_mp(this, &SceneTreeDock::_delete_confirm).bind(false)); delete_dialog->connect("confirmed", callable_mp(this, &SceneTreeDock::_delete_confirm).bind(false));
VBoxContainer *vb = memnew(VBoxContainer);
delete_dialog->add_child(vb);
delete_dialog_label = memnew(Label);
vb->add_child(delete_dialog_label);
delete_tracks_checkbox = memnew(CheckBox(TTR("Delete Related Animation Tracks")));
delete_tracks_checkbox->set_pressed(true);
vb->add_child(delete_tracks_checkbox);
editable_instance_remove_dialog = memnew(ConfirmationDialog); editable_instance_remove_dialog = memnew(ConfirmationDialog);
add_child(editable_instance_remove_dialog); add_child(editable_instance_remove_dialog);
editable_instance_remove_dialog->connect("confirmed", callable_mp(this, &SceneTreeDock::_toggle_editable_children_from_selection)); editable_instance_remove_dialog->connect("confirmed", callable_mp(this, &SceneTreeDock::_toggle_editable_children_from_selection));
@ -3810,6 +3871,7 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec
EDITOR_DEF("interface/editors/show_scene_tree_root_selection", true); EDITOR_DEF("interface/editors/show_scene_tree_root_selection", true);
EDITOR_DEF("interface/editors/derive_script_globals_by_name", true); EDITOR_DEF("interface/editors/derive_script_globals_by_name", true);
EDITOR_DEF("docks/scene_tree/ask_before_deleting_related_animation_tracks", true);
EDITOR_DEF("_use_favorites_root_selection", false); EDITOR_DEF("_use_favorites_root_selection", false);
Resource::_update_configuration_warning = _update_configuration_warning; Resource::_update_configuration_warning = _update_configuration_warning;

View file

@ -36,6 +36,7 @@
#include "scene/gui/box_container.h" #include "scene/gui/box_container.h"
#include "scene/resources/animation.h" #include "scene/resources/animation.h"
class CheckBox;
class EditorData; class EditorData;
class EditorSelection; class EditorSelection;
class EditorQuickOpen; class EditorQuickOpen;
@ -148,6 +149,8 @@ class SceneTreeDock : public VBoxContainer {
ShaderCreateDialog *shader_create_dialog = nullptr; ShaderCreateDialog *shader_create_dialog = nullptr;
AcceptDialog *accept = nullptr; AcceptDialog *accept = nullptr;
ConfirmationDialog *delete_dialog = nullptr; ConfirmationDialog *delete_dialog = nullptr;
Label *delete_dialog_label = nullptr;
CheckBox *delete_tracks_checkbox = nullptr;
ConfirmationDialog *editable_instance_remove_dialog = nullptr; ConfirmationDialog *editable_instance_remove_dialog = nullptr;
ConfirmationDialog *placeholder_editable_instance_remove_dialog = nullptr; ConfirmationDialog *placeholder_editable_instance_remove_dialog = nullptr;
@ -213,6 +216,7 @@ class SceneTreeDock : public VBoxContainer {
void _shader_creation_closed(); void _shader_creation_closed();
void _delete_confirm(bool p_cut = false); void _delete_confirm(bool p_cut = false);
void _delete_dialog_closed();
void _toggle_editable_children_from_selection(); void _toggle_editable_children_from_selection();
void _toggle_editable_children(Node *p_node); void _toggle_editable_children(Node *p_node);
@ -234,6 +238,7 @@ class SceneTreeDock : public VBoxContainer {
void _update_script_button(); void _update_script_button();
void _fill_path_renames(Vector<StringName> base_path, Vector<StringName> new_base_path, Node *p_node, HashMap<Node *, NodePath> *p_renames); void _fill_path_renames(Vector<StringName> base_path, Vector<StringName> new_base_path, Node *p_node, HashMap<Node *, NodePath> *p_renames);
bool _has_tracks_to_delete(Node *p_node, List<Node *> &p_to_delete) const;
void _normalize_drop(Node *&to_node, int &to_pos, int p_type); void _normalize_drop(Node *&to_node, int &to_pos, int p_type);