1
0
Fork 0

Merge pull request #100470 from YeldhamDev/i_hate_math_so_much_you_would_not_believe

Add selection box movement/scaling to the animation bezier editor
This commit is contained in:
Rémi Verschelde 2025-03-21 13:03:08 +01:00
commit 027cc80b76
No known key found for this signature in database
GPG Key ID: C3336907360768E1
2 changed files with 353 additions and 33 deletions

View File

@ -60,8 +60,12 @@ void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) {
for (int i = 0; i < animation->track_get_key_count(p_track); i++) {
real_t ofs = animation->track_get_key_time(p_track, i);
if (moving_selection && selection.has(IntPair(p_track, i))) {
ofs += moving_selection_offset.x;
if (selection.has(IntPair(p_track, i))) {
if (moving_selection) {
ofs += moving_selection_offset.x;
} else if (scaling_selection) {
ofs += -scaling_selection_offset.x + (ofs - scaling_selection_pivot.x) * (scaling_selection_scale.x - 1);
}
}
key_order[ofs] = i;
@ -83,9 +87,14 @@ void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) {
out_handle = moving_handle_right;
}
if (moving_selection && selection.has(IntPair(p_track, i))) {
offset += moving_selection_offset.x;
height += moving_selection_offset.y;
if (selection.has(IntPair(p_track, i))) {
if (moving_selection) {
offset += moving_selection_offset.x;
height += moving_selection_offset.y;
} else if (scaling_selection) {
offset += -scaling_selection_offset.x + (offset - scaling_selection_pivot.x) * (scaling_selection_scale.x - 1);
height += -scaling_selection_offset.y + (height - scaling_selection_pivot.y) * (scaling_selection_scale.y - 1);
}
}
out_handle += Vector2(offset, height);
@ -97,9 +106,14 @@ void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) {
in_handle = moving_handle_left;
}
if (moving_selection && selection.has(IntPair(p_track, i_n))) {
offset_n += moving_selection_offset.x;
height_n += moving_selection_offset.y;
if (selection.has(IntPair(p_track, i_n))) {
if (moving_selection) {
offset_n += moving_selection_offset.x;
height_n += moving_selection_offset.y;
} else if (scaling_selection) {
offset_n += -scaling_selection_offset.x + (offset_n - scaling_selection_pivot.x) * (scaling_selection_scale.x - 1);
height_n += -scaling_selection_offset.y + (height_n - scaling_selection_pivot.y) * (scaling_selection_scale.y - 1);
}
}
in_handle += Vector2(offset_n, height_n);
@ -506,29 +520,46 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
}
}
const bool draw_selection_handles = selection.size() > 1;
LocalVector<Point2> selected_pos;
// Draw editor handles.
{
edit_points.clear();
float scale = timeline->get_zoom_scale();
for (int i = 0; i < track_count; ++i) {
if (!_is_track_curves_displayed(i) || locked_tracks.has(i)) {
bool draw_track = _is_track_curves_displayed(i) && !locked_tracks.has(i);
if (!draw_selection_handles && !draw_track) {
continue;
}
int key_count = animation->track_get_key_count(i);
for (int j = 0; j < key_count; ++j) {
float offset = animation->track_get_key_time(i, j);
float value = animation->bezier_track_get_key_value(i, j);
bool is_selected = selection.has(IntPair(i, j));
if (moving_selection && selection.has(IntPair(i, j))) {
offset += moving_selection_offset.x;
value += moving_selection_offset.y;
if (is_selected) {
if (moving_selection) {
offset += moving_selection_offset.x;
value += moving_selection_offset.y;
} else if (scaling_selection) {
offset += -scaling_selection_offset.x + (offset - scaling_selection_pivot.x) * (scaling_selection_scale.x - 1);
value += -scaling_selection_offset.y + (value - scaling_selection_pivot.y) * (scaling_selection_scale.y - 1);
}
}
Vector2 pos((offset - timeline->get_value()) * scale + limit, _bezier_h_to_pixel(value));
if (draw_selection_handles && is_selected) {
selected_pos.push_back(pos);
}
if (!draw_track) {
continue;
}
Vector2 in_vec = animation->bezier_track_get_key_in_handle(i, j);
if ((moving_handle == 1 || moving_handle == -1) && moving_handle_track == i && moving_handle_key == j) {
@ -544,7 +575,7 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
Vector2 pos_out(((offset + out_vec.x) - timeline->get_value()) * scale + limit, _bezier_h_to_pixel(value + out_vec.y));
if (i == selected_track || selection.has(IntPair(i, j))) {
if (i == selected_track || is_selected) {
_draw_line_clipped(pos, pos_in, accent, limit, right_limit);
_draw_line_clipped(pos, pos_out, accent, limit, right_limit);
}
@ -555,7 +586,7 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
if (pos.x >= limit && pos.x <= right_limit) {
ep.point_rect.position = (pos - bezier_icon->get_size() / 2.0).floor();
ep.point_rect.size = bezier_icon->get_size();
if (selection.has(IntPair(i, j))) {
if (is_selected) {
draw_texture(selected_icon, ep.point_rect.position);
draw_string(font, ep.point_rect.position + Vector2(8, -font->get_height(font_size) - 8), TTR("Time:") + " " + TS->format_number(rtos(Math::snapped(offset, 0.0001))), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, accent);
draw_string(font, ep.point_rect.position + Vector2(8, -8), TTR("Value:") + " " + TS->format_number(rtos(Math::snapped(value, 0.001))), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, accent);
@ -570,7 +601,7 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
}
ep.point_rect = ep.point_rect.grow(ep.point_rect.size.width * 0.5);
if (i == selected_track || selection.has(IntPair(i, j))) {
if (i == selected_track || is_selected) {
if (animation->bezier_track_get_key_handle_mode(i, j) != Animation::HANDLE_MODE_LINEAR) {
if (pos_in.x >= limit && pos_in.x <= right_limit) {
ep.in_rect.position = (pos_in - bezier_handle_icon->get_size() / 2.0).floor();
@ -601,6 +632,34 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
}
}
selection_rect = Rect2();
selection_handles_rect = Rect2();
// Draw scale handles.
if (draw_selection_handles) {
selection_rect.position = selected_pos[0];
selected_pos.remove_at(0);
for (const Point2 &pos : selected_pos) {
selection_rect = selection_rect.expand(pos);
}
const int outer_ofs = Math::round(12 * EDSCALE);
const int inner_ofs = Math::round(outer_ofs / 2.0);
// Draw horizontal handles.
if (selection_rect.size.height > CMP_EPSILON) {
_draw_line_clipped(selection_rect.position - Vector2(inner_ofs, inner_ofs), selection_rect.position + Vector2(selection_rect.size.width + inner_ofs, -inner_ofs), accent, limit, right_limit);
_draw_line_clipped(selection_rect.position + Vector2(-inner_ofs, selection_rect.size.height + inner_ofs), selection_rect.position + selection_rect.size + Vector2(inner_ofs, inner_ofs), accent, limit, right_limit);
}
// Draw vertical handles.
if (selection_rect.size.width > CMP_EPSILON) {
_draw_line_clipped(selection_rect.position - Vector2(inner_ofs, inner_ofs), selection_rect.position + Vector2(-inner_ofs, selection_rect.size.height + inner_ofs), accent, limit, right_limit);
_draw_line_clipped(selection_rect.position + Vector2(selection_rect.size.width + inner_ofs, -inner_ofs), selection_rect.position + selection_rect.size + Vector2(inner_ofs, inner_ofs), accent, limit, right_limit);
}
selection_handles_rect.position = selection_rect.position - Vector2(outer_ofs, outer_ofs);
selection_handles_rect.size = selection_rect.size + Vector2(outer_ofs, outer_ofs) * 2;
}
if (box_selecting) {
Vector2 bs_from = box_selection_from;
Vector2 bs_to = box_selection_to;
@ -1193,15 +1252,17 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
}
}
// Check this first, to allow manipulating key handles while ignoring keyframes before scaling/moving.
bool inside_selection_handles_rect = !read_only && selection_handles_rect.has_point(mb->get_position());
// First, check keyframe.
// Command/Control makes it ignore the keyframe, so control point editors can be force-edited.
if (!mb->is_command_or_control_pressed()) {
if (!inside_selection_handles_rect && !mb->is_command_or_control_pressed()) {
if (_try_select_at_ui_pos(mb->get_position(), mb->is_shift_pressed(), true)) {
return;
}
}
// Second, check handles.
// Second, check key handles.
for (int i = 0; i < edit_points.size(); i++) {
if (!read_only) {
if (edit_points[i].in_rect.has_point(mb->get_position())) {
@ -1226,6 +1287,50 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
}
}
// Box scaling/movement.
if (inside_selection_handles_rect) {
const Vector2i rel_pos = mb->get_position() - selection_rect.position;
scaling_selection_handles = Vector2i();
// Check which scaling handles are available.
if (selection_rect.size.width > CMP_EPSILON) {
if (rel_pos.x <= 0) {
scaling_selection_handles.x = -1;
} else if (rel_pos.x >= selection_rect.size.width) {
scaling_selection_handles.x = 1;
}
}
if (selection_rect.size.height > CMP_EPSILON) {
if (rel_pos.y <= 0) {
scaling_selection_handles.y = -1;
} else if (rel_pos.y >= selection_rect.size.height) {
scaling_selection_handles.y = 1;
}
}
if (scaling_selection_handles != Vector2i()) {
scaling_selection = true;
const float time = ((selection_rect.position.x - limit) / timeline->get_zoom_scale()) + timeline->get_value();
const float h = (get_size().height / 2.0 - selection_rect.position.y) * timeline_v_zoom + timeline_v_scroll;
scaling_selection_pivot = Point2(time, h);
return;
}
// If not scaling, that means we're moving.
moving_selection_attempt = true;
moving_selection = false;
moving_selection_mouse_begin = mb->get_position();
// The pivot will be from the mouse click location, not a specific key.
moving_selection_from_key = -1;
moving_selection_from_track = selected_track;
moving_selection_offset = Vector2();
select_single_attempt = IntPair(-1, -1);
return;
}
// Insert new point.
if (mb->get_position().x >= limit && mb->get_position().x < get_size().width && mb->is_command_or_control_pressed()) {
float h = (get_size().height / 2.0 - mb->get_position().y) * timeline_v_zoom + timeline_v_scroll;
@ -1250,7 +1355,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
moving_selection_attempt = true;
moving_selection = false;
moving_selection_mouse_begin_x = mb->get_position().x;
moving_selection_mouse_begin = mb->get_position();
moving_selection_from_key = index;
moving_selection_from_track = selected_track;
moving_selection_offset = Vector2();
@ -1285,12 +1390,12 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
if (bs_from.y > bs_to.y) {
SWAP(bs_from.y, bs_to.y);
}
Rect2 selection_rect(bs_from, bs_to - bs_from);
Rect2 rect(bs_from, bs_to - bs_from);
bool track_set = false;
int j = 0;
for (int i = 0; i < edit_points.size(); i++) {
if (edit_points[i].point_rect.intersects(selection_rect)) {
if (edit_points[i].point_rect.intersects(rect)) {
_select_at_anim(animation, edit_points[i].track, animation->track_get_key_time(edit_points[i].track, edit_points[i].key), j == 0 && !box_selecting_add);
if (!track_set) {
track_set = true;
@ -1335,8 +1440,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
if (moving_selection_attempt && mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
if (!read_only) {
if (moving_selection && (abs(moving_selection_offset.x) > CMP_EPSILON || abs(moving_selection_offset.y) > CMP_EPSILON)) {
//combit it
// Commit it.
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Move Bezier Points"));
@ -1459,11 +1563,141 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
moving_selection = false;
moving_selection_attempt = false;
moving_selection_mouse_begin_x = 0.0;
moving_selection_mouse_begin = Point2();
queue_redraw();
}
}
if (scaling_selection && mb.is_valid() && !read_only && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
if (abs(scaling_selection_scale.x - 1) > CMP_EPSILON || abs(scaling_selection_scale.y - 1) > CMP_EPSILON) {
// Scale it.
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Scale Bezier Points"));
List<AnimMoveRestore> to_restore;
List<Animation::HandleMode> to_restore_handle_modes;
// 1 - Remove the keys.
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
undo_redo->add_do_method(animation.ptr(), "track_remove_key", E->get().first, E->get().second);
}
// 2 - Remove overlapped keys.
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
real_t newtime = animation->track_get_key_time(E->get().first, E->get().second);
newtime += -scaling_selection_offset.x + (newtime - scaling_selection_pivot.x) * (scaling_selection_scale.x - 1);
int idx = animation->track_find_key(E->get().first, newtime, Animation::FIND_MODE_APPROX);
if (idx == -1) {
continue;
}
if (selection.has(IntPair(E->get().first, idx))) {
continue; // Already in selection, don't save.
}
undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_time", E->get().first, newtime);
AnimMoveRestore amr;
amr.key = animation->track_get_key_value(E->get().first, idx);
amr.track = E->get().first;
amr.time = newtime;
to_restore.push_back(amr);
to_restore_handle_modes.push_back(animation->bezier_track_get_key_handle_mode(E->get().first, idx));
}
// 3 - Scale the keys (re-insert them).
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
real_t newpos = animation->track_get_key_time(E->get().first, E->get().second);
newpos += -scaling_selection_offset.x + (newpos - scaling_selection_pivot.x) * (scaling_selection_scale.x - 1);
Array key = animation->track_get_key_value(E->get().first, E->get().second);
real_t h = key[0];
h += -scaling_selection_offset.y + (h - scaling_selection_pivot.y) * (scaling_selection_scale.y - 1);
key[0] = h;
undo_redo->add_do_method(
this,
"_bezier_track_insert_key_at_anim",
animation,
E->get().first,
newpos,
key[0],
Vector2(key[1], key[2]),
Vector2(key[3], key[4]),
animation->bezier_track_get_key_handle_mode(E->get().first, E->get().second));
}
// 4 - (undo) Remove inserted keys.
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
real_t newpos = animation->track_get_key_time(E->get().first, E->get().second);
newpos += -scaling_selection_offset.x + (newpos - scaling_selection_pivot.x) * (scaling_selection_scale.x - 1);
undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", E->get().first, newpos);
}
// 5 - (undo) Reinsert keys.
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
real_t oldpos = animation->track_get_key_time(E->get().first, E->get().second);
Array key = animation->track_get_key_value(E->get().first, E->get().second);
undo_redo->add_undo_method(
this,
"_bezier_track_insert_key_at_anim",
animation,
E->get().first,
oldpos,
key[0],
Vector2(key[1], key[2]),
Vector2(key[3], key[4]),
animation->bezier_track_get_key_handle_mode(E->get().first, E->get().second));
}
// 6 - (undo) Reinsert overlapped keys.
List<AnimMoveRestore>::ConstIterator restore_itr = to_restore.begin();
List<Animation::HandleMode>::ConstIterator handle_itr = to_restore_handle_modes.begin();
for (; restore_itr != to_restore.end() && handle_itr != to_restore_handle_modes.end(); ++restore_itr, ++handle_itr) {
const AnimMoveRestore &amr = *restore_itr;
Array key = amr.key;
undo_redo->add_undo_method(animation.ptr(), "track_insert_key", amr.track, amr.time, amr.key, 1);
undo_redo->add_undo_method(
this,
"_bezier_track_insert_key_at_anim",
animation,
amr.track,
amr.time,
key[0],
Vector2(key[1], key[2]),
Vector2(key[3], key[4]),
*handle_itr);
}
undo_redo->add_do_method(this, "_clear_selection_for_anim", animation);
undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
// 7 - Reselect.
int i = 0;
for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
real_t oldpos = animation->track_get_key_time(E->get().first, E->get().second);
real_t newpos = animation->track_get_key_time(E->get().first, E->get().second);
newpos += -scaling_selection_offset.x + (newpos - scaling_selection_pivot.x) * (scaling_selection_scale.x - 1);
undo_redo->add_do_method(this, "_select_at_anim", animation, E->get().first, newpos, i == 0);
undo_redo->add_undo_method(this, "_select_at_anim", animation, E->get().first, oldpos, i == 0);
i++;
}
AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton();
if (ape) {
undo_redo->add_do_method(ape, "_animation_update_key_frame");
undo_redo->add_undo_method(ape, "_animation_update_key_frame");
}
undo_redo->commit_action();
}
scaling_selection = false;
scaling_selection_scale = Vector2(1, 1);
scaling_selection_offset = Vector2();
queue_redraw();
}
Ref<InputEventMouseMotion> mm = p_event;
if (moving_selection_attempt && mm.is_valid()) {
if (!moving_selection) {
@ -1473,9 +1707,9 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
if (!read_only) {
float y = (get_size().height / 2.0 - mm->get_position().y) * timeline_v_zoom + timeline_v_scroll;
float moving_selection_begin_time = ((moving_selection_mouse_begin_x - limit) / timeline->get_zoom_scale()) + timeline->get_value();
float moving_selection_begin_time = ((moving_selection_mouse_begin.x - limit) / timeline->get_zoom_scale()) + timeline->get_value();
float new_time = ((mm->get_position().x - limit) / timeline->get_zoom_scale()) + timeline->get_value();
float moving_selection_pivot = animation->track_get_key_time(moving_selection_from_track, moving_selection_from_key);
float moving_selection_pivot = moving_selection_from_key != -1 ? animation->track_get_key_time(moving_selection_from_track, moving_selection_from_key) : 0;
float time_delta = new_time - moving_selection_begin_time;
float snapped_time = editor->snap_time(moving_selection_pivot + time_delta);
@ -1483,9 +1717,15 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
if (abs(moving_selection_offset.x) > CMP_EPSILON || (snapped_time > moving_selection_pivot && time_delta > CMP_EPSILON) || (snapped_time < moving_selection_pivot && time_delta < -CMP_EPSILON)) {
time_offset = snapped_time - moving_selection_pivot;
}
float moving_selection_begin_value = animation->bezier_track_get_key_value(moving_selection_from_track, moving_selection_from_key);
float y_offset = y - moving_selection_begin_value;
float moving_selection_begin_value;
if (moving_selection_from_key == -1) {
moving_selection_begin_value = (get_size().height / 2.0 - moving_selection_mouse_begin.y) * timeline_v_zoom + timeline_v_scroll;
} else {
moving_selection_begin_value = animation->bezier_track_get_key_value(moving_selection_from_track, moving_selection_from_key);
}
float y_offset = y - moving_selection_begin_value;
moving_selection_offset = Vector2(time_offset, y_offset);
}
@ -1505,11 +1745,82 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
queue_redraw();
}
if (scaling_selection && mm.is_valid() && !read_only) {
Point2 mp = mm->get_position();
const int handle_length = Math::round((selection_handles_rect.size.width - selection_rect.size.width) / 4.0);
Point2 rel_pos;
// Calculate the scale according with the distance between the mouse's position (adjusted so that the cursor appears inside the handles)
// and the opposite end of the `selection_rect`.
if (scaling_selection_handles.x != 0) {
if (scaling_selection_handles.x == 1) { // Right Handle
const int handle_adjust = Math::round(mp.x - (scaling_selection_scale.x >= 0 ? selection_rect.position.x : (selection_rect.position.x + selection_rect.size.width)));
mp.x -= MIN(Math::abs(handle_adjust), handle_length) * scaling_selection_handles.x * SIGN(handle_adjust);
if (editor->is_snap_keys_enabled()) {
mp.x = editor->snap_time((mp.x - limit) / timeline->get_zoom_scale(), true) + timeline->get_value();
mp.x = (mp.x - timeline->get_value()) * timeline->get_zoom_scale() + limit;
}
rel_pos.x = scaling_selection_scale.x >= 0 ? (mp.x - selection_rect.position.x) : selection_rect.position.x + selection_rect.size.width - mp.x;
} else { // Left Handle
const int handle_adjust = Math::round((scaling_selection_scale.x >= 0 ? (selection_rect.position.x + selection_rect.size.width) : selection_rect.position.x) - mp.x);
mp.x -= MIN(Math::abs(handle_adjust), handle_length) * scaling_selection_handles.x * SIGN(handle_adjust);
const float x = editor->snap_time((mp.x - limit) / timeline->get_zoom_scale(), true) + timeline->get_value();
if (editor->is_snap_keys_enabled()) {
mp.x = (x - timeline->get_value()) * timeline->get_zoom_scale() + limit;
}
rel_pos.x = scaling_selection_scale.x >= 0 ? (selection_rect.position.x + selection_rect.size.width - mp.x) : (mp.x - selection_rect.position.x);
scaling_selection_offset.x = scaling_selection_pivot.x - x;
}
scaling_selection_scale.x *= rel_pos.x / selection_rect.size.width;
if (scaling_selection_scale.x == 0) {
scaling_selection_scale.x = CMP_EPSILON;
}
}
if (scaling_selection_handles.y != 0) {
if (scaling_selection_handles.y == 1) { // Bottom Handle
const int handle_adjust = Math::round(mp.y - (scaling_selection_scale.y >= 0 ? selection_rect.position.y : (selection_rect.position.y + selection_rect.size.height)));
mp.y -= MIN(Math::abs(handle_adjust), handle_length) * scaling_selection_handles.y * SIGN(handle_adjust);
if (scaling_selection_scale.y >= 0) {
rel_pos.y = mp.y - selection_rect.position.y;
} else {
rel_pos.y = selection_rect.position.y + selection_rect.size.height - mp.y;
}
} else { // Top Handle
const int handle_adjust = Math::round((scaling_selection_scale.y >= 0 ? (selection_rect.position.y + selection_rect.size.height) : selection_rect.position.y) - mp.y);
mp.y -= MIN(Math::abs(handle_adjust), handle_length) * scaling_selection_handles.y * SIGN(handle_adjust);
if (scaling_selection_scale.y >= 0) {
rel_pos.y = selection_rect.position.y + selection_rect.size.height - mp.y;
} else {
rel_pos.y = mp.y - selection_rect.position.y;
}
const float h = (get_size().height / 2.0 - mp.y) * timeline_v_zoom + timeline_v_scroll;
scaling_selection_offset.y = scaling_selection_pivot.y - h;
}
scaling_selection_scale.y *= rel_pos.y / selection_rect.size.height;
if (scaling_selection_scale.y == 0) {
scaling_selection_scale.y = CMP_EPSILON;
}
}
queue_redraw();
}
if ((moving_handle == 1 || moving_handle == -1) && mm.is_valid()) {
float y = (get_size().height / 2.0 - mm->get_position().y) * timeline_v_zoom + timeline_v_scroll;
float x = editor->snap_time((mm->get_position().x - timeline->get_name_limit()) / timeline->get_zoom_scale()) + timeline->get_value();
float x = editor->snap_time((mm->get_position().x - limit) / timeline->get_zoom_scale()) + timeline->get_value();
Vector2 key_pos = Vector2(animation->track_get_key_time(selected_track, moving_handle_key), animation->bezier_track_get_key_value(selected_track, moving_handle_key));
Vector2 key_pos = Vector2(animation->track_get_key_time(moving_handle_track, moving_handle_key), animation->bezier_track_get_key_value(moving_handle_track, moving_handle_key));
Vector2 moving_handle_value = Vector2(x, y) - key_pos;
@ -1601,7 +1912,7 @@ bool AnimationBezierTrackEdit::_try_select_at_ui_pos(const Point2 &p_pos, bool p
moving_selection_attempt = true;
moving_selection_from_key = pair.second;
moving_selection_from_track = pair.first;
moving_selection_mouse_begin_x = p_pos.x;
moving_selection_mouse_begin = p_pos;
moving_selection_offset = Vector2();
moving_handle_track = pair.first;
moving_handle_left = animation->bezier_track_get_key_in_handle(pair.first, pair.second);

View File

@ -108,7 +108,7 @@ class AnimationBezierTrackEdit : public Control {
typedef Pair<int, int> IntPair;
bool moving_selection_attempt = false;
float moving_selection_mouse_begin_x = 0.0;
Point2 moving_selection_mouse_begin;
IntPair select_single_attempt;
bool moving_selection = false;
int moving_selection_from_key = 0;
@ -122,6 +122,15 @@ class AnimationBezierTrackEdit : public Control {
Vector2 box_selection_from;
Vector2 box_selection_to;
Rect2 selection_rect;
Rect2 selection_handles_rect;
bool scaling_selection = false;
Vector2i scaling_selection_handles;
Vector2 scaling_selection_scale = Vector2(1, 1);
Vector2 scaling_selection_offset;
Point2 scaling_selection_pivot;
int moving_handle = 0; //0 no move -1 or +1 out, 2 both (drawing only)
int moving_handle_key = 0;
int moving_handle_track = 0;