diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp index 40ea0a378b1..39ba51face1 100644 --- a/editor/animation_bezier_editor.cpp +++ b/editor/animation_bezier_editor.cpp @@ -34,6 +34,7 @@ #include "editor/editor_settings.h" #include "editor/editor_string_names.h" #include "editor/editor_undo_redo_manager.h" +#include "editor/gui/editor_spin_slider.h" #include "editor/themes/editor_scale.h" #include "scene/gui/view_panner.h" #include "scene/resources/text_line.h" @@ -900,7 +901,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref &p_event) { if (p_event->is_pressed()) { if (ED_IS_SHORTCUT("animation_editor/duplicate_selected_keys", p_event)) { if (!read_only) { - duplicate_selected_keys(-1.0); + duplicate_selected_keys(-1.0, false); } accept_event(); } @@ -918,7 +919,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref &p_event) { } if (ED_IS_SHORTCUT("animation_editor/paste_keys", p_event)) { if (!read_only) { - paste_keys(-1.0); + paste_keys(-1.0, false); } accept_event(); } @@ -1642,25 +1643,28 @@ void AnimationBezierTrackEdit::_menu_selected(int p_index) { real_t time = ((menu_insert_key.x - limit) / timeline->get_zoom_scale()) + timeline->get_value(); - while (animation->track_find_key(selected_track, time, Animation::FIND_MODE_APPROX) != -1) { - time += 0.001; - } - switch (p_index) { case MENU_KEY_INSERT: { if (animation->get_track_count() > 0) { + if (editor->snap->is_pressed() && editor->step->get_value() != 0) { + time = editor->snap_time(time); + } + while (animation->track_find_key(selected_track, time, Animation::FIND_MODE_APPROX) != -1) { + time += 0.001; + } float h = (get_size().height / 2.0 - menu_insert_key.y) * timeline_v_zoom + timeline_v_scroll; Array new_point = make_default_bezier_key(h); EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->create_action(TTR("Add Bezier Point")); undo_redo->add_do_method(animation.ptr(), "track_insert_key", selected_track, time, new_point); + undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation); undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", selected_track, time); undo_redo->commit_action(); queue_redraw(); } } break; case MENU_KEY_DUPLICATE: { - duplicate_selected_keys(time); + duplicate_selected_keys(time, true); } break; case MENU_KEY_DELETE: { delete_selection(); @@ -1672,7 +1676,7 @@ void AnimationBezierTrackEdit::_menu_selected(int p_index) { copy_selected_keys(false); } break; case MENU_KEY_PASTE: { - paste_keys(time); + paste_keys(time, true); } break; case MENU_KEY_SET_HANDLE_FREE: { _change_selected_keys_handle_mode(Animation::HANDLE_MODE_FREE); @@ -1695,7 +1699,7 @@ void AnimationBezierTrackEdit::_menu_selected(int p_index) { } } -void AnimationBezierTrackEdit::duplicate_selected_keys(real_t p_ofs) { +void AnimationBezierTrackEdit::duplicate_selected_keys(real_t p_ofs, bool p_ofs_valid) { if (selection.size() == 0) { return; } @@ -1715,7 +1719,14 @@ void AnimationBezierTrackEdit::duplicate_selected_keys(real_t p_ofs) { for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) { real_t t = animation->track_get_key_time(E->get().first, E->get().second); - real_t insert_pos = p_ofs >= 0 ? p_ofs : timeline->get_play_position(); + real_t insert_pos = p_ofs_valid ? p_ofs : timeline->get_play_position(); + + if (p_ofs_valid) { + if (editor->snap->is_pressed() && editor->step->get_value() != 0) { + insert_pos = editor->snap_time(insert_pos); + } + } + real_t dst_time = t + (insert_pos - top_time); int existing_idx = animation->track_find_key(E->get().first, dst_time, Animation::FIND_MODE_APPROX); @@ -1793,7 +1804,7 @@ void AnimationBezierTrackEdit::copy_selected_keys(bool p_cut) { } } -void AnimationBezierTrackEdit::paste_keys(real_t p_ofs) { +void AnimationBezierTrackEdit::paste_keys(real_t p_ofs, bool p_ofs_valid) { if (editor->is_key_clipboard_active() && animation.is_valid() && (selected_track >= 0 && selected_track < animation->get_track_count())) { EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->create_action(TTR("Animation Paste Keys")); @@ -1824,7 +1835,12 @@ void AnimationBezierTrackEdit::paste_keys(real_t p_ofs) { for (int i = 0; i < editor->key_clipboard.keys.size(); i++) { const AnimationTrackEditor::KeyClipboard::Key key = editor->key_clipboard.keys[i]; - float insert_pos = p_ofs >= 0 ? p_ofs : timeline->get_play_position(); + float insert_pos = p_ofs_valid ? p_ofs : timeline->get_play_position(); + if (p_ofs_valid) { + if (editor->snap->is_pressed() && editor->step->get_value() != 0) { + insert_pos = editor->snap_time(insert_pos); + } + } float dst_time = key.time + insert_pos; int existing_idx = animation->track_find_key(selected_track, dst_time, Animation::FIND_MODE_APPROX); diff --git a/editor/animation_bezier_editor.h b/editor/animation_bezier_editor.h index 371c5632678..2254d474ba7 100644 --- a/editor/animation_bezier_editor.h +++ b/editor/animation_bezier_editor.h @@ -216,9 +216,9 @@ public: void set_play_position(real_t p_pos); void update_play_position(); - void duplicate_selected_keys(real_t p_ofs); + void duplicate_selected_keys(real_t p_ofs, bool p_ofs_valid); void copy_selected_keys(bool p_cut); - void paste_keys(real_t p_ofs); + void paste_keys(real_t p_ofs, bool p_ofs_valid); void delete_selection(); void _bezier_track_insert_key(int p_track, double p_time, real_t p_value, const Vector2 &p_in_handle, const Vector2 &p_out_handle, const Animation::HandleMode p_handle_mode); diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 7f0d4a124fc..d89dd4981e3 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -2754,7 +2754,7 @@ void AnimationTrackEdit::gui_input(const Ref &p_event) { if (p_event->is_pressed()) { if (ED_IS_SHORTCUT("animation_editor/duplicate_selected_keys", p_event)) { if (!read_only) { - emit_signal(SNAME("duplicate_request"), -1.0); + emit_signal(SNAME("duplicate_request"), -1.0, false); } accept_event(); } @@ -2773,7 +2773,7 @@ void AnimationTrackEdit::gui_input(const Ref &p_event) { if (ED_IS_SHORTCUT("animation_editor/paste_keys", p_event)) { if (!read_only) { - emit_signal(SNAME("paste_request"), -1.0); + emit_signal(SNAME("paste_request"), -1.0, false); } accept_event(); } @@ -3248,7 +3248,7 @@ void AnimationTrackEdit::_menu_selected(int p_index) { emit_signal(SNAME("insert_key"), insert_at_pos); } break; case MENU_KEY_DUPLICATE: { - emit_signal(SNAME("duplicate_request"), insert_at_pos); + emit_signal(SNAME("duplicate_request"), insert_at_pos, true); } break; case MENU_KEY_CUT: { emit_signal(SNAME("cut_request")); @@ -3257,7 +3257,7 @@ void AnimationTrackEdit::_menu_selected(int p_index) { emit_signal(SNAME("copy_request")); } break; case MENU_KEY_PASTE: { - emit_signal(SNAME("paste_request"), insert_at_pos); + emit_signal(SNAME("paste_request"), insert_at_pos, true); } break; case MENU_KEY_ADD_RESET: { emit_signal(SNAME("create_reset_request")); @@ -3331,11 +3331,11 @@ void AnimationTrackEdit::_bind_methods() { ADD_SIGNAL(MethodInfo("move_selection_commit")); ADD_SIGNAL(MethodInfo("move_selection_cancel")); - ADD_SIGNAL(MethodInfo("duplicate_request", PropertyInfo(Variant::FLOAT, "offset"))); + ADD_SIGNAL(MethodInfo("duplicate_request", PropertyInfo(Variant::FLOAT, "offset"), PropertyInfo(Variant::BOOL, "is_offset_valid"))); ADD_SIGNAL(MethodInfo("create_reset_request")); ADD_SIGNAL(MethodInfo("copy_request")); ADD_SIGNAL(MethodInfo("cut_request")); - ADD_SIGNAL(MethodInfo("paste_request", PropertyInfo(Variant::FLOAT, "offset"))); + ADD_SIGNAL(MethodInfo("paste_request", PropertyInfo(Variant::FLOAT, "offset"), PropertyInfo(Variant::BOOL, "is_offset_valid"))); ADD_SIGNAL(MethodInfo("delete_request")); } @@ -5158,6 +5158,7 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) { undo_redo->create_action(TTR("Add Position Key")); undo_redo->add_do_method(animation.ptr(), "position_track_insert_key", p_track, p_ofs, pos); + undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation); undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs); undo_redo->commit_action(); @@ -5178,6 +5179,7 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) { undo_redo->create_action(TTR("Add Rotation Key")); undo_redo->add_do_method(animation.ptr(), "rotation_track_insert_key", p_track, p_ofs, rot); + undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation); undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs); undo_redo->commit_action(); @@ -5196,6 +5198,7 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) { undo_redo->create_action(TTR("Add Scale Key")); undo_redo->add_do_method(animation.ptr(), "scale_track_insert_key", p_track, p_ofs, base->get_scale()); + undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation); undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs); undo_redo->commit_action(); @@ -5241,6 +5244,7 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) { undo_redo->create_action(TTR("Add Track Key")); undo_redo->add_do_method(animation.ptr(), "track_insert_key", p_track, p_ofs, arr); + undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation); undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs); undo_redo->commit_action(); @@ -5253,6 +5257,7 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) { undo_redo->create_action(TTR("Add Track Key")); undo_redo->add_do_method(animation.ptr(), "track_insert_key", p_track, p_ofs, ak); + undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation); undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs); undo_redo->commit_action(); } break; @@ -5261,6 +5266,7 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) { undo_redo->create_action(TTR("Add Track Key")); undo_redo->add_do_method(animation.ptr(), "track_insert_key", p_track, p_ofs, anim); + undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation); undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", p_track, p_ofs); undo_redo->commit_action(); } break; @@ -5301,6 +5307,7 @@ void AnimationTrackEditor::_add_method_key(const String &p_method) { EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->create_action(TTR("Add Method Track Key")); undo_redo->add_do_method(animation.ptr(), "track_insert_key", insert_key_from_track_call_track, insert_key_from_track_call_ofs, d); + undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation); undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", insert_key_from_track_call_track, insert_key_from_track_call_ofs); undo_redo->commit_action(); @@ -5711,7 +5718,7 @@ void AnimationTrackEditor::_bezier_track_set_key_handle_mode(Animation *p_anim, p_anim->bezier_track_set_key_handle_mode(p_track, p_index, p_mode, p_set_mode); } -void AnimationTrackEditor::_anim_duplicate_keys(float p_ofs, int p_track) { +void AnimationTrackEditor::_anim_duplicate_keys(float p_ofs, bool p_ofs_valid, int p_track) { if (selection.size() && animation.is_valid()) { int top_track = 0x7FFFFFFF; float top_time = 1e10; @@ -5764,7 +5771,13 @@ void AnimationTrackEditor::_anim_duplicate_keys(float p_ofs, int p_track) { const SelectedKey &sk = E->key(); float t = animation->track_get_key_time(sk.track, sk.key); - float insert_pos = p_ofs >= 0 ? p_ofs : timeline->get_play_position(); + float insert_pos = p_ofs_valid ? p_ofs : timeline->get_play_position(); + + if (p_ofs_valid) { + if (snap->is_pressed() && step->get_value() != 0) { + insert_pos = snap_time(insert_pos); + } + } float dst_time = t + (insert_pos - top_time); int dst_track = sk.track + (start_track - top_track); @@ -5871,7 +5884,7 @@ void AnimationTrackEditor::_set_key_clipboard(int p_top_track, float p_top_time, } } -void AnimationTrackEditor::_anim_paste_keys(float p_ofs, int p_track) { +void AnimationTrackEditor::_anim_paste_keys(float p_ofs, bool p_ofs_valid, int p_track) { if (is_key_clipboard_active() && animation.is_valid()) { int start_track = p_track; if (p_track == -1) { // Pasting from shortcut or Edit menu. @@ -5906,7 +5919,13 @@ void AnimationTrackEditor::_anim_paste_keys(float p_ofs, int p_track) { for (int i = 0; i < key_clipboard.keys.size(); i++) { const KeyClipboard::Key key = key_clipboard.keys[i]; - float insert_pos = p_ofs >= 0 ? p_ofs : timeline->get_play_position(); + float insert_pos = p_ofs_valid ? p_ofs : timeline->get_play_position(); + + if (p_ofs_valid) { + if (snap->is_pressed() && step->get_value() != 0) { + insert_pos = snap_time(insert_pos); + } + } float dst_time = key.time + insert_pos; int dst_track = key.track + start_track; @@ -6503,10 +6522,10 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) { case EDIT_DUPLICATE_SELECTED_KEYS: { if (bezier_edit->is_visible()) { - bezier_edit->duplicate_selected_keys(-1.0); + bezier_edit->duplicate_selected_keys(-1.0, false); break; } - _anim_duplicate_keys(-1.0, -1.0); + _anim_duplicate_keys(-1.0, false, -1.0); } break; case EDIT_CUT_KEYS: { if (bezier_edit->is_visible()) { @@ -6523,7 +6542,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) { _anim_copy_keys(false); } break; case EDIT_PASTE_KEYS: { - _anim_paste_keys(-1.0, -1.0); + _anim_paste_keys(-1.0, false, -1.0); } break; case EDIT_MOVE_FIRST_SELECTED_KEY_TO_CURSOR: { if (moving_selection || selection.is_empty()) { diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h index d2d8462dbc1..49a18409d05 100644 --- a/editor/animation_track_editor.h +++ b/editor/animation_track_editor.h @@ -585,13 +585,13 @@ class AnimationTrackEditor : public VBoxContainer { void _cleanup_animation(Ref p_animation); - void _anim_duplicate_keys(float p_ofs, int p_track); + void _anim_duplicate_keys(float p_ofs, bool p_ofs_valid, int p_track); void _anim_copy_keys(bool p_cut); bool _is_track_compatible(int p_target_track_idx, Variant::Type p_source_value_type, Animation::TrackType p_source_track_type); - void _anim_paste_keys(float p_ofs, int p_track); + void _anim_paste_keys(float p_ofs, bool p_ofs_valid, int p_track); void _view_group_toggle(); Button *view_group = nullptr;