From 2642c6851f2ab90c944f565e060ad2df643d575e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E9=9D=92=E5=B1=B1?= Date: Thu, 14 Sep 2023 18:06:49 +0800 Subject: [PATCH] Defer updating the animations Tree in SpriteFramesEditor to avoid crashes Previously, clicking the LMB while renaming an animation could cause `SpriteFramesEditor::_update_library(false)` to be called during `Tree::propagate_mouse_event()`. This may cause a crash. We can defer updates to the editor interface to avoid calling `Tree::create_item()` at the wrong time. Enables `SpriteFramesEditor::_select_animation()` to be able to undo/redo --- .../plugins/sprite_frames_editor_plugin.cpp | 38 +++++++++++++------ editor/plugins/sprite_frames_editor_plugin.h | 4 ++ 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index f41b1d30d7e5..01e9eb7a49eb 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -870,13 +870,10 @@ void SpriteFramesEditor::_sync_animation() { } void SpriteFramesEditor::_select_animation(const String &p_name, bool p_update_node) { - TreeItem *selected = nullptr; - selected = animations->get_item_with_text(p_name); - if (!selected) { + if (!frames->has_animation(p_name)) { return; - }; - - edited_anim = selected->get_text(0); + } + edited_anim = p_name; if (animated_sprite) { if (p_update_node) { @@ -951,6 +948,7 @@ void SpriteFramesEditor::_animation_name_edited() { counter++; name = new_name + "_" + itos(counter); } + edited->set_text(0, name); EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->create_action(TTR("Rename Animation"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene()); @@ -958,11 +956,12 @@ void SpriteFramesEditor::_animation_name_edited() { undo_redo->add_undo_method(frames.ptr(), "rename_animation", name, edited_anim); _rename_node_animation(undo_redo, false, edited_anim, name, name); _rename_node_animation(undo_redo, true, edited_anim, edited_anim, edited_anim); + undo_redo->add_do_method(this, "_select_animation", name); + undo_redo->add_undo_method(this, "_select_animation", edited_anim); undo_redo->add_do_method(this, "_update_library"); undo_redo->add_undo_method(this, "_update_library"); undo_redo->commit_action(); - _select_animation(name); animations->grab_focus(); } @@ -1007,11 +1006,12 @@ void SpriteFramesEditor::_animation_add() { undo_redo->create_action(TTR("Add Animation"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene()); undo_redo->add_do_method(frames.ptr(), "add_animation", name); undo_redo->add_undo_method(frames.ptr(), "remove_animation", name); + undo_redo->add_do_method(this, "_select_animation", name); + undo_redo->add_undo_method(this, "_select_animation", edited_anim); undo_redo->add_do_method(this, "_update_library"); undo_redo->add_undo_method(this, "_update_library"); undo_redo->commit_action(); - _select_animation(name); animations->grab_focus(); } @@ -1057,11 +1057,11 @@ void SpriteFramesEditor::_animation_remove_confirmed() { float duration = frames->get_frame_duration(edited_anim, i); undo_redo->add_undo_method(frames.ptr(), "add_frame", edited_anim, texture, duration); } + undo_redo->add_do_method(this, "_select_animation", new_edited); + undo_redo->add_undo_method(this, "_select_animation", edited_anim); undo_redo->add_do_method(this, "_update_library"); undo_redo->add_undo_method(this, "_update_library"); undo_redo->commit_action(); - - _select_animation(new_edited); } void SpriteFramesEditor::_animation_search_text_changed(const String &p_text) { @@ -1179,6 +1179,20 @@ void SpriteFramesEditor::_zoom_reset() { } void SpriteFramesEditor::_update_library(bool p_skip_selector) { + if (!p_skip_selector) { + animations_dirty = true; + } + + if (pending_update) { + return; + } + pending_update = true; + callable_mp(this, &SpriteFramesEditor::_update_library_impl).call_deferred(); +} + +void SpriteFramesEditor::_update_library_impl() { + pending_update = false; + if (frames.is_null()) { return; } @@ -1187,7 +1201,8 @@ void SpriteFramesEditor::_update_library(bool p_skip_selector) { frame_duration->set_value_no_signal(1.0); // Default. - if (!p_skip_selector) { + if (animations_dirty) { + animations_dirty = false; animations->clear(); TreeItem *anim_root = animations->create_item(); @@ -1624,6 +1639,7 @@ void SpriteFramesEditor::_autoplay_pressed() { void SpriteFramesEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_update_library", "skipsel"), &SpriteFramesEditor::_update_library, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("_select_animation", "name", "update_node"), &SpriteFramesEditor::_select_animation, DEFVAL(true)); } void SpriteFramesEditor::_node_removed(Node *p_node) { diff --git a/editor/plugins/sprite_frames_editor_plugin.h b/editor/plugins/sprite_frames_editor_plugin.h index ed75be906137..f14c2203df23 100644 --- a/editor/plugins/sprite_frames_editor_plugin.h +++ b/editor/plugins/sprite_frames_editor_plugin.h @@ -189,6 +189,7 @@ class SpriteFramesEditor : public HSplitContainer { void _down_pressed(); void _frame_duration_changed(double p_value); void _update_library(bool p_skip_selector = false); + void _update_library_impl(); void _update_stop_icon(); void _play_pressed(); @@ -214,6 +215,9 @@ class SpriteFramesEditor : public HSplitContainer { void _zoom_out(); void _zoom_reset(); + bool animations_dirty = false; + bool pending_update = false; + bool updating; bool updating_split_settings = false; // Skip SpinBox/Range callback when setting value by code.