diff --git a/doc/classes/SpriteFrames.xml b/doc/classes/SpriteFrames.xml index fd8c15c560375..b891f4adcd74b 100644 --- a/doc/classes/SpriteFrames.xml +++ b/doc/classes/SpriteFrames.xml @@ -39,6 +39,14 @@ Removes all animations. An empty [code]default[/code] animation will be created. + + + + + + Duplicates the animation [param anim_from] to a new animation named [param anim_to]. Fails if [param anim_to] already exists, or if [param anim_from] does not exist. + + diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index 27056a6cc4ce8..4d1263789de39 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -582,6 +582,7 @@ void SpriteFramesEditor::_notification(int p_what) { zoom_reset->set_icon(get_editor_theme_icon(SNAME("ZoomReset"))); zoom_in->set_icon(get_editor_theme_icon(SNAME("ZoomMore"))); add_anim->set_icon(get_editor_theme_icon(SNAME("New"))); + duplicate_anim->set_icon(get_editor_theme_icon(SNAME("Duplicate"))); delete_anim->set_icon(get_editor_theme_icon(SNAME("Remove"))); anim_search_box->set_right_icon(get_editor_theme_icon(SNAME("Search"))); split_sheet_zoom_out->set_icon(get_editor_theme_icon(SNAME("ZoomLess"))); @@ -1102,6 +1103,41 @@ void SpriteFramesEditor::_animation_add() { animations->grab_focus(); } +void SpriteFramesEditor::_animation_duplicate() { + if (updating) { + return; + } + + if (!frames->has_animation(edited_anim)) { + return; + } + + int counter = 1; + String new_name = edited_anim; + PackedStringArray name_component = new_name.rsplit("_", true, 1); + String base_name = name_component[0]; + if (name_component.size() > 1 && name_component[1].is_valid_int() && name_component[1].to_int() >= 0) { + counter = name_component[1].to_int(); + } + new_name = base_name + "_" + itos(counter); + while (frames->has_animation(new_name)) { + counter++; + new_name = base_name + "_" + itos(counter); + } + + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + undo_redo->create_action(TTR("Duplicate Animation"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene()); + undo_redo->add_do_method(frames.ptr(), "duplicate_animation", edited_anim, new_name); + undo_redo->add_undo_method(frames.ptr(), "remove_animation", new_name); + undo_redo->add_do_method(this, "_select_animation", new_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(); + + animations->grab_focus(); +} + void SpriteFramesEditor::_animation_remove() { if (updating) { return; @@ -1464,6 +1500,7 @@ void SpriteFramesEditor::edit(Ref p_frames) { _zoom_reset(); add_anim->set_disabled(read_only); + duplicate_anim->set_disabled(read_only); delete_anim->set_disabled(read_only); anim_speed->set_editable(!read_only); anim_loop->set_disabled(read_only); @@ -1788,6 +1825,11 @@ SpriteFramesEditor::SpriteFramesEditor() { hbc_animlist->add_child(add_anim); add_anim->connect(SceneStringName(pressed), callable_mp(this, &SpriteFramesEditor::_animation_add)); + duplicate_anim = memnew(Button); + duplicate_anim->set_flat(true); + hbc_animlist->add_child(duplicate_anim); + duplicate_anim->connect(SceneStringName(pressed), callable_mp(this, &SpriteFramesEditor::_animation_duplicate)); + delete_anim = memnew(Button); delete_anim->set_theme_type_variation("FlatButton"); hbc_animlist->add_child(delete_anim); @@ -1841,6 +1883,8 @@ SpriteFramesEditor::SpriteFramesEditor() { add_anim->set_shortcut_context(animations); add_anim->set_shortcut(ED_SHORTCUT("sprite_frames/new_animation", TTR("Add Animation"), KeyModifierMask::CMD_OR_CTRL | Key::N)); + duplicate_anim->set_shortcut_context(animations); + duplicate_anim->set_shortcut(ED_SHORTCUT("sprite_frames/duplicate_animation", TTR("Duplicate Animation"), KeyModifierMask::CMD_OR_CTRL | Key::D)); delete_anim->set_shortcut_context(animations); delete_anim->set_shortcut(ED_SHORTCUT("sprite_frames/delete_animation", TTR("Delete Animation"), Key::KEY_DELETE)); diff --git a/editor/plugins/sprite_frames_editor_plugin.h b/editor/plugins/sprite_frames_editor_plugin.h index 9b6aaf98fe0c9..d89366b179b93 100644 --- a/editor/plugins/sprite_frames_editor_plugin.h +++ b/editor/plugins/sprite_frames_editor_plugin.h @@ -121,6 +121,7 @@ class SpriteFramesEditor : public HSplitContainer { Vector selection; Button *add_anim = nullptr; + Button *duplicate_anim = nullptr; Button *delete_anim = nullptr; SpinBox *anim_speed = nullptr; Button *anim_loop = nullptr; @@ -210,6 +211,7 @@ class SpriteFramesEditor : public HSplitContainer { void _animation_selected(); void _animation_name_edited(); void _animation_add(); + void _animation_duplicate(); void _animation_remove(); void _animation_remove_confirmed(); void _animation_search_text_changed(const String &p_text); diff --git a/scene/resources/sprite_frames.cpp b/scene/resources/sprite_frames.cpp index 6e43ea9b1744d..dac0ceaa78b0e 100644 --- a/scene/resources/sprite_frames.cpp +++ b/scene/resources/sprite_frames.cpp @@ -106,6 +106,12 @@ bool SpriteFrames::has_animation(const StringName &p_anim) const { return animations.has(p_anim); } +void SpriteFrames::duplicate_animation(const StringName &p_from, const StringName &p_to) { + ERR_FAIL_COND_MSG(!animations.has(p_from), vformat("SpriteFrames doesn't have animation '%s'.", p_from)); + ERR_FAIL_COND_MSG(animations.has(p_to), vformat("Animation '%s' already exists.", p_to)); + animations[p_to] = animations[p_from]; +} + void SpriteFrames::remove_animation(const StringName &p_anim) { animations.erase(p_anim); } @@ -246,6 +252,7 @@ void SpriteFrames::get_argument_options(const StringName &p_function, int p_idx, void SpriteFrames::_bind_methods() { ClassDB::bind_method(D_METHOD("add_animation", "anim"), &SpriteFrames::add_animation); ClassDB::bind_method(D_METHOD("has_animation", "anim"), &SpriteFrames::has_animation); + ClassDB::bind_method(D_METHOD("duplicate_animation", "anim_from", "anim_to"), &SpriteFrames::duplicate_animation); ClassDB::bind_method(D_METHOD("remove_animation", "anim"), &SpriteFrames::remove_animation); ClassDB::bind_method(D_METHOD("rename_animation", "anim", "newname"), &SpriteFrames::rename_animation); diff --git a/scene/resources/sprite_frames.h b/scene/resources/sprite_frames.h index 0e0bb28d71938..8d5b4232cf070 100644 --- a/scene/resources/sprite_frames.h +++ b/scene/resources/sprite_frames.h @@ -60,6 +60,7 @@ class SpriteFrames : public Resource { public: void add_animation(const StringName &p_anim); bool has_animation(const StringName &p_anim) const; + void duplicate_animation(const StringName &p_from, const StringName &p_to); void remove_animation(const StringName &p_anim); void rename_animation(const StringName &p_prev, const StringName &p_next);