|
@@ -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<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);
|
|
|
-
|
|
|
- if (moving_selection && selection.has(IntPair(i, j))) {
|
|
|
- offset += moving_selection_offset.x;
|
|
|
- value += moving_selection_offset.y;
|
|
|
+ bool is_selected = selection.has(IntPair(i, j));
|
|
|
+
|
|
|
+ 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<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())) {
|
|
@@ -1225,6 +1286,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;
|
|
@@ -1249,7 +1354,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();
|
|
@@ -1284,12 +1389,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;
|
|
@@ -1334,8 +1439,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"));
|
|
|
|
|
@@ -1458,11 +1562,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) {
|
|
@@ -1472,9 +1706,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);
|
|
@@ -1482,9 +1716,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);
|
|
|
}
|
|
|
|
|
@@ -1504,11 +1744,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;
|
|
|
|
|
@@ -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);
|