From c5490f72843e6bfdebe898103e3e4f5e90f6e169 Mon Sep 17 00:00:00 2001
From: shadow-foss <144371189+shadow-foss@users.noreply.github.com>
Date: Sat, 14 Jun 2025 03:43:11 +0530
Subject: [PATCH] Add toggle to insert keys/markers at current time or mouse
position
Adds a new editor setting editors/animation/insert_at_current_time and a toggle button in the Animation Track Editor to let users choose whether to insert keys and markers at the current timeline cursor (when enabled) or at the mouse position (default behavior).
- Key insertion
- Paste and duplicate operations
- Editor setting persistence
- Icon by @TokageItLab
Fixes #103272
---
doc/classes/EditorSettings.xml | 4 +++
editor/animation/animation_track_editor.cpp | 31 +++++++++++++++++++--
editor/animation/animation_track_editor.h | 3 ++
editor/icons/InsertAtCurrentTime.svg | 1 +
editor/settings/editor_settings.cpp | 1 +
5 files changed, 38 insertions(+), 2 deletions(-)
create mode 100644 editor/icons/InsertAtCurrentTime.svg
diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml
index 4bfa4a57029..28360544e4f 100644
--- a/doc/classes/EditorSettings.xml
+++ b/doc/classes/EditorSettings.xml
@@ -547,6 +547,10 @@
Default step mode for [AnimationPlayer] (seconds or FPS). The option is remembered locally for a scene and this option only determines the default value when scene doesn't have local state yet.
+
+ If [code]true[/code], animation keys and markers are inserted at the current time in the animation.
+ If [code]false[/code], they are inserted at the mouse cursor's position.
+
The modulate color to use for "future" frames displayed in the animation editor's onion skinning feature.
diff --git a/editor/animation/animation_track_editor.cpp b/editor/animation/animation_track_editor.cpp
index 49249eefd40..96775cd6957 100644
--- a/editor/animation/animation_track_editor.cpp
+++ b/editor/animation/animation_track_editor.cpp
@@ -3596,7 +3596,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, true);
+ emit_signal(SNAME("duplicate_request"), insert_at_pos, !editor->is_insert_at_current_time_enabled());
} break;
case MENU_KEY_CUT: {
emit_signal(SNAME("cut_request"));
@@ -3605,7 +3605,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, true);
+ emit_signal(SNAME("paste_request"), insert_at_pos, !editor->is_insert_at_current_time_enabled());
} break;
case MENU_KEY_ADD_RESET: {
emit_signal(SNAME("create_reset_request"));
@@ -3948,6 +3948,7 @@ void AnimationTrackEditor::set_animation(const Ref &p_anim, bool p_re
step->set_read_only(false);
snap_keys->set_disabled(false);
snap_timeline->set_disabled(false);
+ insert_at_current_time->set_disabled(false);
fps_compat->set_disabled(false);
snap_mode->set_disabled(false);
auto_fit->set_disabled(false);
@@ -3971,6 +3972,7 @@ void AnimationTrackEditor::set_animation(const Ref &p_anim, bool p_re
step->set_read_only(true);
snap_keys->set_disabled(true);
snap_timeline->set_disabled(true);
+ insert_at_current_time->set_disabled(true);
fps_compat->set_disabled(true);
snap_mode->set_disabled(true);
bezier_edit_icon->set_disabled(true);
@@ -4930,6 +4932,16 @@ bool AnimationTrackEditor::is_snap_keys_enabled() const {
return snap_keys->is_pressed() ^ Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL);
}
+bool AnimationTrackEditor::is_insert_at_current_time_enabled() const {
+ return insert_at_current_time->is_pressed();
+}
+
+void AnimationTrackEditor::resolve_insertion_offset(float &r_offset) const {
+ if (is_insert_at_current_time_enabled()) {
+ r_offset = timeline->get_play_position();
+ }
+}
+
bool AnimationTrackEditor::is_bezier_editor_active() const {
return bezier_edit->is_visible();
}
@@ -5342,6 +5354,7 @@ void AnimationTrackEditor::_notification(int p_what) {
bezier_edit_icon->set_button_icon(get_editor_theme_icon(SNAME("EditBezier")));
snap_timeline->set_button_icon(get_editor_theme_icon(SNAME("SnapTimeline")));
snap_keys->set_button_icon(get_editor_theme_icon(SNAME("SnapKeys")));
+ insert_at_current_time->set_button_icon(get_editor_theme_icon(SNAME("InsertAtCurrentTime")));
fps_compat->set_button_icon(get_editor_theme_icon(SNAME("FPS")));
view_group->set_button_icon(get_editor_theme_icon(view_group->is_pressed() ? SNAME("AnimationTrackList") : SNAME("AnimationTrackGroup")));
function_name_toggler->set_button_icon(get_editor_theme_icon(SNAME("MemberMethod")));
@@ -5696,6 +5709,9 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) {
if (snap_keys->is_pressed() && step->get_value() != 0) {
p_ofs = snap_time(p_ofs);
}
+
+ resolve_insertion_offset(p_ofs);
+
while (animation->track_find_key(p_track, p_ofs, Animation::FIND_MODE_APPROX) != -1) { // Make sure insertion point is valid.
p_ofs += SECOND_DECIMAL;
}
@@ -7909,6 +7925,15 @@ AnimationTrackEditor::AnimationTrackEditor() {
view_group->set_tooltip_text(TTR("Group tracks by node or display them as plain list."));
bottom_hf->add_child(view_group);
+
+ insert_at_current_time = memnew(Button);
+ insert_at_current_time->set_flat(true);
+ bottom_hf->add_child(insert_at_current_time);
+ insert_at_current_time->set_disabled(true);
+ insert_at_current_time->set_toggle_mode(true);
+ insert_at_current_time->set_pressed(EDITOR_GET("editors/animation/insert_at_current_time"));
+ insert_at_current_time->set_tooltip_text(TTRC("Insert at current time."));
+
bottom_hf->add_child(memnew(VSeparator));
snap_timeline = memnew(Button);
@@ -9222,6 +9247,8 @@ void AnimationMarkerEdit::_insert_marker(float p_ofs) {
p_ofs = editor->snap_time(p_ofs);
}
+ editor->resolve_insertion_offset(p_ofs);
+
marker_insert_confirm->popup_centered(Size2(200, 100) * EDSCALE);
marker_insert_color->set_pick_color(Color(1, 1, 1));
diff --git a/editor/animation/animation_track_editor.h b/editor/animation/animation_track_editor.h
index 50c0f38bf28..c6458f9df71 100644
--- a/editor/animation/animation_track_editor.h
+++ b/editor/animation/animation_track_editor.h
@@ -613,6 +613,7 @@ class AnimationTrackEditor : public VBoxContainer {
Label *nearest_fps_label = nullptr;
TextureRect *zoom_icon = nullptr;
Button *snap_keys = nullptr;
+ Button *insert_at_current_time = nullptr;
Button *snap_timeline = nullptr;
Button *bezier_edit_icon = nullptr;
OptionButton *snap_mode = nullptr;
@@ -954,6 +955,8 @@ public:
bool is_moving_selection() const;
bool is_snap_timeline_enabled() const;
bool is_snap_keys_enabled() const;
+ bool is_insert_at_current_time_enabled() const;
+ void resolve_insertion_offset(float &r_offset) const;
bool is_bezier_editor_active() const;
bool can_add_reset_key() const;
void _on_filter_updated(const String &p_filter);
diff --git a/editor/icons/InsertAtCurrentTime.svg b/editor/icons/InsertAtCurrentTime.svg
new file mode 100644
index 00000000000..cd808852b43
--- /dev/null
+++ b/editor/icons/InsertAtCurrentTime.svg
@@ -0,0 +1 @@
+
diff --git a/editor/settings/editor_settings.cpp b/editor/settings/editor_settings.cpp
index 6175cc57039..d14cca42f26 100644
--- a/editor/settings/editor_settings.cpp
+++ b/editor/settings/editor_settings.cpp
@@ -997,6 +997,7 @@ void EditorSettings::_load_defaults(Ref p_extra_config) {
_initial_set("editors/animation/confirm_insert_track", true, true);
_initial_set("editors/animation/default_create_bezier_tracks", false, true);
_initial_set("editors/animation/default_create_reset_tracks", true, true);
+ _initial_set("editors/animation/insert_at_current_time", false, true);
_initial_set("editors/animation/onion_layers_past_color", Color(1, 0, 0));
_initial_set("editors/animation/onion_layers_future_color", Color(0, 1, 0));