summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRĂ©mi Verschelde <remi@verschelde.fr>2022-02-10 08:11:51 +0100
committerGitHub <noreply@github.com>2022-02-10 08:11:51 +0100
commitba4c3dc65a3f22c710afd918b8e4d734247b1f91 (patch)
treea4365f5958ae954cf1874d801e1afe16eb1ed8c9
parent5d536f06c2e7818ca2898a16f36f366087ef2521 (diff)
parent13a0d9177dd2f423180736b29df18903d4b0028d (diff)
Merge pull request #55030 from Razoric480/raz/simul-bezier-edit
Allow the drawing and editing of all animation bezier curves
-rw-r--r--editor/animation_bezier_editor.cpp956
-rw-r--r--editor/animation_bezier_editor.h44
-rw-r--r--editor/animation_track_editor.cpp71
-rw-r--r--editor/animation_track_editor.h5
-rw-r--r--editor/scene_tree_dock.cpp2
5 files changed, 789 insertions, 289 deletions
diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp
index da376c588e..67cdba043a 100644
--- a/editor/animation_bezier_editor.cpp
+++ b/editor/animation_bezier_editor.cpp
@@ -35,6 +35,8 @@
#include "scene/gui/view_panner.h"
#include "scene/resources/text_line.h"
+#include <limits.h>
+
float AnimationBezierTrackEdit::_bezier_h_to_pixel(float p_h) {
float h = p_h;
h = (h - v_scroll) / v_zoom;
@@ -55,15 +57,16 @@ static _FORCE_INLINE_ Vector2 _bezier_interp(real_t t, const Vector2 &start, con
void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) {
float scale = timeline->get_zoom_scale();
+
int limit = timeline->get_name_limit();
- int right_limit = get_size().width - timeline->get_buttons_width();
+ int right_limit = get_size().width;
//selection may have altered the order of keys
Map<float, int> key_order;
for (int i = 0; i < animation->track_get_key_count(p_track); i++) {
float ofs = animation->track_get_key_time(p_track, i);
- if (moving_selection && track == p_track && selection.has(i)) {
+ if (moving_selection && selection.has(IntPair(p_track, i))) {
ofs += moving_selection_offset.x;
}
@@ -82,11 +85,11 @@ void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) {
float offset = animation->track_get_key_time(p_track, i);
float height = animation->bezier_track_get_key_value(p_track, i);
Vector2 out_handle = animation->bezier_track_get_key_out_handle(p_track, i);
- if (track == p_track && moving_handle != 0 && moving_handle_key == i) {
+ if (p_track == moving_handle_track && moving_handle != 0 && moving_handle_key == i) {
out_handle = moving_handle_right;
}
- if (moving_selection && track == p_track && selection.has(i)) {
+ if (moving_selection && selection.has(IntPair(p_track, i))) {
offset += moving_selection_offset.x;
height += moving_selection_offset.y;
}
@@ -96,11 +99,11 @@ void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) {
float offset_n = animation->track_get_key_time(p_track, i_n);
float height_n = animation->bezier_track_get_key_value(p_track, i_n);
Vector2 in_handle = animation->bezier_track_get_key_in_handle(p_track, i_n);
- if (track == p_track && moving_handle != 0 && moving_handle_key == i_n) {
+ if (p_track == moving_handle_track && moving_handle != 0 && moving_handle_key == i_n) {
in_handle = moving_handle_left;
}
- if (moving_selection && track == p_track && selection.has(i_n)) {
+ if (moving_selection && selection.has(IntPair(p_track, i_n))) {
offset_n += moving_selection_offset.x;
height_n += moving_selection_offset.y;
}
@@ -221,20 +224,10 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/animation_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EditorSettings::get_singleton()->get("editors/panning/simple_panning")));
}
if (p_what == NOTIFICATION_THEME_CHANGED || p_what == NOTIFICATION_ENTER_TREE) {
- close_button->set_icon(get_theme_icon(SNAME("Close"), SNAME("EditorIcons")));
-
bezier_icon = get_theme_icon(SNAME("KeyBezierPoint"), SNAME("EditorIcons"));
bezier_handle_icon = get_theme_icon(SNAME("KeyBezierHandle"), SNAME("EditorIcons"));
selected_icon = get_theme_icon(SNAME("KeyBezierSelected"), SNAME("EditorIcons"));
}
- if (p_what == NOTIFICATION_RESIZED) {
- int right_limit = get_size().width - timeline->get_buttons_width();
- int hsep = get_theme_constant(SNAME("hseparation"), SNAME("ItemList"));
- int vsep = get_theme_constant(SNAME("vseparation"), SNAME("ItemList"));
-
- right_column->set_position(Vector2(right_limit + hsep, vsep));
- right_column->set_size(Vector2(timeline->get_buttons_width() - hsep * 2, get_size().y - vsep * 2));
- }
if (p_what == NOTIFICATION_DRAW) {
if (animation.is_null()) {
return;
@@ -258,101 +251,191 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
draw_line(Point2(limit, 0), Point2(limit, get_size().height), linecolor, Math::round(EDSCALE));
- int right_limit = get_size().width - timeline->get_buttons_width();
+ int right_limit = get_size().width;
- draw_line(Point2(right_limit, 0), Point2(right_limit, get_size().height), linecolor, Math::round(EDSCALE));
-
- String base_path = animation->track_get_path(track);
- int end = base_path.find(":");
- if (end != -1) {
- base_path = base_path.substr(0, end + 1);
- }
-
- // NAMES AND ICON
int vofs = vsep;
int margin = 0;
- {
- NodePath path = animation->track_get_path(track);
+ Map<int, Color> subtrack_colors;
+ Color selected_track_color;
+ subtracks.clear();
+ subtrack_icons.clear();
+
+ Map<String, Vector<int>> track_indices;
+ int track_count = animation->get_track_count();
+ for (int i = 0; i < track_count; ++i) {
+ if (animation->track_get_type(i) != Animation::TrackType::TYPE_BEZIER) {
+ continue;
+ }
- Node *node = nullptr;
+ String base_path = animation->track_get_path(i);
+ if (is_filtered) {
+ if (root && root->has_node(base_path)) {
+ Node *node = root->get_node(base_path);
+ if (!node) {
+ continue; // No node, no filter.
+ }
+ if (!EditorNode::get_singleton()->get_editor_selection()->is_selected(node)) {
+ continue; // Skip track due to not selected.
+ }
+ }
+ }
- if (root && root->has_node(path)) {
- node = root->get_node(path);
+ int end = base_path.find(":");
+ if (end != -1) {
+ base_path = base_path.substr(0, end + 1);
}
+ Vector<int> indices = track_indices.has(base_path) ? track_indices[base_path] : Vector<int>();
+ indices.push_back(i);
+ track_indices[base_path] = indices;
+ }
- String text;
+ for (const KeyValue<String, Vector<int>> &E : track_indices) {
+ String base_path = E.key;
- if (node) {
- int ofs = 0;
+ Vector<int> tracks = E.value;
- Ref<Texture2D> icon = EditorNode::get_singleton()->get_object_icon(node, "Node");
+ // NAMES AND ICON
+ {
+ NodePath path = animation->track_get_path(tracks[0]);
- text = node->get_name();
- ofs += hsep;
- ofs += icon->get_width();
+ Node *node = nullptr;
- TextLine text_buf = TextLine(text, font, font_size);
- text_buf.set_width(limit - ofs - hsep);
+ if (root && root->has_node(path)) {
+ node = root->get_node(path);
+ }
- int h = MAX(text_buf.get_size().y, icon->get_height());
+ String text;
- draw_texture(icon, Point2(ofs, vofs + int(h - icon->get_height()) / 2));
+ if (node) {
+ int ofs = 0;
- margin = icon->get_width();
+ Ref<Texture2D> icon = EditorNode::get_singleton()->get_object_icon(node, "Node");
- Vector2 string_pos = Point2(ofs, vofs + (h - text_buf.get_size().y) / 2 + text_buf.get_line_ascent());
- string_pos = string_pos.floor();
- text_buf.draw(get_canvas_item(), string_pos, color);
+ text = node->get_name();
+ ofs += hsep;
- vofs += h + vsep;
- }
- }
+ TextLine text_buf = TextLine(text, font, font_size);
+ text_buf.set_width(limit - ofs - icon->get_width() - hsep);
- // RELATED TRACKS TITLES
+ int h = MAX(text_buf.get_size().y, icon->get_height());
- Map<int, Color> subtrack_colors;
- subtracks.clear();
+ draw_texture(icon, Point2(ofs, vofs + int(h - icon->get_height()) / 2));
+ ofs += icon->get_width();
- for (int i = 0; i < animation->get_track_count(); i++) {
- if (animation->track_get_type(i) != Animation::TYPE_BEZIER) {
- continue;
- }
- String path = animation->track_get_path(i);
- if (!path.begins_with(base_path)) {
- continue; //another node
+ margin = icon->get_width();
+
+ Vector2 string_pos = Point2(ofs, vofs);
+ string_pos = string_pos.floor();
+ text_buf.draw(get_canvas_item(), string_pos, color);
+
+ vofs += h + vsep;
+ }
}
- path = path.replace_first(base_path, "");
- Color cc = color;
- TextLine text_buf = TextLine(path, font, font_size);
- text_buf.set_width(limit - margin - hsep);
+ Ref<Texture2D> remove = get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"));
+ float remove_hpos = limit - hsep - remove->get_width();
+
+ Ref<Texture2D> lock = get_theme_icon(SNAME("Lock"), SNAME("EditorIcons"));
+ Ref<Texture2D> unlock = get_theme_icon(SNAME("Unlock"), SNAME("EditorIcons"));
+ float lock_hpos = remove_hpos - hsep - lock->get_width();
+
+ Ref<Texture2D> visible = get_theme_icon(SNAME("GuiVisibilityVisible"), SNAME("EditorIcons"));
+ Ref<Texture2D> hidden = get_theme_icon(SNAME("GuiVisibilityHidden"), SNAME("EditorIcons"));
+ float visibility_hpos = lock_hpos - hsep - visible->get_width();
+
+ Ref<Texture2D> solo = get_theme_icon(SNAME("AudioBusSolo"), SNAME("EditorIcons"));
+ float solo_hpos = visibility_hpos - hsep - solo->get_width();
+
+ float buttons_width = remove->get_width() + lock->get_width() + visible->get_width() + solo->get_width() + hsep * 3;
+
+ for (int i = 0; i < tracks.size(); ++i) {
+ // RELATED TRACKS TITLES
+
+ int current_track = tracks[i];
+
+ String path = animation->track_get_path(current_track);
+ path = path.replace_first(base_path, "");
+
+ Color cc = color;
+ TextLine text_buf = TextLine(path, font, font_size);
+ text_buf.set_width(limit - margin - buttons_width);
+
+ Rect2 rect = Rect2(margin, vofs, solo_hpos - hsep - solo->get_width(), text_buf.get_size().y + vsep);
- Rect2 rect = Rect2(margin, vofs, limit - margin - hsep, text_buf.get_size().y + vsep);
- if (i != track) {
cc.a *= 0.7;
- uint32_t hash = path.hash();
- hash = ((hash >> 16) ^ hash) * 0x45d9f3b;
- hash = ((hash >> 16) ^ hash) * 0x45d9f3b;
- hash = (hash >> 16) ^ hash;
- float h = (hash % 65535) / 65536.0;
- Color subcolor;
- subcolor.set_hsv(h, 0.2, 0.8);
- subcolor.a = 0.5;
- draw_rect(Rect2(0, vofs + text_buf.get_size().y * 0.1, margin - hsep, text_buf.get_size().y * 0.8), subcolor);
- subtrack_colors[i] = subcolor;
-
- subtracks[i] = rect;
- } else {
- Color ac = get_theme_color(SNAME("accent_color"), SNAME("Editor"));
- ac.a = 0.5;
- draw_rect(rect, ac);
- }
+ float h;
+ if (path.ends_with(":x")) {
+ h = 0;
+ } else if (path.ends_with(":y")) {
+ h = 0.33f;
+ } else if (path.ends_with(":z")) {
+ h = 0.66f;
+ } else {
+ uint32_t hash = path.hash();
+ hash = ((hash >> 16) ^ hash) * 0x45d9f3b;
+ hash = ((hash >> 16) ^ hash) * 0x45d9f3b;
+ hash = (hash >> 16) ^ hash;
+ h = (hash % 65535) / 65536.0;
+ }
+
+ if (current_track != selected_track) {
+ Color track_color;
+ if (locked_tracks.has(current_track)) {
+ track_color.set_hsv(h, 0, 0.4);
+ } else {
+ track_color.set_hsv(h, 0.2, 0.8);
+ }
+ track_color.a = 0.5;
+ draw_rect(Rect2(0, vofs, margin - hsep, text_buf.get_size().y * 0.8), track_color);
+ subtrack_colors[current_track] = track_color;
+
+ subtracks[current_track] = rect;
+ } else {
+ Color ac = get_theme_color(SNAME("accent_color"), SNAME("Editor"));
+ ac.a = 0.5;
+ draw_rect(rect, ac);
+ if (locked_tracks.has(selected_track)) {
+ selected_track_color.set_hsv(h, 0.0, 0.4);
+ } else {
+ selected_track_color.set_hsv(h, 0.8, 0.8);
+ }
+ }
+
+ Vector2 string_pos = Point2(margin, vofs);
+ text_buf.draw(get_canvas_item(), string_pos, cc);
+
+ float icon_start_height = vofs + rect.size.y / 2;
+ Rect2 remove_rect = Rect2(remove_hpos, icon_start_height - remove->get_height() / 2, remove->get_width(), remove->get_height());
+ draw_texture(remove, remove_rect.position);
+
+ Rect2 lock_rect = Rect2(lock_hpos, icon_start_height - lock->get_height() / 2, lock->get_width(), lock->get_height());
+ if (locked_tracks.has(current_track)) {
+ draw_texture(lock, lock_rect.position);
+ } else {
+ draw_texture(unlock, lock_rect.position);
+ }
+
+ Rect2 visible_rect = Rect2(visibility_hpos, icon_start_height - visible->get_height() / 2, visible->get_width(), visible->get_height());
+ if (hidden_tracks.has(current_track)) {
+ draw_texture(hidden, visible_rect.position);
+ } else {
+ draw_texture(visible, visible_rect.position);
+ }
- Vector2 string_pos = Point2(margin, vofs + text_buf.get_line_ascent());
- text_buf.draw(get_canvas_item(), string_pos, cc);
+ Rect2 solo_rect = Rect2(solo_hpos, icon_start_height - solo->get_height() / 2, solo->get_width(), solo->get_height());
+ draw_texture(solo, solo_rect.position);
- vofs += text_buf.get_size().y + vsep;
+ Map<int, Rect2> track_icons;
+ track_icons[REMOVE_ICON] = remove_rect;
+ track_icons[LOCK_ICON] = lock_rect;
+ track_icons[VISIBILITY_ICON] = visible_rect;
+ track_icons[SOLO_ICON] = solo_rect;
+
+ subtrack_icons[current_track] = track_icons;
+
+ vofs += text_buf.get_size().y + vsep;
+ }
}
Color accent = get_theme_color(SNAME("accent_color"), SNAME("Editor"));
@@ -398,6 +481,9 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
float scale = timeline->get_zoom_scale();
Ref<Texture2D> point = get_theme_icon(SNAME("KeyValue"), SNAME("EditorIcons"));
for (const KeyValue<int, Color> &E : subtrack_colors) {
+ if (hidden_tracks.has(E.key)) {
+ continue;
+ }
_draw_track(E.key, E.value);
for (int i = 0; i < animation->track_get_key_count(E.key); i++) {
@@ -412,70 +498,116 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
}
}
- //draw edited curve
- const Color highlight = get_theme_color(SNAME("highlight_color"), SNAME("Editor"));
- _draw_track(track, highlight);
+ if (track_count > 0 && !hidden_tracks.has(selected_track)) {
+ //draw edited curve
+ _draw_track(selected_track, selected_track_color);
+ }
}
//draw editor handles
{
edit_points.clear();
-
float scale = timeline->get_zoom_scale();
- for (int i = 0; i < animation->track_get_key_count(track); i++) {
- float offset = animation->track_get_key_time(track, i);
- float value = animation->bezier_track_get_key_value(track, i);
- if (moving_selection && selection.has(i)) {
- offset += moving_selection_offset.x;
- value += moving_selection_offset.y;
+ for (int i = 0; i < track_count; ++i) {
+ if (animation->track_get_type(i) != Animation::TrackType::TYPE_BEZIER || hidden_tracks.has(i)) {
+ continue;
}
- Vector2 pos((offset - timeline->get_value()) * scale + limit, _bezier_h_to_pixel(value));
-
- Vector2 in_vec = animation->bezier_track_get_key_in_handle(track, i);
- if (moving_handle != 0 && moving_handle_key == i) {
- in_vec = moving_handle_left;
+ if (hidden_tracks.has(i) || locked_tracks.has(i)) {
+ continue;
}
- Vector2 pos_in(((offset + in_vec.x) - timeline->get_value()) * scale + limit, _bezier_h_to_pixel(value + in_vec.y));
- Vector2 out_vec = animation->bezier_track_get_key_out_handle(track, i);
+ int key_count = animation->track_get_key_count(i);
+ String path = animation->track_get_path(i);
- if (moving_handle != 0 && moving_handle_key == i) {
- out_vec = moving_handle_right;
+ if (is_filtered) {
+ if (root && root->has_node(path)) {
+ Node *node = root->get_node(path);
+ if (!node) {
+ continue; // No node, no filter.
+ }
+ if (!EditorNode::get_singleton()->get_editor_selection()->is_selected(node)) {
+ continue; // Skip track due to not selected.
+ }
+ }
}
- Vector2 pos_out(((offset + out_vec.x) - timeline->get_value()) * scale + limit, _bezier_h_to_pixel(value + out_vec.y));
+ 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;
+ }
- _draw_line_clipped(pos, pos_in, accent, limit, right_limit);
- _draw_line_clipped(pos, pos_out, accent, limit, right_limit);
+ Vector2 pos((offset - timeline->get_value()) * scale + limit, _bezier_h_to_pixel(value));
- EditPoint ep;
- if (pos.x >= limit && pos.x <= right_limit) {
- ep.point_rect.position = (pos - bezier_icon->get_size() / 2).floor();
- ep.point_rect.size = bezier_icon->get_size();
- if (selection.has(i)) {
- 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.001))), 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);
- } else {
- draw_texture(bezier_icon, ep.point_rect.position);
+ Vector2 in_vec = animation->bezier_track_get_key_in_handle(i, j);
+ if (moving_handle != 0 && moving_handle_track == i && moving_handle_key == j) {
+ in_vec = moving_handle_left;
+ }
+ Vector2 pos_in(((offset + in_vec.x) - timeline->get_value()) * scale + limit, _bezier_h_to_pixel(value + in_vec.y));
+
+ Vector2 out_vec = animation->bezier_track_get_key_out_handle(i, j);
+
+ if (moving_handle != 0 && moving_handle_track == i && moving_handle_key == j) {
+ out_vec = moving_handle_right;
+ }
+
+ 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))) {
+ _draw_line_clipped(pos, pos_in, accent, limit, right_limit);
+ _draw_line_clipped(pos, pos_out, accent, limit, right_limit);
+ }
+
+ EditPoint ep;
+ ep.track = i;
+ ep.key = j;
+ if (pos.x >= limit && pos.x <= right_limit) {
+ ep.point_rect.position = (pos - bezier_icon->get_size() / 2).floor();
+ ep.point_rect.size = bezier_icon->get_size();
+ if (selection.has(IntPair(i, j))) {
+ 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.001))), 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);
+ } else {
+ Color track_color = Color(1, 1, 1, 1);
+ if (i != selected_track) {
+ track_color = subtrack_colors[i];
+ }
+ draw_texture(bezier_icon, ep.point_rect.position, track_color);
+ }
+ ep.point_rect = ep.point_rect.grow(ep.point_rect.size.width * 0.5);
+ }
+ if (i == selected_track || selection.has(IntPair(i, j))) {
+ if (pos_in.x >= limit && pos_in.x <= right_limit) {
+ ep.in_rect.position = (pos_in - bezier_handle_icon->get_size() / 2).floor();
+ ep.in_rect.size = bezier_handle_icon->get_size();
+ draw_texture(bezier_handle_icon, ep.in_rect.position);
+ ep.in_rect = ep.in_rect.grow(ep.in_rect.size.width * 0.5);
+ }
+ if (pos_out.x >= limit && pos_out.x <= right_limit) {
+ ep.out_rect.position = (pos_out - bezier_handle_icon->get_size() / 2).floor();
+ ep.out_rect.size = bezier_handle_icon->get_size();
+ draw_texture(bezier_handle_icon, ep.out_rect.position);
+ ep.out_rect = ep.out_rect.grow(ep.out_rect.size.width * 0.5);
+ }
+ }
+ if (!locked_tracks.has(i)) {
+ edit_points.push_back(ep);
}
- ep.point_rect = ep.point_rect.grow(ep.point_rect.size.width * 0.5);
- }
- if (pos_in.x >= limit && pos_in.x <= right_limit) {
- ep.in_rect.position = (pos_in - bezier_handle_icon->get_size() / 2).floor();
- ep.in_rect.size = bezier_handle_icon->get_size();
- draw_texture(bezier_handle_icon, ep.in_rect.position);
- ep.in_rect = ep.in_rect.grow(ep.in_rect.size.width * 0.5);
}
- if (pos_out.x >= limit && pos_out.x <= right_limit) {
- ep.out_rect.position = (pos_out - bezier_handle_icon->get_size() / 2).floor();
- ep.out_rect.size = bezier_handle_icon->get_size();
- draw_texture(bezier_handle_icon, ep.out_rect.position);
- ep.out_rect = ep.out_rect.grow(ep.out_rect.size.width * 0.5);
+ }
+
+ for (int i = 0; i < edit_points.size(); ++i) {
+ if (edit_points[i].track == selected_track) {
+ EditPoint ep = edit_points[i];
+ edit_points.remove_at(i);
+ edit_points.insert(0, ep);
}
- edit_points.push_back(ep);
}
}
@@ -506,15 +638,7 @@ Ref<Animation> AnimationBezierTrackEdit::get_animation() const {
void AnimationBezierTrackEdit::set_animation_and_track(const Ref<Animation> &p_animation, int p_track) {
animation = p_animation;
- track = p_track;
- if (is_connected("select_key", Callable(editor, "_key_selected"))) {
- disconnect("select_key", Callable(editor, "_key_selected"));
- }
- if (is_connected("deselect_key", Callable(editor, "_key_deselected"))) {
- disconnect("deselect_key", Callable(editor, "_key_deselected"));
- }
- connect("select_key", Callable(editor, "_key_selected"), varray(p_track), CONNECT_DEFERRED);
- connect("deselect_key", Callable(editor, "_key_deselected"), varray(p_track), CONNECT_DEFERRED);
+ selected_track = p_track;
update();
}
@@ -529,11 +653,14 @@ void AnimationBezierTrackEdit::set_undo_redo(UndoRedo *p_undo_redo) {
void AnimationBezierTrackEdit::set_timeline(AnimationTimelineEdit *p_timeline) {
timeline = p_timeline;
timeline->connect("zoom_changed", callable_mp(this, &AnimationBezierTrackEdit::_zoom_changed));
+ timeline->connect("name_limit_changed", callable_mp(this, &AnimationBezierTrackEdit::_zoom_changed));
}
void AnimationBezierTrackEdit::set_editor(AnimationTrackEditor *p_editor) {
editor = p_editor;
connect("clear_selection", Callable(editor, "_clear_selection"), varray(false));
+ connect("select_key", Callable(editor, "_key_selected"), varray(), CONNECT_DEFERRED);
+ connect("deselect_key", Callable(editor, "_key_deselected"), varray(), CONNECT_DEFERRED);
}
void AnimationBezierTrackEdit::_play_position_draw() {
@@ -544,9 +671,11 @@ void AnimationBezierTrackEdit::_play_position_draw() {
float scale = timeline->get_zoom_scale();
int h = get_size().height;
- int px = (-timeline->get_value() + play_position_pos) * scale + timeline->get_name_limit();
+ int limit = timeline->get_name_limit();
+
+ int px = (-timeline->get_value() + play_position_pos) * scale + limit;
- if (px >= timeline->get_name_limit() && px < (get_size().width - timeline->get_buttons_width())) {
+ if (px >= limit && px < (get_size().width)) {
Color color = get_theme_color(SNAME("accent_color"), SNAME("Editor"));
play_position->draw_line(Point2(px, 0), Point2(px, h), color, Math::round(2 * EDSCALE));
}
@@ -565,11 +694,84 @@ void AnimationBezierTrackEdit::set_root(Node *p_root) {
root = p_root;
}
+void AnimationBezierTrackEdit::set_filtered(bool p_filtered) {
+ is_filtered = p_filtered;
+ if (animation == nullptr) {
+ return;
+ }
+ String base_path = animation->track_get_path(selected_track);
+ if (is_filtered) {
+ if (root && root->has_node(base_path)) {
+ Node *node = root->get_node(base_path);
+ if (!node || !EditorNode::get_singleton()->get_editor_selection()->is_selected(node)) {
+ for (int i = 0; i < animation->get_track_count(); ++i) {
+ if (animation->track_get_type(i) != Animation::TrackType::TYPE_BEZIER) {
+ continue;
+ }
+
+ base_path = animation->track_get_path(i);
+ if (root && root->has_node(base_path)) {
+ node = root->get_node(base_path);
+ if (!node) {
+ continue; // No node, no filter.
+ }
+ if (!EditorNode::get_singleton()->get_editor_selection()->is_selected(node)) {
+ continue; // Skip track due to not selected.
+ }
+
+ set_animation_and_track(animation, i);
+ break;
+ }
+ }
+ }
+ }
+ }
+ update();
+}
+
void AnimationBezierTrackEdit::_zoom_changed() {
update();
play_position->update();
}
+void AnimationBezierTrackEdit::_update_locked_tracks_after(int p_track) {
+ if (locked_tracks.has(p_track)) {
+ locked_tracks.erase(p_track);
+ }
+
+ Vector<int> updated_locked_tracks;
+ for (Set<int>::Element *E = locked_tracks.front(); E; E = E->next()) {
+ updated_locked_tracks.push_back(E->get());
+ }
+ locked_tracks.clear();
+ for (int i = 0; i < updated_locked_tracks.size(); ++i) {
+ if (updated_locked_tracks[i] > p_track) {
+ locked_tracks.insert(updated_locked_tracks[i] - 1);
+ } else {
+ locked_tracks.insert(updated_locked_tracks[i]);
+ }
+ }
+}
+
+void AnimationBezierTrackEdit::_update_hidden_tracks_after(int p_track) {
+ if (hidden_tracks.has(p_track)) {
+ hidden_tracks.erase(p_track);
+ }
+
+ Vector<int> updated_hidden_tracks;
+ for (Set<int>::Element *E = hidden_tracks.front(); E; E = E->next()) {
+ updated_hidden_tracks.push_back(E->get());
+ }
+ hidden_tracks.clear();
+ for (int i = 0; i < updated_hidden_tracks.size(); ++i) {
+ if (updated_hidden_tracks[i] > p_track) {
+ hidden_tracks.insert(updated_hidden_tracks[i] - 1);
+ } else {
+ hidden_tracks.insert(updated_hidden_tracks[i]);
+ }
+ }
+}
+
String AnimationBezierTrackEdit::get_tooltip(const Point2 &p_pos) const {
return Control::get_tooltip(p_pos);
}
@@ -583,10 +785,10 @@ void AnimationBezierTrackEdit::_clear_selection() {
void AnimationBezierTrackEdit::_change_selected_keys_handle_mode(Animation::HandleMode p_mode) {
undo_redo->create_action(TTR("Update Selected Key Handles"));
double ratio = timeline->get_zoom_scale() * v_zoom;
- for (Set<int>::Element *E = selection.back(); E; E = E->prev()) {
- const int key_index = E->get();
- undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_handle_mode", track, key_index, animation->bezier_track_get_key_handle_mode(track, key_index), ratio);
- undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_handle_mode", track, key_index, p_mode, ratio);
+ for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
+ const IntPair track_key_pair = E->get();
+ undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_handle_mode", track_key_pair.first, track_key_pair.second, animation->bezier_track_get_key_handle_mode(track_key_pair.first, track_key_pair.second), ratio);
+ undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_handle_mode", track_key_pair.first, track_key_pair.second, p_mode, ratio);
}
undo_redo->commit_action();
}
@@ -606,8 +808,8 @@ void AnimationBezierTrackEdit::_select_at_anim(const Ref<Animation> &p_anim, int
int idx = animation->track_find_key(p_track, p_pos, true);
ERR_FAIL_COND(idx < 0);
- selection.insert(idx);
- emit_signal(SNAME("select_key"), idx, true);
+ selection.insert(IntPair(p_track, idx));
+ emit_signal(SNAME("select_key"), p_track, idx, true);
update();
}
@@ -631,14 +833,100 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
}
}
+ Ref<InputEventKey> key_press = p_event;
+
+ if (key_press.is_valid() && key_press->is_pressed()) {
+ if (ED_GET_SHORTCUT("animation_bezier_editor/focus")->matches_event(p_event)) {
+ SelectionSet focused_keys;
+ if (selection.is_empty()) {
+ for (int i = 0; i < edit_points.size(); ++i) {
+ IntPair key_pair = IntPair(edit_points[i].track, edit_points[i].key);
+ focused_keys.insert(key_pair);
+ }
+ } else {
+ for (SelectionSet::Element *E = selection.front(); E; E = E->next()) {
+ focused_keys.insert(E->get());
+ if (E->get().second > 0) {
+ IntPair previous_key = IntPair(E->get().first, E->get().second - 1);
+ focused_keys.insert(previous_key);
+ }
+ if (E->get().second < animation->track_get_key_count(E->get().first) - 1) {
+ IntPair next_key = IntPair(E->get().first, E->get().second + 1);
+ focused_keys.insert(next_key);
+ }
+ }
+ }
+ if (focused_keys.is_empty()) {
+ accept_event();
+ return;
+ }
+
+ float minimum_time = INFINITY;
+ float maximum_time = -INFINITY;
+ float minimum_value = INFINITY;
+ float maximum_value = -INFINITY;
+
+ for (SelectionSet::Element *E = focused_keys.front(); E; E = E->next()) {
+ IntPair key_pair = E->get();
+
+ float time = animation->track_get_key_time(key_pair.first, key_pair.second);
+ float value = animation->bezier_track_get_key_value(key_pair.first, key_pair.second);
+
+ minimum_time = MIN(time, minimum_time);
+ maximum_time = MAX(time, maximum_time);
+ minimum_value = MIN(value, minimum_value);
+ maximum_value = MAX(value, maximum_value);
+ }
+
+ float width = get_size().width - timeline->get_name_limit() - timeline->get_buttons_width();
+ float padding = width * 0.1;
+ float desired_scale = (width - padding / 2) / (maximum_time - minimum_time);
+ minimum_time = MAX(0, minimum_time - (padding / 2) / desired_scale);
+
+ float zv = Math::pow(100 / desired_scale, 0.125f);
+ if (zv < 1) {
+ zv = Math::pow(desired_scale / 100, 0.125f) - 1;
+ zv = 1 - zv;
+ }
+ float zoom_value = timeline->get_zoom()->get_max() - zv;
+
+ timeline->get_zoom()->set_value(zoom_value);
+ timeline->call_deferred("set_value", minimum_time);
+
+ v_scroll = (maximum_value + minimum_value) / 2.0;
+ v_zoom = (maximum_value - minimum_value) / ((get_size().height - timeline->get_size().height) * 0.9);
+
+ update();
+ accept_event();
+ return;
+ } else if (ED_GET_SHORTCUT("animation_bezier_editor/select_all_keys")->matches_event(p_event)) {
+ for (int i = 0; i < edit_points.size(); ++i) {
+ selection.insert(IntPair(edit_points[i].track, edit_points[i].key));
+ }
+
+ update();
+ accept_event();
+ return;
+ } else if (ED_GET_SHORTCUT("animation_bezier_editor/deselect_all_keys")->matches_event(p_event)) {
+ selection.clear();
+
+ update();
+ accept_event();
+ return;
+ }
+ }
+
Ref<InputEventMouseButton> mb = p_event;
+ int limit = timeline->get_name_limit();
if (mb.is_valid() && mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) {
menu_insert_key = mb->get_position();
- if (menu_insert_key.x >= timeline->get_name_limit() && menu_insert_key.x <= get_size().width - timeline->get_buttons_width()) {
+ if (menu_insert_key.x >= limit && menu_insert_key.x <= get_size().width) {
Vector2 popup_pos = get_screen_position() + mb->get_position();
menu->clear();
- menu->add_icon_item(bezier_icon, TTR("Insert Key Here"), MENU_KEY_INSERT);
+ if (!locked_tracks.has(selected_track) || locked_tracks.has(selected_track)) {
+ menu->add_icon_item(bezier_icon, TTR("Insert Key Here"), MENU_KEY_INSERT);
+ }
if (selection.size()) {
menu->add_separator();
menu->add_icon_item(get_theme_icon(SNAME("Duplicate"), SNAME("EditorIcons")), TTR("Duplicate Selected Key(s)"), MENU_KEY_DUPLICATE);
@@ -649,50 +937,163 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
menu->add_icon_item(get_theme_icon(SNAME("BezierHandlesBalanced"), SNAME("EditorIcons")), TTR("Make Handles Balanced"), MENU_KEY_SET_HANDLE_BALANCED);
}
- menu->set_as_minsize();
- menu->set_position(popup_pos);
- menu->popup();
+ if (menu->get_item_count()) {
+ menu->set_as_minsize();
+ menu->set_position(popup_pos);
+ menu->popup();
+ }
}
}
if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
for (const KeyValue<int, Rect2> &E : subtracks) {
if (E.value.has_point(mb->get_position())) {
- set_animation_and_track(animation, E.key);
- _clear_selection();
+ if (!locked_tracks.has(E.key) && !hidden_tracks.has(E.key)) {
+ set_animation_and_track(animation, E.key);
+ _clear_selection();
+ }
return;
}
}
+ for (const KeyValue<int, Map<int, Rect2>> &E : subtrack_icons) {
+ int track = E.key;
+ Map<int, Rect2> track_icons = E.value;
+ for (const KeyValue<int, Rect2> &I : track_icons) {
+ if (I.value.has_point(mb->get_position())) {
+ if (I.key == REMOVE_ICON) {
+ undo_redo->create_action("Remove Bezier Track");
+
+ undo_redo->add_do_method(this, "_update_locked_tracks_after", track);
+ undo_redo->add_do_method(this, "_update_hidden_tracks_after", track);
+
+ undo_redo->add_do_method(animation.ptr(), "remove_track", track);
+
+ undo_redo->add_undo_method(animation.ptr(), "add_track", Animation::TrackType::TYPE_BEZIER, track);
+ undo_redo->add_undo_method(animation.ptr(), "track_set_path", track, animation->track_get_path(track));
+
+ for (int i = 0; i < animation->track_get_key_count(track); ++i) {
+ undo_redo->add_undo_method(
+ animation.ptr(),
+ "bezier_track_insert_key",
+ track, animation->track_get_key_time(track, i),
+ animation->bezier_track_get_key_value(track, i),
+ animation->bezier_track_get_key_in_handle(track, i),
+ animation->bezier_track_get_key_out_handle(track, i),
+ animation->bezier_track_get_key_handle_mode(track, i));
+ }
+
+ undo_redo->commit_action();
+
+ selected_track = CLAMP(selected_track, 0, animation->get_track_count() - 1);
+ return;
+ } else if (I.key == LOCK_ICON) {
+ if (locked_tracks.has(track)) {
+ locked_tracks.erase(track);
+ } else {
+ locked_tracks.insert(track);
+ if (selected_track == track) {
+ for (int i = 0; i < animation->get_track_count(); ++i) {
+ if (!locked_tracks.has(i) && animation->track_get_type(i) == Animation::TrackType::TYPE_BEZIER) {
+ set_animation_and_track(animation, i);
+ break;
+ }
+ }
+ }
+ }
+ update();
+ return;
+ } else if (I.key == VISIBILITY_ICON) {
+ if (hidden_tracks.has(track)) {
+ hidden_tracks.erase(track);
+ } else {
+ hidden_tracks.insert(track);
+ if (selected_track == track) {
+ for (int i = 0; i < animation->get_track_count(); ++i) {
+ if (!hidden_tracks.has(i) && animation->track_get_type(i) == Animation::TrackType::TYPE_BEZIER) {
+ set_animation_and_track(animation, i);
+ break;
+ }
+ }
+ }
+ }
+
+ Vector<int> visible_tracks;
+ for (int i = 0; i < animation->get_track_count(); ++i) {
+ if (!hidden_tracks.has(i) && animation->track_get_type(i) == Animation::TrackType::TYPE_BEZIER) {
+ visible_tracks.push_back(i);
+ }
+ }
+
+ if (visible_tracks.size() == 1) {
+ solo_track = visible_tracks[0];
+ } else {
+ solo_track = -1;
+ }
+
+ update();
+ return;
+ } else if (I.key == SOLO_ICON) {
+ if (solo_track == track) {
+ solo_track = -1;
+
+ hidden_tracks.clear();
+ } else {
+ if (hidden_tracks.has(track)) {
+ hidden_tracks.erase(track);
+ }
+ for (int i = 0; i < animation->get_track_count(); ++i) {
+ if (animation->track_get_type(i) == Animation::TrackType::TYPE_BEZIER) {
+ if (i != track && !hidden_tracks.has(i)) {
+ hidden_tracks.insert(i);
+ }
+ }
+ }
+
+ set_animation_and_track(animation, track);
+ solo_track = track;
+ }
+ update();
+ return;
+ }
+ return;
+ }
+ }
+ }
+
for (int i = 0; i < edit_points.size(); i++) {
//first check point
//command makes it ignore the main point, so control point editors can be force-edited
//path 2D editing in the 3D and 2D editors works the same way
if (!mb->is_command_pressed()) {
if (edit_points[i].point_rect.has_point(mb->get_position())) {
+ IntPair pair = IntPair(edit_points[i].track, edit_points[i].key);
if (mb->is_shift_pressed()) {
//add to selection
- if (selection.has(i)) {
- selection.erase(i);
+ if (selection.has(pair)) {
+ selection.erase(pair);
} else {
- selection.insert(i);
+ selection.insert(pair);
}
update();
- select_single_attempt = -1;
- } else if (selection.has(i)) {
+ select_single_attempt = IntPair(-1, -1);
+ } else if (selection.has(pair)) {
moving_selection_attempt = true;
moving_selection = false;
- moving_selection_from_key = i;
+ moving_selection_from_key = pair.second;
+ moving_selection_from_track = pair.first;
moving_selection_offset = Vector2();
- select_single_attempt = i;
+ select_single_attempt = pair;
update();
} else {
moving_selection_attempt = true;
moving_selection = true;
- moving_selection_from_key = i;
+ moving_selection_from_key = pair.second;
+ moving_selection_from_track = pair.first;
moving_selection_offset = Vector2();
+ set_animation_and_track(animation, pair.first);
selection.clear();
- selection.insert(i);
+ selection.insert(pair);
update();
}
return;
@@ -701,26 +1102,27 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
if (edit_points[i].in_rect.has_point(mb->get_position())) {
moving_handle = -1;
- moving_handle_key = i;
- moving_handle_left = animation->bezier_track_get_key_in_handle(track, i);
- moving_handle_right = animation->bezier_track_get_key_out_handle(track, i);
+ moving_handle_key = edit_points[i].key;
+ moving_handle_track = edit_points[i].track;
+ moving_handle_left = animation->bezier_track_get_key_in_handle(edit_points[i].track, edit_points[i].key);
+ moving_handle_right = animation->bezier_track_get_key_out_handle(edit_points[i].track, edit_points[i].key);
update();
return;
}
if (edit_points[i].out_rect.has_point(mb->get_position())) {
moving_handle = 1;
- moving_handle_key = i;
- moving_handle_left = animation->bezier_track_get_key_in_handle(track, i);
- moving_handle_right = animation->bezier_track_get_key_out_handle(track, i);
+ moving_handle_key = edit_points[i].key;
+ moving_handle_track = edit_points[i].track;
+ moving_handle_left = animation->bezier_track_get_key_in_handle(edit_points[i].track, edit_points[i].key);
+ moving_handle_right = animation->bezier_track_get_key_out_handle(edit_points[i].track, edit_points[i].key);
update();
return;
- ;
}
}
//insert new point
- if (mb->is_command_pressed() && mb->get_position().x >= timeline->get_name_limit() && mb->get_position().x < get_size().width - timeline->get_buttons_width()) {
+ if (mb->get_position().x >= limit && mb->get_position().x < get_size().width && mb->is_command_pressed()) {
Array new_point;
new_point.resize(6);
@@ -733,34 +1135,35 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
new_point[4] = 0;
new_point[5] = 0;
- float time = ((mb->get_position().x - timeline->get_name_limit()) / timeline->get_zoom_scale()) + timeline->get_value();
- while (animation->track_find_key(track, time, true) != -1) {
+ float time = ((mb->get_position().x - limit) / timeline->get_zoom_scale()) + timeline->get_value();
+ while (animation->track_find_key(selected_track, time, true) != -1) {
time += 0.001;
}
undo_redo->create_action(TTR("Add Bezier Point"));
- undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, time, new_point);
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", track, time);
+ undo_redo->add_do_method(animation.ptr(), "track_insert_key", selected_track, time, new_point);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", selected_track, time);
undo_redo->commit_action();
//then attempt to move
- int index = animation->track_find_key(track, time, true);
+ int index = animation->track_find_key(selected_track, time, true);
ERR_FAIL_COND(index == -1);
_clear_selection();
- selection.insert(index);
+ selection.insert(IntPair(selected_track, index));
moving_selection_attempt = true;
moving_selection = false;
moving_selection_from_key = index;
+ moving_selection_from_track = selected_track;
moving_selection_offset = Vector2();
- select_single_attempt = -1;
+ select_single_attempt = IntPair(-1, -1);
update();
return;
}
//box select
- if (mb->get_position().x >= timeline->get_name_limit() && mb->get_position().x < get_size().width - timeline->get_buttons_width()) {
+ if (mb->get_position().x >= limit && mb->get_position().x < get_size().width) {
box_selecting_attempt = true;
box_selecting = false;
box_selecting_add = false;
@@ -786,14 +1189,44 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
}
Rect2 selection_rect(bs_from, bs_to - bs_from);
+ bool track_set = false;
for (int i = 0; i < edit_points.size(); i++) {
if (edit_points[i].point_rect.intersects(selection_rect)) {
- selection.insert(i);
+ selection.insert(IntPair(edit_points[i].track, edit_points[i].key));
+ if (!track_set) {
+ track_set = true;
+ set_animation_and_track(animation, edit_points[i].track);
+ }
}
}
} else {
_clear_selection(); //clicked and nothing happened, so clear the selection
+
+ //select by clicking on curve
+ int track_count = animation->get_track_count();
+
+ float animation_length = animation->get_length();
+ animation->set_length(real_t(INT_MAX)); //bezier_track_interpolate doesn't find keys if they exist beyond anim length
+
+ float time = ((mb->get_position().x - limit) / timeline->get_zoom_scale()) + timeline->get_value();
+
+ for (int i = 0; i < track_count; ++i) {
+ if (animation->track_get_type(i) != Animation::TrackType::TYPE_BEZIER || hidden_tracks.has(i) || locked_tracks.has(i)) {
+ continue;
+ }
+
+ float track_h = animation->bezier_track_interpolate(i, time);
+ float track_height = _bezier_h_to_pixel(track_h);
+
+ if (abs(mb->get_position().y - track_height) < 10) {
+ set_animation_and_track(animation, i);
+ break;
+ }
+ }
+
+ animation->set_length(animation_length);
}
+
box_selecting_attempt = false;
box_selecting = false;
update();
@@ -801,10 +1234,10 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
if (moving_handle != 0 && mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
undo_redo->create_action(TTR("Move Bezier Points"));
- undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_in_handle", track, moving_handle_key, moving_handle_left);
- undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_out_handle", track, moving_handle_key, moving_handle_right);
- undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_in_handle", track, moving_handle_key, animation->bezier_track_get_key_in_handle(track, moving_handle_key));
- undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_out_handle", track, moving_handle_key, animation->bezier_track_get_key_out_handle(track, moving_handle_key));
+ undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_in_handle", selected_track, moving_handle_key, moving_handle_left);
+ undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_out_handle", selected_track, moving_handle_key, moving_handle_right);
+ undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_in_handle", selected_track, moving_handle_key, animation->bezier_track_get_key_in_handle(selected_track, moving_handle_key));
+ undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_out_handle", selected_track, moving_handle_key, animation->bezier_track_get_key_out_handle(selected_track, moving_handle_key));
undo_redo->commit_action();
moving_handle = 0;
@@ -819,60 +1252,60 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
List<AnimMoveRestore> to_restore;
// 1-remove the keys
- for (Set<int>::Element *E = selection.back(); E; E = E->prev()) {
- undo_redo->add_do_method(animation.ptr(), "track_remove_key", track, E->get());
+ 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 (Set<int>::Element *E = selection.back(); E; E = E->prev()) {
- float newtime = editor->snap_time(animation->track_get_key_time(track, E->get()) + moving_selection_offset.x);
+ for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
+ float newtime = editor->snap_time(animation->track_get_key_time(E->get().first, E->get().second) + moving_selection_offset.x);
- int idx = animation->track_find_key(track, newtime, true);
+ int idx = animation->track_find_key(E->get().first, newtime, true);
if (idx == -1) {
continue;
}
- if (selection.has(idx)) {
+ 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", track, newtime);
+ 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(track, idx);
- amr.track = track;
+ amr.key = animation->track_get_key_value(E->get().first, idx);
+ amr.track = E->get().first;
amr.time = newtime;
to_restore.push_back(amr);
}
// 3-move the keys (re insert them)
- for (Set<int>::Element *E = selection.back(); E; E = E->prev()) {
- float newpos = editor->snap_time(animation->track_get_key_time(track, E->get()) + moving_selection_offset.x);
+ for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
+ float newpos = editor->snap_time(animation->track_get_key_time(E->get().first, E->get().second) + moving_selection_offset.x);
/*
if (newpos<0)
continue; //no add at the beginning
*/
- Array key = animation->track_get_key_value(track, E->get());
+ Array key = animation->track_get_key_value(E->get().first, E->get().second);
float h = key[0];
h += moving_selection_offset.y;
key[0] = h;
- undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, newpos, key, 1);
+ undo_redo->add_do_method(animation.ptr(), "track_insert_key", E->get().first, newpos, key, 1);
}
// 4-(undo) remove inserted keys
- for (Set<int>::Element *E = selection.back(); E; E = E->prev()) {
- float newpos = editor->snap_time(animation->track_get_key_time(track, E->get()) + moving_selection_offset.x);
+ for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
+ float newpos = editor->snap_time(animation->track_get_key_time(E->get().first, E->get().second) + moving_selection_offset.x);
/*
if (newpos<0)
continue; //no remove what no inserted
*/
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", track, newpos);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", E->get().first, newpos);
}
// 5-(undo) reinsert keys
- for (Set<int>::Element *E = selection.back(); E; E = E->prev()) {
- float oldpos = animation->track_get_key_time(track, E->get());
- undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track, oldpos, animation->track_get_key_value(track, E->get()), 1);
+ for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
+ float oldpos = animation->track_get_key_time(E->get().first, E->get().second);
+ undo_redo->add_undo_method(animation.ptr(), "track_insert_key", E->get().first, oldpos, animation->track_get_key_value(E->get().first, E->get().second), 1);
}
// 6-(undo) reinsert overlapped keys
@@ -885,20 +1318,21 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
// 7-reselect
- for (Set<int>::Element *E = selection.back(); E; E = E->prev()) {
- float oldpos = animation->track_get_key_time(track, E->get());
+ for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
+ float oldpos = animation->track_get_key_time(E->get().first, E->get().second);
float newpos = editor->snap_time(oldpos + moving_selection_offset.x);
- undo_redo->add_do_method(this, "_select_at_anim", animation, track, newpos);
- undo_redo->add_undo_method(this, "_select_at_anim", animation, track, oldpos);
+ undo_redo->add_do_method(this, "_select_at_anim", animation, E->get().first, newpos);
+ undo_redo->add_undo_method(this, "_select_at_anim", animation, E->get().first, oldpos);
}
undo_redo->commit_action();
moving_selection = false;
- } else if (select_single_attempt != -1) {
+ } else if (select_single_attempt != IntPair(-1, -1)) {
selection.clear();
selection.insert(select_single_attempt);
+ set_animation_and_track(animation, select_single_attempt.first);
}
moving_selection_attempt = false;
@@ -909,13 +1343,13 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
if (moving_selection_attempt && mm.is_valid()) {
if (!moving_selection) {
moving_selection = true;
- select_single_attempt = -1;
+ select_single_attempt = IntPair(-1, -1);
}
float y = (get_size().height / 2 - mm->get_position().y) * v_zoom + 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());
- moving_selection_offset = Vector2(x - animation->track_get_key_time(track, moving_selection_from_key), y - animation->bezier_track_get_key_value(track, moving_selection_from_key));
+ moving_selection_offset = Vector2(x - animation->track_get_key_time(moving_selection_from_track, moving_selection_from_key), y - animation->bezier_track_get_key_value(moving_selection_from_track, moving_selection_from_key));
update();
}
@@ -938,17 +1372,17 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
float y = (get_size().height / 2 - mm->get_position().y) * v_zoom + v_scroll;
float x = editor->snap_time((mm->get_position().x - timeline->get_name_limit()) / timeline->get_zoom_scale()) + timeline->get_value();
- Vector2 key_pos = Vector2(animation->track_get_key_time(track, moving_handle_key), animation->bezier_track_get_key_value(track, moving_handle_key));
+ 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 moving_handle_value = Vector2(x, y) - key_pos;
- moving_handle_left = animation->bezier_track_get_key_in_handle(track, moving_handle_key);
- moving_handle_right = animation->bezier_track_get_key_out_handle(track, moving_handle_key);
+ moving_handle_left = animation->bezier_track_get_key_in_handle(moving_handle_track, moving_handle_key);
+ moving_handle_right = animation->bezier_track_get_key_out_handle(moving_handle_track, moving_handle_key);
if (moving_handle == -1) {
moving_handle_left = moving_handle_value;
- if (animation->bezier_track_get_key_handle_mode(track, moving_handle_key) == Animation::HANDLE_MODE_BALANCED) {
+ if (animation->bezier_track_get_key_handle_mode(moving_handle_track, moving_handle_key) == Animation::HANDLE_MODE_BALANCED) {
double ratio = timeline->get_zoom_scale() * v_zoom;
Transform2D xform;
xform.set_scale(Vector2(1.0, 1.0 / ratio));
@@ -961,7 +1395,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
} else if (moving_handle == 1) {
moving_handle_right = moving_handle_value;
- if (animation->bezier_track_get_key_handle_mode(track, moving_handle_key) == Animation::HANDLE_MODE_BALANCED) {
+ if (animation->bezier_track_get_key_handle_mode(moving_handle_track, moving_handle_key) == Animation::HANDLE_MODE_BALANCED) {
double ratio = timeline->get_zoom_scale() * v_zoom;
Transform2D xform;
xform.set_scale(Vector2(1.0, 1.0 / ratio));
@@ -980,12 +1414,12 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
undo_redo->create_action(TTR("Move Bezier Points"));
if (moving_handle == -1) {
double ratio = timeline->get_zoom_scale() * v_zoom;
- undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_in_handle", track, moving_handle_key, moving_handle_left, ratio);
- undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_in_handle", track, moving_handle_key, animation->bezier_track_get_key_in_handle(track, moving_handle_key), ratio);
+ undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_in_handle", moving_handle_track, moving_handle_key, moving_handle_left, ratio);
+ undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_in_handle", moving_handle_track, moving_handle_key, animation->bezier_track_get_key_in_handle(moving_handle_track, moving_handle_key), ratio);
} else if (moving_handle == 1) {
double ratio = timeline->get_zoom_scale() * v_zoom;
- undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_out_handle", track, moving_handle_key, moving_handle_right, ratio);
- undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_out_handle", track, moving_handle_key, animation->bezier_track_get_key_out_handle(track, moving_handle_key), ratio);
+ undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_out_handle", moving_handle_track, moving_handle_key, moving_handle_right, ratio);
+ undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_out_handle", moving_handle_track, moving_handle_key, animation->bezier_track_get_key_out_handle(moving_handle_track, moving_handle_key), ratio);
}
undo_redo->commit_action();
@@ -1028,27 +1462,32 @@ void AnimationBezierTrackEdit::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_or
void AnimationBezierTrackEdit::_menu_selected(int p_index) {
switch (p_index) {
case MENU_KEY_INSERT: {
- Array new_point;
- new_point.resize(6);
+ if (animation->get_track_count() > 0) {
+ Array new_point;
+ new_point.resize(6);
- float h = (get_size().height / 2 - menu_insert_key.y) * v_zoom + v_scroll;
+ float h = (get_size().height / 2 - menu_insert_key.y) * v_zoom + v_scroll;
- new_point[0] = h;
- new_point[1] = -0.25;
- new_point[2] = 0;
- new_point[3] = 0.25;
- new_point[4] = 0;
- new_point[5] = Animation::HANDLE_MODE_BALANCED;
+ new_point[0] = h;
+ new_point[1] = -0.25;
+ new_point[2] = 0;
+ new_point[3] = 0.25;
+ new_point[4] = 0;
+ new_point[5] = Animation::HANDLE_MODE_BALANCED;
- float time = ((menu_insert_key.x - timeline->get_name_limit()) / timeline->get_zoom_scale()) + timeline->get_value();
- while (animation->track_find_key(track, time, true) != -1) {
- time += 0.001;
- }
+ int limit = timeline->get_name_limit();
- undo_redo->create_action(TTR("Add Bezier Point"));
- undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, time, new_point);
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", track, time);
- undo_redo->commit_action();
+ float time = ((menu_insert_key.x - limit) / timeline->get_zoom_scale()) + timeline->get_value();
+
+ while (animation->track_find_key(selected_track, time, true) != -1) {
+ time += 0.001;
+ }
+
+ undo_redo->create_action(TTR("Add Bezier Point"));
+ undo_redo->add_do_method(animation.ptr(), "track_insert_key", selected_track, time, new_point);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", selected_track, time);
+ undo_redo->commit_action();
+ }
} break;
case MENU_KEY_DUPLICATE: {
@@ -1072,8 +1511,8 @@ void AnimationBezierTrackEdit::duplicate_selection() {
}
float top_time = 1e10;
- for (Set<int>::Element *E = selection.back(); E; E = E->prev()) {
- float t = animation->track_get_key_time(track, E->get());
+ for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
+ float t = animation->track_get_key_time(E->get().first, E->get().second);
if (t < top_time) {
top_time = t;
}
@@ -1083,21 +1522,21 @@ void AnimationBezierTrackEdit::duplicate_selection() {
List<Pair<int, float>> new_selection_values;
- for (Set<int>::Element *E = selection.back(); E; E = E->prev()) {
- float t = animation->track_get_key_time(track, E->get());
+ for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) {
+ float t = animation->track_get_key_time(E->get().first, E->get().second);
float dst_time = t + (timeline->get_play_position() - top_time);
- int existing_idx = animation->track_find_key(track, dst_time, true);
+ int existing_idx = animation->track_find_key(E->get().first, dst_time, true);
- undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, dst_time, animation->track_get_key_value(track, E->get()), animation->track_get_key_transition(track, E->get()));
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", track, dst_time);
+ undo_redo->add_do_method(animation.ptr(), "track_insert_key", E->get().first, dst_time, animation->track_get_key_value(E->get().first, E->get().second), animation->track_get_key_transition(E->get().first, E->get().second));
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", E->get().first, dst_time);
Pair<int, float> p;
- p.first = track;
+ p.first = E->get().first;
p.second = dst_time;
new_selection_values.push_back(p);
if (existing_idx != -1) {
- undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track, dst_time, animation->track_get_key_value(track, existing_idx), animation->track_get_key_transition(track, existing_idx));
+ undo_redo->add_undo_method(animation.ptr(), "track_insert_key", E->get().first, dst_time, animation->track_get_key_value(E->get().first, existing_idx), animation->track_get_key_transition(E->get().first, existing_idx));
}
}
@@ -1116,7 +1555,7 @@ void AnimationBezierTrackEdit::duplicate_selection() {
continue;
}
- selection.insert(existing_idx);
+ selection.insert(IntPair(track, existing_idx));
}
update();
@@ -1126,9 +1565,9 @@ void AnimationBezierTrackEdit::delete_selection() {
if (selection.size()) {
undo_redo->create_action(TTR("Anim Delete Keys"));
- for (Set<int>::Element *E = selection.back(); E; E = E->prev()) {
- undo_redo->add_do_method(animation.ptr(), "track_remove_key", track, E->get());
- undo_redo->add_undo_method(animation.ptr(), "track_insert_key", track, animation->track_get_key_time(track, E->get()), animation->track_get_key_value(track, E->get()), 1);
+ 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);
+ undo_redo->add_undo_method(animation.ptr(), "track_insert_key", E->get().first, animation->track_get_key_time(E->get().first, E->get().second), animation->track_get_key_value(E->get().first, E->get().second), 1);
}
undo_redo->add_do_method(this, "_clear_selection_for_anim", animation);
undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
@@ -1142,12 +1581,14 @@ void AnimationBezierTrackEdit::_bind_methods() {
ClassDB::bind_method("_clear_selection", &AnimationBezierTrackEdit::_clear_selection);
ClassDB::bind_method("_clear_selection_for_anim", &AnimationBezierTrackEdit::_clear_selection_for_anim);
ClassDB::bind_method("_select_at_anim", &AnimationBezierTrackEdit::_select_at_anim);
+ ClassDB::bind_method("_update_hidden_tracks_after", &AnimationBezierTrackEdit::_update_hidden_tracks_after);
+ ClassDB::bind_method("_update_locked_tracks_after", &AnimationBezierTrackEdit::_update_locked_tracks_after);
ADD_SIGNAL(MethodInfo("timeline_changed", PropertyInfo(Variant::FLOAT, "position"), PropertyInfo(Variant::BOOL, "drag")));
ADD_SIGNAL(MethodInfo("remove_request", PropertyInfo(Variant::INT, "track")));
ADD_SIGNAL(MethodInfo("insert_key", PropertyInfo(Variant::FLOAT, "ofs")));
- ADD_SIGNAL(MethodInfo("select_key", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::BOOL, "single")));
- ADD_SIGNAL(MethodInfo("deselect_key", PropertyInfo(Variant::INT, "index")));
+ ADD_SIGNAL(MethodInfo("select_key", PropertyInfo(Variant::INT, "track"), PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::BOOL, "single")));
+ ADD_SIGNAL(MethodInfo("deselect_key", PropertyInfo(Variant::INT, "track"), PropertyInfo(Variant::INT, "index")));
ADD_SIGNAL(MethodInfo("clear_selection"));
ADD_SIGNAL(MethodInfo("close_request"));
@@ -1170,14 +1611,9 @@ AnimationBezierTrackEdit::AnimationBezierTrackEdit() {
set_clip_contents(true);
- close_button = memnew(Button);
- close_button->connect("pressed", Callable(this, SNAME("emit_signal")), varray(SNAME("close_request")));
- close_button->set_text(TTR("Close"));
-
- right_column = memnew(VBoxContainer);
- right_column->add_child(close_button);
- right_column->add_spacer();
- add_child(right_column);
+ ED_SHORTCUT("animation_bezier_editor/focus", TTR("Focus"), Key::F);
+ ED_SHORTCUT("animation_bezier_editor/select_all_keys", TTR("Select All Keys"), KeyModifierMask::CMD | Key::A);
+ ED_SHORTCUT("animation_bezier_editor/deselect_all_keys", TTR("Deselect All Keys"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::A);
menu = memnew(PopupMenu);
add_child(menu);
diff --git a/editor/animation_bezier_editor.h b/editor/animation_bezier_editor.h
index cf719a0355..fa6fc405f2 100644
--- a/editor/animation_bezier_editor.h
+++ b/editor/animation_bezier_editor.h
@@ -46,9 +46,6 @@ class AnimationBezierTrackEdit : public Control {
MENU_KEY_SET_HANDLE_BALANCED,
};
- VBoxContainer *right_column;
- Button *close_button;
-
AnimationTimelineEdit *timeline = nullptr;
UndoRedo *undo_redo = nullptr;
Node *root = nullptr;
@@ -56,7 +53,7 @@ class AnimationBezierTrackEdit : public Control {
float play_position_pos = 0;
Ref<Animation> animation;
- int track;
+ int selected_track;
Vector<Rect2> view_rects;
@@ -66,6 +63,19 @@ class AnimationBezierTrackEdit : public Control {
Map<int, Rect2> subtracks;
+ enum {
+ REMOVE_ICON,
+ LOCK_ICON,
+ SOLO_ICON,
+ VISIBILITY_ICON
+ };
+
+ Map<int, Map<int, Rect2>> subtrack_icons;
+ Set<int> locked_tracks;
+ Set<int> hidden_tracks;
+ int solo_track = -1;
+ bool is_filtered = false;
+
float v_scroll = 0;
float v_zoom = 1;
@@ -73,6 +83,9 @@ class AnimationBezierTrackEdit : public Control {
void _zoom_changed();
+ void _update_locked_tracks_after(int p_track);
+ void _update_hidden_tracks_after(int p_track);
+
virtual void gui_input(const Ref<InputEvent> &p_event) override;
void _menu_selected(int p_index);
@@ -80,10 +93,13 @@ class AnimationBezierTrackEdit : public Control {
Vector2 insert_at_pos;
+ typedef Pair<int, int> IntPair;
+
bool moving_selection_attempt = false;
- int select_single_attempt = -1;
+ IntPair select_single_attempt;
bool moving_selection = false;
int moving_selection_from_key;
+ int moving_selection_from_track;
Vector2 moving_selection_offset;
@@ -95,6 +111,7 @@ class AnimationBezierTrackEdit : public Control {
int moving_handle = 0; //0 no move -1 or +1 out
int moving_handle_key = 0;
+ int moving_handle_track = 0;
Vector2 moving_handle_left;
Vector2 moving_handle_right;
int moving_handle_mode; // value from Animation::HandleMode
@@ -119,11 +136,25 @@ class AnimationBezierTrackEdit : public Control {
Rect2 point_rect;
Rect2 in_rect;
Rect2 out_rect;
+ int track;
+ int key;
};
Vector<EditPoint> edit_points;
- Set<int> selection;
+ struct SelectionCompare {
+ bool operator()(const IntPair &lh, const IntPair &rh) {
+ if (lh.first == rh.first) {
+ return lh.second < rh.second;
+ } else {
+ return lh.first < rh.first;
+ }
+ }
+ };
+
+ typedef Set<IntPair, SelectionCompare> SelectionSet;
+
+ SelectionSet selection;
Ref<ViewPanner> panner;
void _scroll_callback(Vector2 p_scroll_vec, bool p_alt);
@@ -151,6 +182,7 @@ public:
void set_timeline(AnimationTimelineEdit *p_timeline);
void set_editor(AnimationTrackEditor *p_editor);
void set_root(Node *p_root);
+ void set_filtered(bool p_filtered);
void set_play_position(float p_pos);
void update_play_position();
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index adb91fb08d..de924e84dc 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -2118,23 +2118,19 @@ void AnimationTrackEdit::_notification(int p_what) {
update_mode_rect.position.y = 0;
update_mode_rect.size.y = get_size().height;
- ofs += update_icon->get_width() + hsep;
- update_mode_rect.size.x += hsep;
+ ofs += update_icon->get_width() + hsep / 2;
+ update_mode_rect.size.x += hsep / 2;
if (animation->track_get_type(track) == Animation::TYPE_VALUE) {
draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2));
update_mode_rect.size.x += down_icon->get_width();
- bezier_edit_rect = Rect2();
} else if (animation->track_get_type(track) == Animation::TYPE_BEZIER) {
Ref<Texture2D> bezier_icon = get_theme_icon(SNAME("EditBezier"), SNAME("EditorIcons"));
update_mode_rect.size.x += down_icon->get_width();
- bezier_edit_rect.position = update_mode_rect.position + (update_mode_rect.size - bezier_icon->get_size()) / 2;
- bezier_edit_rect.size = bezier_icon->get_size();
- draw_texture(bezier_icon, bezier_edit_rect.position);
+
update_mode_rect = Rect2();
} else {
update_mode_rect = Rect2();
- bezier_edit_rect = Rect2();
}
ofs += down_icon->get_width();
@@ -2160,8 +2156,8 @@ void AnimationTrackEdit::_notification(int p_what) {
interp_mode_rect.position.y = 0;
interp_mode_rect.size.y = get_size().height;
- ofs += icon->get_width() + hsep;
- interp_mode_rect.size.x += hsep;
+ ofs += icon->get_width() + hsep / 2;
+ interp_mode_rect.size.x += hsep / 2;
if (!animation->track_is_compressed(track) && (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_BLEND_SHAPE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D)) {
draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2));
@@ -2193,8 +2189,8 @@ void AnimationTrackEdit::_notification(int p_what) {
loop_wrap_rect.position.y = 0;
loop_wrap_rect.size.y = get_size().height;
- ofs += icon->get_width() + hsep;
- loop_wrap_rect.size.x += hsep;
+ ofs += icon->get_width() + hsep / 2;
+ loop_wrap_rect.size.x += hsep / 2;
if (!animation->track_is_compressed(track) && (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_BLEND_SHAPE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D)) {
draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2));
@@ -2213,7 +2209,7 @@ void AnimationTrackEdit::_notification(int p_what) {
Ref<Texture2D> icon = get_theme_icon(animation->track_is_compressed(track) ? SNAME("Lock") : SNAME("Remove"), SNAME("EditorIcons"));
- remove_rect.position.x = ofs + ((get_size().width - ofs) - icon->get_width()) / 2;
+ remove_rect.position.x = ofs + ((get_size().width - ofs) - icon->get_width());
remove_rect.position.y = int(get_size().height - icon->get_height()) / 2;
remove_rect.size = icon->get_size();
@@ -2792,11 +2788,6 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
return;
}
- if (bezier_edit_rect.has_point(pos)) {
- emit_signal(SNAME("bezier_edit"));
- accept_event();
- }
-
// Check keyframes.
if (!animation->track_is_compressed(track)) { // Selecting compressed keyframes for editing is not possible.
@@ -3326,10 +3317,21 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim) {
snap->set_disabled(false);
snap_mode->set_disabled(false);
+ bezier_edit_icon->set_disabled(true);
+
imported_anim_warning->hide();
+ bool import_warning_done = false;
+ bool bezier_done = false;
for (int i = 0; i < animation->get_track_count(); i++) {
if (animation->track_is_imported(i)) {
imported_anim_warning->show();
+ import_warning_done = true;
+ }
+ if (animation->track_get_type(i) == Animation::TrackType::TYPE_BEZIER) {
+ bezier_edit_icon->set_disabled(false);
+ bezier_done = true;
+ }
+ if (import_warning_done && bezier_done) {
break;
}
}
@@ -3343,6 +3345,7 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim) {
step->set_read_only(true);
snap->set_disabled(true);
snap_mode->set_disabled(true);
+ bezier_edit_icon->set_disabled(true);
}
}
@@ -4167,13 +4170,15 @@ AnimationTrackEditor::TrackIndices AnimationTrackEditor::_confirm_insert(InsertD
} break;
case Animation::TYPE_BEZIER: {
Array array;
- array.resize(5);
+ array.resize(6);
array[0] = p_id.value;
array[1] = -0.25;
array[2] = 0;
array[3] = 0.25;
array[4] = 0;
+ array[5] = Animation::HANDLE_MODE_BALANCED;
value = array;
+ bezier_edit_icon->set_disabled(false);
} break;
case Animation::TYPE_ANIMATION: {
@@ -4399,7 +4404,6 @@ void AnimationTrackEditor::_update_tracks() {
track_edit->connect("insert_key", callable_mp(this, &AnimationTrackEditor::_insert_key_from_track), varray(i), CONNECT_DEFERRED);
track_edit->connect("select_key", callable_mp(this, &AnimationTrackEditor::_key_selected), varray(i), CONNECT_DEFERRED);
track_edit->connect("deselect_key", callable_mp(this, &AnimationTrackEditor::_key_deselected), varray(i), CONNECT_DEFERRED);
- track_edit->connect("bezier_edit", callable_mp(this, &AnimationTrackEditor::_bezier_edit), varray(i), CONNECT_DEFERRED);
track_edit->connect("move_selection_begin", callable_mp(this, &AnimationTrackEditor::_move_selection_begin));
track_edit->connect("move_selection", callable_mp(this, &AnimationTrackEditor::_move_selection));
track_edit->connect("move_selection_commit", callable_mp(this, &AnimationTrackEditor::_move_selection_commit));
@@ -4515,6 +4519,7 @@ void AnimationTrackEditor::_notification(int p_what) {
if (p_what == NOTIFICATION_THEME_CHANGED || p_what == NOTIFICATION_ENTER_TREE) {
zoom_icon->set_texture(get_theme_icon(SNAME("Zoom"), SNAME("EditorIcons")));
+ bezier_edit_icon->set_icon(get_theme_icon(SNAME("EditBezier"), SNAME("EditorIcons")));
snap->set_icon(get_theme_icon(SNAME("Snap"), SNAME("EditorIcons")));
view_group->set_icon(get_theme_icon(view_group->is_pressed() ? SNAME("AnimationTrackList") : SNAME("AnimationTrackGroup"), SNAME("EditorIcons")));
selected_filter->set_icon(get_theme_icon(SNAME("AnimationFilter"), SNAME("EditorIcons")));
@@ -4630,6 +4635,7 @@ void AnimationTrackEditor::_new_track_node_selected(NodePath p_path) {
adding_track_path = path_to;
prop_selector->set_type_filter(filter);
prop_selector->select_property_from_instance(node);
+ bezier_edit_icon->set_disabled(false);
} break;
case Animation::TYPE_AUDIO: {
if (!node->is_class("AudioStreamPlayer") && !node->is_class("AudioStreamPlayer2D") && !node->is_class("AudioStreamPlayer3D")) {
@@ -4946,7 +4952,7 @@ void AnimationTrackEditor::_add_method_key(const String &p_method) {
EditorNode::get_singleton()->show_warning(TTR("Method not found in object: ") + p_method);
}
-void AnimationTrackEditor::_key_selected(int p_key, bool p_single, int p_track) {
+void AnimationTrackEditor::_key_selected(int p_track, int p_key, bool p_single) {
ERR_FAIL_INDEX(p_track, animation->get_track_count());
ERR_FAIL_INDEX(p_key, animation->track_get_key_count(p_track));
@@ -5294,6 +5300,20 @@ void AnimationTrackEditor::_scroll_input(const Ref<InputEvent> &p_event) {
}
}
+void AnimationTrackEditor::_toggle_bezier_edit() {
+ if (bezier_edit->is_visible()) {
+ _cancel_bezier_edit();
+ } else {
+ int track_count = animation->get_track_count();
+ for (int i = 0; i < track_count; ++i) {
+ if (animation->track_get_type(i) == Animation::TrackType::TYPE_BEZIER) {
+ _bezier_edit(i);
+ return;
+ }
+ }
+ }
+}
+
void AnimationTrackEditor::_scroll_callback(Vector2 p_scroll_vec, bool p_alt) {
if (p_alt) {
if (p_scroll_vec.x < 0 || p_scroll_vec.y < 0) {
@@ -5322,6 +5342,7 @@ void AnimationTrackEditor::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin
void AnimationTrackEditor::_cancel_bezier_edit() {
bezier_edit->hide();
scroll->show();
+ bezier_edit_icon->set_pressed(false);
}
void AnimationTrackEditor::_bezier_edit(int p_for_track) {
@@ -5908,6 +5929,7 @@ void AnimationTrackEditor::_cleanup_animation(Ref<Animation> p_animation) {
void AnimationTrackEditor::_view_group_toggle() {
_update_tracks();
view_group->set_icon(get_theme_icon(view_group->is_pressed() ? SNAME("AnimationTrackList") : SNAME("AnimationTrackGroup"), SNAME("EditorIcons")));
+ bezier_edit->set_filtered(selected_filter->is_pressed());
}
bool AnimationTrackEditor::is_grouping_tracks() {
@@ -6153,6 +6175,15 @@ AnimationTrackEditor::AnimationTrackEditor() {
bottom_hb->add_spacer();
+ bezier_edit_icon = memnew(Button);
+ bezier_edit_icon->set_flat(true);
+ bezier_edit_icon->set_disabled(true);
+ bezier_edit_icon->set_toggle_mode(true);
+ bezier_edit_icon->connect("pressed", callable_mp(this, &AnimationTrackEditor::_toggle_bezier_edit));
+ bezier_edit_icon->set_tooltip(TTR("Toggle between the bezier curve editor and track editor."));
+
+ bottom_hb->add_child(bezier_edit_icon);
+
selected_filter = memnew(Button);
selected_filter->set_flat(true);
selected_filter->connect("pressed", callable_mp(this, &AnimationTrackEditor::_view_group_toggle)); // Same function works the same.
diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h
index 50c5c692c0..edba784310 100644
--- a/editor/animation_track_editor.h
+++ b/editor/animation_track_editor.h
@@ -164,7 +164,6 @@ class AnimationTrackEdit : public Control {
Rect2 interp_mode_rect;
Rect2 loop_wrap_rect;
Rect2 remove_rect;
- Rect2 bezier_edit_rect;
Ref<Texture2D> type_icon;
Ref<Texture2D> selected_icon;
@@ -300,6 +299,7 @@ class AnimationTrackEditor : public VBoxContainer {
EditorSpinSlider *step;
TextureRect *zoom_icon;
Button *snap;
+ Button *bezier_edit_icon;
OptionButton *snap_mode;
Button *imported_anim_warning;
@@ -406,7 +406,7 @@ class AnimationTrackEditor : public VBoxContainer {
Map<SelectedKey, KeyInfo> selection;
- void _key_selected(int p_key, bool p_single, int p_track);
+ void _key_selected(int p_track, int p_key, bool p_single);
void _key_deselected(int p_key, int p_track);
bool moving_selection;
@@ -431,6 +431,7 @@ class AnimationTrackEditor : public VBoxContainer {
Vector<Ref<AnimationTrackEditPlugin>> track_edit_plugins;
+ void _toggle_bezier_edit();
void _cancel_bezier_edit();
void _bezier_edit(int p_for_track);
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 0e362b13c6..a4f9563840 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -1560,7 +1560,7 @@ void SceneTreeDock::perform_node_renames(Node *p_base, Map<Node *, NodePath> *p_
for (int i = 0; i < anim->get_track_count(); i++) {
NodePath track_np = anim->track_get_path(i);
- Node *n = root->get_node(track_np);
+ Node *n = root->get_node_or_null(track_np);
if (!n) {
continue;
}