瀏覽代碼

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
风青山 2 年之前
父節點
當前提交
2642c6851f
共有 2 個文件被更改,包括 31 次插入11 次删除
  1. 27 11
      editor/plugins/sprite_frames_editor_plugin.cpp
  2. 4 0
      editor/plugins/sprite_frames_editor_plugin.h

+ 27 - 11
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) {

+ 4 - 0
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.