summaryrefslogtreecommitdiffstats
path: root/editor/animation_track_editor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'editor/animation_track_editor.cpp')
-rw-r--r--editor/animation_track_editor.cpp203
1 files changed, 174 insertions, 29 deletions
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index 6f1439a91f..bec95d40c6 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -1305,7 +1305,11 @@ void AnimationTimelineEdit::_zoom_changed(double) {
}
float AnimationTimelineEdit::get_zoom_scale() const {
- float zv = zoom->get_max() - zoom->get_value();
+ return _get_zoom_scale(zoom->get_value());
+}
+
+float AnimationTimelineEdit::_get_zoom_scale(double p_zoom_value) const {
+ float zv = zoom->get_max() - p_zoom_value;
if (zv < 1) {
zv = 1.0 - zv;
return Math::pow(1.0f + zv, 8.0f) * 100;
@@ -1633,6 +1637,68 @@ void AnimationTimelineEdit::set_zoom(Range *p_zoom) {
zoom->connect("value_changed", callable_mp(this, &AnimationTimelineEdit::_zoom_changed));
}
+void AnimationTimelineEdit::auto_fit() {
+ if (!animation.is_valid()) {
+ return;
+ }
+
+ float anim_end = animation->get_length();
+ float anim_start = 0;
+
+ // Search for keyframe outside animation boundaries to include keyframes before animation start and after animation length.
+ int track_count = animation->get_track_count();
+ for (int track = 0; track < track_count; ++track) {
+ for (int i = 0; i < animation->track_get_key_count(track); i++) {
+ float key_time = animation->track_get_key_time(track, i);
+ if (key_time > anim_end) {
+ anim_end = key_time;
+ }
+ if (key_time < anim_start) {
+ anim_start = key_time;
+ }
+ }
+ }
+
+ float anim_length = anim_end - anim_start;
+ int timeline_width_pixels = get_size().width - get_buttons_width() - get_name_limit();
+
+ // I want a little buffer at the end... (5% looks nice and we should keep some space for the bezier handles)
+ timeline_width_pixels *= 0.95;
+
+ // The technique is to reuse the _get_zoom_scale function directly to be sure that the auto_fit is always calculated
+ // the same way as the zoom slider. It's a little bit more calculation then doing the inverse of get_zoom_scale but
+ // it's really easier to understand and should always be accurate.
+ float new_zoom = zoom->get_max();
+ while (true) {
+ double test_zoom_scale = _get_zoom_scale(new_zoom);
+
+ if (anim_length * test_zoom_scale <= timeline_width_pixels) {
+ // It fits...
+ break;
+ }
+
+ new_zoom -= zoom->get_step();
+
+ if (new_zoom <= zoom->get_min()) {
+ new_zoom = zoom->get_min();
+ break;
+ }
+ }
+
+ // Horizontal scroll to get_min which should include keyframes that are before the animation start.
+ hscroll->set_value(hscroll->get_min());
+ // Set the zoom value... the signal value_changed will be emitted and the timeline will be refreshed correctly!
+ zoom->set_value(new_zoom);
+ // The new zoom value must be applied correctly so the scrollbar are updated before we move the scrollbar to
+ // the beginning of the animation, hence the call deferred.
+ callable_mp(this, &AnimationTimelineEdit::_scroll_to_start).call_deferred();
+}
+
+void AnimationTimelineEdit::_scroll_to_start() {
+ // Horizontal scroll to get_min which should include keyframes that are before the animation start.
+ hscroll->set_value(hscroll->get_min());
+}
+
void AnimationTimelineEdit::set_track_edit(AnimationTrackEdit *p_track_edit) {
track_edit = p_track_edit;
}
@@ -2001,13 +2067,13 @@ void AnimationTrackEdit::_notification(int p_what) {
for (int i = 0; i < animation->track_get_key_count(track); i++) {
float offset = animation->track_get_key_time(track, i) - timeline->get_value();
if (editor->is_key_selected(track, i) && editor->is_moving_selection()) {
- offset = editor->snap_time(offset + editor->get_moving_selection_offset(), true);
+ offset = offset + editor->get_moving_selection_offset();
}
offset = offset * scale + limit;
if (i < animation->track_get_key_count(track) - 1) {
float offset_n = animation->track_get_key_time(track, i + 1) - timeline->get_value();
if (editor->is_key_selected(track, i + 1) && editor->is_moving_selection()) {
- offset_n = editor->snap_time(offset_n + editor->get_moving_selection_offset());
+ offset_n = offset_n + editor->get_moving_selection_offset();
}
offset_n = offset_n * scale + limit;
@@ -2688,7 +2754,7 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &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();
}
@@ -2707,7 +2773,7 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &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();
}
@@ -2908,8 +2974,10 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
if (mb.is_valid() && moving_selection_attempt) {
if (!mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
moving_selection_attempt = false;
- if (moving_selection) {
- emit_signal(SNAME("move_selection_commit"));
+ if (moving_selection && moving_selection_effective) {
+ if (abs(editor->get_moving_selection_offset()) > CMP_EPSILON) {
+ emit_signal(SNAME("move_selection_commit"));
+ }
} else if (select_single_attempt != -1) {
emit_signal(SNAME("select_key"), select_single_attempt, true);
}
@@ -2982,8 +3050,18 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
emit_signal(SNAME("move_selection_begin"));
}
- float new_ofs = (mm->get_position().x - timeline->get_name_limit()) / timeline->get_zoom_scale();
- emit_signal(SNAME("move_selection"), new_ofs - moving_selection_from_ofs);
+ float moving_begin_time = ((moving_selection_mouse_begin_x - timeline->get_name_limit()) / timeline->get_zoom_scale()) + timeline->get_value();
+ float new_time = ((mm->get_position().x - timeline->get_name_limit()) / timeline->get_zoom_scale()) + timeline->get_value();
+ float delta = new_time - moving_begin_time;
+ float snapped_time = editor->snap_time(moving_selection_pivot + delta);
+
+ float offset = 0.0;
+ if (abs(editor->get_moving_selection_offset()) > CMP_EPSILON || (snapped_time > moving_selection_pivot && delta > CMP_EPSILON) || (snapped_time < moving_selection_pivot && delta < -CMP_EPSILON)) {
+ offset = snapped_time - moving_selection_pivot;
+ moving_selection_effective = true;
+ }
+
+ emit_signal(SNAME("move_selection"), offset);
}
}
@@ -3026,12 +3104,16 @@ bool AnimationTrackEdit::_try_select_at_ui_pos(const Point2 &p_pos, bool p_aggre
if (editor->is_key_selected(track, key_idx)) {
if (p_deselectable) {
emit_signal(SNAME("deselect_key"), key_idx);
+ moving_selection_pivot = 0.0f;
+ moving_selection_mouse_begin_x = 0.0f;
}
} else {
emit_signal(SNAME("select_key"), key_idx, false);
moving_selection_attempt = true;
+ moving_selection_effective = false;
select_single_attempt = -1;
- moving_selection_from_ofs = (p_pos.x - limit) / timeline->get_zoom_scale();
+ moving_selection_pivot = animation->track_get_key_time(track, key_idx);
+ moving_selection_mouse_begin_x = p_pos.x;
}
} else {
if (!editor->is_key_selected(track, key_idx)) {
@@ -3042,12 +3124,15 @@ bool AnimationTrackEdit::_try_select_at_ui_pos(const Point2 &p_pos, bool p_aggre
}
moving_selection_attempt = true;
- moving_selection_from_ofs = (p_pos.x - limit) / timeline->get_zoom_scale();
+ moving_selection_effective = false;
+ moving_selection_pivot = animation->track_get_key_time(track, key_idx);
+ moving_selection_mouse_begin_x = p_pos.x;
}
if (read_only) {
moving_selection_attempt = false;
- moving_selection_from_ofs = 0.0f;
+ moving_selection_pivot = 0.0f;
+ moving_selection_mouse_begin_x = 0.0f;
}
return true;
}
@@ -3182,7 +3267,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"));
@@ -3191,7 +3276,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"));
@@ -3265,11 +3350,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"));
}
@@ -3446,6 +3531,8 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim, bool p_re
step->set_read_only(false);
snap->set_disabled(false);
snap_mode->set_disabled(false);
+ auto_fit->set_disabled(false);
+ auto_fit_bezier->set_disabled(false);
imported_anim_warning->hide();
for (int i = 0; i < animation->get_track_count(); i++) {
@@ -3466,6 +3553,8 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim, bool p_re
snap->set_disabled(true);
snap_mode->set_disabled(true);
bezier_edit_icon->set_disabled(true);
+ auto_fit->set_disabled(true);
+ auto_fit_bezier->set_disabled(true);
}
}
@@ -4763,6 +4852,8 @@ void AnimationTrackEditor::_notification(int p_what) {
inactive_player_warning->set_icon(get_editor_theme_icon(SNAME("NodeWarning")));
main_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("Tree")));
edit->get_popup()->set_item_icon(edit->get_popup()->get_item_index(EDIT_APPLY_RESET), get_editor_theme_icon(SNAME("Reload")));
+ auto_fit->set_icon(get_editor_theme_icon(SNAME("AnimationAutoFit")));
+ auto_fit_bezier->set_icon(get_editor_theme_icon(SNAME("AnimationAutoFitBezier")));
} break;
case NOTIFICATION_READY: {
@@ -4915,12 +5006,17 @@ void AnimationTrackEditor::_new_track_node_selected(NodePath p_path) {
}
void AnimationTrackEditor::_add_track(int p_type) {
- if (!root) {
+ AnimationPlayer *ap = AnimationPlayerEditor::get_singleton()->get_player();
+ if (!ap) {
+ ERR_FAIL_EDMSG("No AnimationPlayer is currently being edited.");
+ }
+ Node *root_node = ap->get_node_or_null(ap->get_root_node());
+ if (!root_node) {
EditorNode::get_singleton()->show_warning(TTR("Not possible to add a new track without a root"));
return;
}
adding_track_type = p_type;
- pick_track->popup_scenetree_dialog();
+ pick_track->popup_scenetree_dialog(nullptr, root_node);
pick_track->get_filter_line_edit()->clear();
pick_track->get_filter_line_edit()->grab_focus();
}
@@ -5086,6 +5182,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();
@@ -5106,6 +5203,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();
@@ -5124,6 +5222,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();
@@ -5169,6 +5268,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();
@@ -5181,6 +5281,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;
@@ -5189,6 +5290,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;
@@ -5229,6 +5331,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();
@@ -5427,7 +5530,7 @@ void AnimationTrackEditor::_move_selection_commit() {
}
// 2 - Remove overlapped keys.
for (RBMap<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
- float newtime = snap_time(E->get().pos + motion);
+ float newtime = E->get().pos + motion;
int idx = animation->track_find_key(E->key().track, newtime, Animation::FIND_MODE_APPROX);
if (idx == -1) {
continue;
@@ -5452,13 +5555,13 @@ void AnimationTrackEditor::_move_selection_commit() {
// 3 - Move the keys (Reinsert them).
for (RBMap<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
- float newpos = snap_time(E->get().pos + motion);
+ float newpos = E->get().pos + motion;
undo_redo->add_do_method(animation.ptr(), "track_insert_key", E->key().track, newpos, animation->track_get_key_value(E->key().track, E->key().key), animation->track_get_key_transition(E->key().track, E->key().key));
}
// 4 - (Undo) Remove inserted keys.
for (RBMap<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
- float newpos = snap_time(E->get().pos + motion);
+ float newpos = E->get().pos + motion;
undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", E->key().track, newpos);
}
@@ -5478,7 +5581,7 @@ void AnimationTrackEditor::_move_selection_commit() {
// 7 - Reselect.
for (RBMap<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) {
float oldpos = E->get().pos;
- float newpos = snap_time(oldpos + motion);
+ float newpos = oldpos + motion;
undo_redo->add_do_method(this, "_select_at_anim", animation, E->key().track, newpos);
undo_redo->add_undo_method(this, "_select_at_anim", animation, E->key().track, oldpos);
@@ -5617,6 +5720,8 @@ void AnimationTrackEditor::_cancel_bezier_edit() {
bezier_edit->hide();
scroll->show();
bezier_edit_icon->set_pressed(false);
+ auto_fit->show();
+ auto_fit_bezier->hide();
}
void AnimationTrackEditor::_bezier_edit(int p_for_track) {
@@ -5625,6 +5730,8 @@ void AnimationTrackEditor::_bezier_edit(int p_for_track) {
bezier_edit->set_animation_and_track(animation, p_for_track, read_only);
scroll->hide();
bezier_edit->show();
+ auto_fit->hide();
+ auto_fit_bezier->show();
// Search everything within the track and curve - edit it.
}
@@ -5635,7 +5742,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;
@@ -5688,7 +5795,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);
@@ -5795,7 +5908,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.
@@ -5830,7 +5943,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;
@@ -6427,10 +6546,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()) {
@@ -6447,7 +6566,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()) {
@@ -6865,6 +6984,18 @@ bool AnimationTrackEditor::is_grouping_tracks() {
return !view_group->is_pressed();
}
+void AnimationTrackEditor::_auto_fit() {
+ timeline->auto_fit();
+}
+
+void AnimationTrackEditor::_auto_fit_bezier() {
+ timeline->auto_fit();
+
+ if (bezier_edit->is_visible()) {
+ bezier_edit->auto_fit_vertically();
+ }
+}
+
void AnimationTrackEditor::_selection_changed() {
if (selected_filter->is_pressed()) {
_update_tracks(); // Needs updatin.
@@ -7179,6 +7310,19 @@ AnimationTrackEditor::AnimationTrackEditor() {
bottom_hb->add_child(zoom);
timeline->set_zoom(zoom);
+ auto_fit = memnew(Button);
+ auto_fit->set_flat(true);
+ auto_fit->connect("pressed", callable_mp(this, &AnimationTrackEditor::_auto_fit));
+ auto_fit->set_shortcut(ED_SHORTCUT("animation_editor/auto_fit", TTR("Fit to panel"), KeyModifierMask::ALT | Key::F));
+ bottom_hb->add_child(auto_fit);
+
+ auto_fit_bezier = memnew(Button);
+ auto_fit_bezier->set_flat(true);
+ auto_fit_bezier->set_visible(false);
+ auto_fit_bezier->connect("pressed", callable_mp(this, &AnimationTrackEditor::_auto_fit_bezier));
+ auto_fit_bezier->set_shortcut(ED_SHORTCUT("animation_editor/auto_fit", TTR("Fit to panel"), KeyModifierMask::ALT | Key::F));
+ bottom_hb->add_child(auto_fit_bezier);
+
edit = memnew(MenuButton);
edit->set_shortcut_context(this);
edit->set_text(TTR("Edit"));
@@ -7430,6 +7574,7 @@ AnimationTrackEditor::AnimationTrackEditor() {
track_copy_vbox->add_child(select_all_button);
track_copy_select = memnew(Tree);
+ track_copy_select->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
track_copy_select->set_h_size_flags(SIZE_EXPAND_FILL);
track_copy_select->set_v_size_flags(SIZE_EXPAND_FILL);
track_copy_select->set_hide_root(true);