From 14c42162aea7f02226320e696bf8c403a40c4ae9 Mon Sep 17 00:00:00 2001 From: Michael Alexsander Date: Sun, 15 Dec 2024 18:04:51 -0300 Subject: [PATCH] Add selection box movement/scaling to the animation bezier editor --- editor/animation_bezier_editor.cpp | 375 ++++++++++++++++++++++++++--- editor/animation_bezier_editor.h | 11 +- 2 files changed, 353 insertions(+), 33 deletions(-) diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp index 36ca417638..d1fa5fbc5a 100644 --- a/editor/animation_bezier_editor.cpp +++ b/editor/animation_bezier_editor.cpp @@ -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); @@ -505,29 +519,46 @@ void AnimationBezierTrackEdit::_notification(int p_what) { } } + const bool draw_selection_handles = selection.size() > 1; + LocalVector 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) { @@ -543,7 +574,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); } @@ -554,7 +585,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); @@ -569,7 +600,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(); @@ -600,6 +631,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; @@ -1192,15 +1251,17 @@ void AnimationBezierTrackEdit::gui_input(const Ref &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())) { @@ -1225,6 +1286,50 @@ void AnimationBezierTrackEdit::gui_input(const Ref &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; @@ -1249,7 +1354,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref &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(); @@ -1284,12 +1389,12 @@ void AnimationBezierTrackEdit::gui_input(const Ref &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; @@ -1334,8 +1439,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref &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")); @@ -1458,11 +1562,141 @@ void AnimationBezierTrackEdit::gui_input(const Ref &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 to_restore; + List 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::ConstIterator restore_itr = to_restore.begin(); + List::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 mm = p_event; if (moving_selection_attempt && mm.is_valid()) { if (!moving_selection) { @@ -1472,9 +1706,9 @@ void AnimationBezierTrackEdit::gui_input(const Ref &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); @@ -1482,9 +1716,15 @@ void AnimationBezierTrackEdit::gui_input(const Ref &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); } @@ -1504,11 +1744,82 @@ void AnimationBezierTrackEdit::gui_input(const Ref &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; @@ -1600,7 +1911,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); diff --git a/editor/animation_bezier_editor.h b/editor/animation_bezier_editor.h index dd7e8758f3..a2bcef4524 100644 --- a/editor/animation_bezier_editor.h +++ b/editor/animation_bezier_editor.h @@ -109,7 +109,7 @@ class AnimationBezierTrackEdit : public Control { typedef Pair 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; @@ -123,6 +123,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;