diff options
Diffstat (limited to 'editor/animation_track_editor.cpp')
-rw-r--r-- | editor/animation_track_editor.cpp | 1017 |
1 files changed, 537 insertions, 480 deletions
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index e36d0b846b..11995e8cb4 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -33,9 +33,9 @@ #include "animation_track_editor_plugins.h" #include "core/input/input.h" #include "editor/animation_bezier_editor.h" +#include "editor/editor_node.h" +#include "editor/editor_scale.h" #include "editor/plugins/animation_player_editor_plugin.h" -#include "editor_node.h" -#include "editor_scale.h" #include "scene/animation/animation_player.h" #include "scene/gui/view_panner.h" #include "scene/main/window.h" @@ -570,7 +570,7 @@ public: p_list->push_back(PropertyInfo(Variant::VECTOR3, "position")); } break; case Animation::TYPE_ROTATION_3D: { - p_list->push_back(PropertyInfo(Variant::VECTOR3, "rotation")); + p_list->push_back(PropertyInfo(Variant::QUATERNION, "rotation")); } break; case Animation::TYPE_SCALE_3D: { p_list->push_back(PropertyInfo(Variant::VECTOR3, "scale")); @@ -606,8 +606,7 @@ public: } break; case Animation::TYPE_METHOD: { p_list->push_back(PropertyInfo(Variant::STRING_NAME, "name")); - static_assert(VARIANT_ARG_MAX == 8, "PROPERTY_HINT_RANGE needs to be updated if VARIANT_ARG_MAX != 8"); - p_list->push_back(PropertyInfo(Variant::INT, "arg_count", PROPERTY_HINT_RANGE, "0,8,1")); + p_list->push_back(PropertyInfo(Variant::INT, "arg_count", PROPERTY_HINT_RANGE, "0,32,1,or_greater")); Dictionary d = animation->track_get_key_value(track, key); ERR_FAIL_COND(!d.has("args")); @@ -1287,8 +1286,8 @@ public: } break; case Animation::TYPE_METHOD: { p_list->push_back(PropertyInfo(Variant::STRING_NAME, "name")); - static_assert(VARIANT_ARG_MAX == 8, "PROPERTY_HINT_RANGE needs to be updated if VARIANT_ARG_MAX != 8"); - p_list->push_back(PropertyInfo(Variant::INT, "arg_count", PROPERTY_HINT_RANGE, "0,8,1")); + + p_list->push_back(PropertyInfo(Variant::INT, "arg_count", PROPERTY_HINT_RANGE, "0,32,1,or_greater")); Dictionary d = animation->track_get_key_value(first_track, first_key); ERR_FAIL_COND(!d.has("args")); @@ -1458,198 +1457,202 @@ int AnimationTimelineEdit::get_name_limit() const { } void AnimationTimelineEdit::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE || p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) { - 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_ENTER_TREE) { - add_track->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); - loop->set_icon(get_theme_icon(SNAME("Loop"), SNAME("EditorIcons"))); - time_icon->set_texture(get_theme_icon(SNAME("Time"), SNAME("EditorIcons"))); - - add_track->get_popup()->clear(); - add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyValue"), SNAME("EditorIcons")), TTR("Property Track")); - add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXPosition"), SNAME("EditorIcons")), TTR("3D Position Track")); - add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXRotation"), SNAME("EditorIcons")), TTR("3D Rotation Track")); - add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXScale"), SNAME("EditorIcons")), TTR("3D Scale Track")); - add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyBlendShape"), SNAME("EditorIcons")), TTR("Blend Shape Track")); - add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyCall"), SNAME("EditorIcons")), TTR("Call Method Track")); - add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyBezier"), SNAME("EditorIcons")), TTR("Bezier Curve Track")); - add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyAudio"), SNAME("EditorIcons")), TTR("Audio Playback Track")); - add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyAnimation"), SNAME("EditorIcons")), TTR("Animation Playback Track")); - } + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: { + 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"))); + add_track->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); + loop->set_icon(get_theme_icon(SNAME("Loop"), SNAME("EditorIcons"))); + time_icon->set_texture(get_theme_icon(SNAME("Time"), SNAME("EditorIcons"))); + + add_track->get_popup()->clear(); + add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyValue"), SNAME("EditorIcons")), TTR("Property Track")); + add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXPosition"), SNAME("EditorIcons")), TTR("3D Position Track")); + add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXRotation"), SNAME("EditorIcons")), TTR("3D Rotation Track")); + add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyXScale"), SNAME("EditorIcons")), TTR("3D Scale Track")); + add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyBlendShape"), SNAME("EditorIcons")), TTR("Blend Shape Track")); + add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyCall"), SNAME("EditorIcons")), TTR("Call Method Track")); + add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyBezier"), SNAME("EditorIcons")), TTR("Bezier Curve Track")); + add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyAudio"), SNAME("EditorIcons")), TTR("Audio Playback Track")); + add_track->get_popup()->add_icon_item(get_theme_icon(SNAME("KeyAnimation"), SNAME("EditorIcons")), TTR("Animation Playback Track")); + } break; - if (p_what == NOTIFICATION_RESIZED) { - len_hb->set_position(Vector2(get_size().width - get_buttons_width(), 0)); - len_hb->set_size(Size2(get_buttons_width(), get_size().height)); - } + case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { + 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"))); + } break; - if (p_what == NOTIFICATION_DRAW) { - int key_range = get_size().width - get_buttons_width() - get_name_limit(); + case NOTIFICATION_RESIZED: { + len_hb->set_position(Vector2(get_size().width - get_buttons_width(), 0)); + len_hb->set_size(Size2(get_buttons_width(), get_size().height)); + } break; - if (!animation.is_valid()) { - return; - } + case NOTIFICATION_DRAW: { + int key_range = get_size().width - get_buttons_width() - get_name_limit(); - Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label")); - int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label")); - Color color = get_theme_color(SNAME("font_color"), SNAME("Label")); + if (!animation.is_valid()) { + return; + } - int zoomw = key_range; - float scale = get_zoom_scale(); - int h = get_size().height; + Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label")); + int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label")); + Color color = get_theme_color(SNAME("font_color"), SNAME("Label")); - float l = animation->get_length(); - if (l <= 0) { - l = 0.001; // Avoid crashor. - } + int zoomw = key_range; + float scale = get_zoom_scale(); + int h = get_size().height; - Ref<Texture2D> hsize_icon = get_theme_icon(SNAME("Hsize"), SNAME("EditorIcons")); - hsize_rect = Rect2(get_name_limit() - hsize_icon->get_width() - 2 * EDSCALE, (get_size().height - hsize_icon->get_height()) / 2, hsize_icon->get_width(), hsize_icon->get_height()); - draw_texture(hsize_icon, hsize_rect.position); + float l = animation->get_length(); + if (l <= 0) { + l = 0.001; // Avoid crashor. + } - { - float time_min = 0; - float time_max = animation->get_length(); - for (int i = 0; i < animation->get_track_count(); i++) { - if (animation->track_get_key_count(i) > 0) { - float beg = animation->track_get_key_time(i, 0); + Ref<Texture2D> hsize_icon = get_theme_icon(SNAME("Hsize"), SNAME("EditorIcons")); + hsize_rect = Rect2(get_name_limit() - hsize_icon->get_width() - 2 * EDSCALE, (get_size().height - hsize_icon->get_height()) / 2, hsize_icon->get_width(), hsize_icon->get_height()); + draw_texture(hsize_icon, hsize_rect.position); - if (beg < time_min) { - time_min = beg; - } + { + float time_min = 0; + float time_max = animation->get_length(); + for (int i = 0; i < animation->get_track_count(); i++) { + if (animation->track_get_key_count(i) > 0) { + float beg = animation->track_get_key_time(i, 0); + + if (beg < time_min) { + time_min = beg; + } - float end = animation->track_get_key_time(i, animation->track_get_key_count(i) - 1); + float end = animation->track_get_key_time(i, animation->track_get_key_count(i) - 1); - if (end > time_max) { - time_max = end; + if (end > time_max) { + time_max = end; + } } } - } - float extra = (zoomw / scale) * 0.5; + float extra = (zoomw / scale) * 0.5; - time_max += extra; - set_min(time_min); - set_max(time_max); + time_max += extra; + set_min(time_min); + set_max(time_max); - if (zoomw / scale < (time_max - time_min)) { - hscroll->show(); + if (zoomw / scale < (time_max - time_min)) { + hscroll->show(); - } else { - hscroll->hide(); + } else { + hscroll->hide(); + } } - } - set_page(zoomw / scale); + set_page(zoomw / scale); - int end_px = (l - get_value()) * scale; - int begin_px = -get_value() * scale; - Color notimecol = get_theme_color(SNAME("dark_color_2"), SNAME("Editor")); - Color timecolor = color; - timecolor.a = 0.2; - Color linecolor = color; - linecolor.a = 0.2; + int end_px = (l - get_value()) * scale; + int begin_px = -get_value() * scale; + Color notimecol = get_theme_color(SNAME("dark_color_2"), SNAME("Editor")); + Color timecolor = color; + timecolor.a = 0.2; + Color linecolor = color; + linecolor.a = 0.2; - { - draw_rect(Rect2(Point2(get_name_limit(), 0), Point2(zoomw - 1, h)), notimecol); + { + draw_rect(Rect2(Point2(get_name_limit(), 0), Point2(zoomw - 1, h)), notimecol); - if (begin_px < zoomw && end_px > 0) { - if (begin_px < 0) { - begin_px = 0; - } - if (end_px > zoomw) { - end_px = zoomw; - } + if (begin_px < zoomw && end_px > 0) { + if (begin_px < 0) { + begin_px = 0; + } + if (end_px > zoomw) { + end_px = zoomw; + } - draw_rect(Rect2(Point2(get_name_limit() + begin_px, 0), Point2(end_px - begin_px - 1, h)), timecolor); + draw_rect(Rect2(Point2(get_name_limit() + begin_px, 0), Point2(end_px - begin_px - 1, h)), timecolor); + } } - } - Color color_time_sec = color; - Color color_time_dec = color; - color_time_dec.a *= 0.5; + Color color_time_sec = color; + Color color_time_dec = color; + color_time_dec.a *= 0.5; #define SC_ADJ 100 - int min = 30; - int dec = 1; - int step = 1; - int decimals = 2; - bool step_found = false; - - const int period_width = font->get_char_size('.', 0, font_size).width; - int max_digit_width = font->get_char_size('0', 0, font_size).width; - for (int i = 1; i <= 9; i++) { - const int digit_width = font->get_char_size('0' + i, 0, font_size).width; - max_digit_width = MAX(digit_width, max_digit_width); - } - const int max_sc = int(Math::ceil(zoomw / scale)); - const int max_sc_width = String::num(max_sc).length() * max_digit_width; - - while (!step_found) { - min = max_sc_width; - if (decimals > 0) { - min += period_width + max_digit_width * decimals; + int min = 30; + int dec = 1; + int step = 1; + int decimals = 2; + bool step_found = false; + + const float period_width = font->get_char_size('.', 0, font_size).width; + float max_digit_width = font->get_char_size('0', 0, font_size).width; + for (int i = 1; i <= 9; i++) { + const float digit_width = font->get_char_size('0' + i, 0, font_size).width; + max_digit_width = MAX(digit_width, max_digit_width); } + const int max_sc = int(Math::ceil(zoomw / scale)); + const int max_sc_width = String::num(max_sc).length() * max_digit_width; - static const int _multp[3] = { 1, 2, 5 }; - for (int i = 0; i < 3; i++) { - step = (_multp[i] * dec); - if (step * scale / SC_ADJ > min) { - step_found = true; + while (!step_found) { + min = max_sc_width; + if (decimals > 0) { + min += period_width + max_digit_width * decimals; + } + + static const int _multp[3] = { 1, 2, 5 }; + for (int i = 0; i < 3; i++) { + step = (_multp[i] * dec); + if (step * scale / SC_ADJ > min) { + step_found = true; + break; + } + } + if (step_found) { break; } + dec *= 10; + decimals--; + if (decimals < 0) { + decimals = 0; + } } - if (step_found) { - break; - } - dec *= 10; - decimals--; - if (decimals < 0) { - decimals = 0; - } - } - if (use_fps) { - float step_size = animation->get_step(); - if (step_size > 0) { - int prev_frame_ofs = -10000000; + if (use_fps) { + float step_size = animation->get_step(); + if (step_size > 0) { + int prev_frame_ofs = -10000000; - for (int i = 0; i < zoomw; i++) { - float pos = get_value() + double(i) / scale; - float prev = get_value() + (double(i) - 1.0) / scale; + for (int i = 0; i < zoomw; i++) { + float pos = get_value() + double(i) / scale; + float prev = get_value() + (double(i) - 1.0) / scale; - int frame = pos / step_size; - int prev_frame = prev / step_size; + int frame = pos / step_size; + int prev_frame = prev / step_size; - bool sub = Math::floor(prev) == Math::floor(pos); + bool sub = Math::floor(prev) == Math::floor(pos); - if (frame != prev_frame && i >= prev_frame_ofs) { - draw_line(Point2(get_name_limit() + i, 0), Point2(get_name_limit() + i, h), linecolor, Math::round(EDSCALE)); + if (frame != prev_frame && i >= prev_frame_ofs) { + draw_line(Point2(get_name_limit() + i, 0), Point2(get_name_limit() + i, h), linecolor, Math::round(EDSCALE)); - draw_string(font, Point2(get_name_limit() + i + 3 * EDSCALE, (h - font->get_height(font_size)) / 2 + font->get_ascent(font_size)).floor(), itos(frame), HORIZONTAL_ALIGNMENT_LEFT, zoomw - i, font_size, sub ? color_time_dec : color_time_sec); - prev_frame_ofs = i + font->get_string_size(itos(frame), font_size).x + 5 * EDSCALE; + draw_string(font, Point2(get_name_limit() + i + 3 * EDSCALE, (h - font->get_height(font_size)) / 2 + font->get_ascent(font_size)).floor(), itos(frame), HORIZONTAL_ALIGNMENT_LEFT, zoomw - i, font_size, sub ? color_time_dec : color_time_sec); + prev_frame_ofs = i + font->get_string_size(itos(frame), font_size).x + 5 * EDSCALE; + } } } - } - } else { - for (int i = 0; i < zoomw; i++) { - float pos = get_value() + double(i) / scale; - float prev = get_value() + (double(i) - 1.0) / scale; - - int sc = int(Math::floor(pos * SC_ADJ)); - int prev_sc = int(Math::floor(prev * SC_ADJ)); - bool sub = (sc % SC_ADJ); - - if ((sc / step) != (prev_sc / step) || (prev_sc < 0 && sc >= 0)) { - int scd = sc < 0 ? prev_sc : sc; - draw_line(Point2(get_name_limit() + i, 0), Point2(get_name_limit() + i, h), linecolor, Math::round(EDSCALE)); - draw_string(font, Point2(get_name_limit() + i + 3, (h - font->get_height(font_size)) / 2 + font->get_ascent(font_size)).floor(), String::num((scd - (scd % step)) / double(SC_ADJ), decimals), HORIZONTAL_ALIGNMENT_LEFT, zoomw - i, font_size, sub ? color_time_dec : color_time_sec); + } else { + for (int i = 0; i < zoomw; i++) { + float pos = get_value() + double(i) / scale; + float prev = get_value() + (double(i) - 1.0) / scale; + + int sc = int(Math::floor(pos * SC_ADJ)); + int prev_sc = int(Math::floor(prev * SC_ADJ)); + bool sub = (sc % SC_ADJ); + + if ((sc / step) != (prev_sc / step) || (prev_sc < 0 && sc >= 0)) { + int scd = sc < 0 ? prev_sc : sc; + draw_line(Point2(get_name_limit() + i, 0), Point2(get_name_limit() + i, h), linecolor, Math::round(EDSCALE)); + draw_string(font, Point2(get_name_limit() + i + 3, (h - font->get_height(font_size)) / 2 + font->get_ascent(font_size)).floor(), String::num((scd - (scd % step)) / double(SC_ADJ), decimals), HORIZONTAL_ALIGNMENT_LEFT, zoomw - i, font_size, sub ? color_time_dec : color_time_sec); + } } } - } - draw_line(Vector2(0, get_size().height), get_size(), linecolor, Math::round(EDSCALE)); + draw_line(Vector2(0, get_size().height), get_size(), linecolor, Math::round(EDSCALE)); + } break; } } @@ -1723,15 +1726,15 @@ void AnimationTimelineEdit::update_values() { switch (animation->get_loop_mode()) { case Animation::LoopMode::LOOP_NONE: { - loop->set_icon(get_theme_icon("Loop", "EditorIcons")); + loop->set_icon(get_theme_icon(SNAME("Loop"), SNAME("EditorIcons"))); loop->set_pressed(false); } break; case Animation::LoopMode::LOOP_LINEAR: { - loop->set_icon(get_theme_icon("Loop", "EditorIcons")); + loop->set_icon(get_theme_icon(SNAME("Loop"), SNAME("EditorIcons"))); loop->set_pressed(true); } break; case Animation::LoopMode::LOOP_PINGPONG: { - loop->set_icon(get_theme_icon("PingPongLoop", "EditorIcons")); + loop->set_icon(get_theme_icon(SNAME("PingPongLoop"), SNAME("EditorIcons"))); loop->set_pressed(true); } break; default: @@ -1844,11 +1847,14 @@ void AnimationTimelineEdit::_pan_callback(Vector2 p_scroll_vec) { } void AnimationTimelineEdit::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) { - if (p_scroll_vec.y < 0) { - get_zoom()->set_value(get_zoom()->get_value() * 1.05); + double new_zoom_value; + double current_zoom_value = get_zoom()->get_value(); + if (current_zoom_value <= 0.1) { + new_zoom_value = MAX(0.01, current_zoom_value - 0.01 * SIGN(p_scroll_vec.y)); } else { - get_zoom()->set_value(get_zoom()->get_value() / 1.05); + new_zoom_value = p_scroll_vec.y > 0 ? MAX(0.01, current_zoom_value / 1.05) : current_zoom_value * 1.05; } + get_zoom()->set_value(new_zoom_value); } void AnimationTimelineEdit::set_use_fps(bool p_use_fps) { @@ -1939,306 +1945,304 @@ AnimationTimelineEdit::AnimationTimelineEdit() { //////////////////////////////////// void AnimationTrackEdit::_notification(int p_what) { - if (p_what == NOTIFICATION_DRAW) { - if (animation.is_null()) { - return; - } - ERR_FAIL_INDEX(track, animation->get_track_count()); + switch (p_what) { + case NOTIFICATION_THEME_CHANGED: { + if (animation.is_null()) { + return; + } + ERR_FAIL_INDEX(track, animation->get_track_count()); - int limit = timeline->get_name_limit(); + type_icon = _get_key_type_icon(); + selected_icon = get_theme_icon(SNAME("KeySelected"), SNAME("EditorIcons")); + } break; - if (has_focus()) { - Color accent = get_theme_color(SNAME("accent_color"), SNAME("Editor")); - accent.a *= 0.7; - // Offside so the horizontal sides aren't cutoff. - draw_rect(Rect2(Point2(1 * EDSCALE, 0), get_size() - Size2(1 * EDSCALE, 0)), accent, false); - } + case NOTIFICATION_DRAW: { + if (animation.is_null()) { + return; + } + ERR_FAIL_INDEX(track, animation->get_track_count()); - Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label")); - int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label")); - Color color = get_theme_color(SNAME("font_color"), SNAME("Label")); - Ref<Texture2D> type_icons[9] = { - get_theme_icon(SNAME("KeyValue"), SNAME("EditorIcons")), - get_theme_icon(SNAME("KeyTrackPosition"), SNAME("EditorIcons")), - get_theme_icon(SNAME("KeyTrackRotation"), SNAME("EditorIcons")), - get_theme_icon(SNAME("KeyTrackScale"), SNAME("EditorIcons")), - get_theme_icon(SNAME("KeyTrackBlendShape"), SNAME("EditorIcons")), - get_theme_icon(SNAME("KeyCall"), SNAME("EditorIcons")), - get_theme_icon(SNAME("KeyBezier"), SNAME("EditorIcons")), - get_theme_icon(SNAME("KeyAudio"), SNAME("EditorIcons")), - get_theme_icon(SNAME("KeyAnimation"), SNAME("EditorIcons")) - }; - int hsep = get_theme_constant(SNAME("hseparation"), SNAME("ItemList")); - Color linecolor = color; - linecolor.a = 0.2; + int limit = timeline->get_name_limit(); - // NAMES AND ICONS // + if (has_focus()) { + Color accent = get_theme_color(SNAME("accent_color"), SNAME("Editor")); + accent.a *= 0.7; + // Offside so the horizontal sides aren't cutoff. + draw_rect(Rect2(Point2(1 * EDSCALE, 0), get_size() - Size2(1 * EDSCALE, 0)), accent, false); + } - { - Ref<Texture2D> check = animation->track_is_enabled(track) ? get_theme_icon(SNAME("checked"), SNAME("CheckBox")) : get_theme_icon(SNAME("unchecked"), SNAME("CheckBox")); + Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label")); + int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label")); + Color color = get_theme_color(SNAME("font_color"), SNAME("Label")); + int hsep = get_theme_constant(SNAME("hseparation"), SNAME("ItemList")); + Color linecolor = color; + linecolor.a = 0.2; - int ofs = in_group ? check->get_width() : 0; // Not the best reference for margin but.. + // NAMES AND ICONS // - check_rect = Rect2(Point2(ofs, int(get_size().height - check->get_height()) / 2), check->get_size()); - draw_texture(check, check_rect.position); - ofs += check->get_width() + hsep; + { + Ref<Texture2D> check = animation->track_is_enabled(track) ? get_theme_icon(SNAME("checked"), SNAME("CheckBox")) : get_theme_icon(SNAME("unchecked"), SNAME("CheckBox")); - Ref<Texture2D> type_icon = type_icons[animation->track_get_type(track)]; - draw_texture(type_icon, Point2(ofs, int(get_size().height - type_icon->get_height()) / 2)); - ofs += type_icon->get_width() + hsep; + int ofs = in_group ? check->get_width() : 0; // Not the best reference for margin but.. - NodePath path = animation->track_get_path(track); - Node *node = nullptr; - if (root && root->has_node(path)) { - node = root->get_node(path); - } + check_rect = Rect2(Point2(ofs, int(get_size().height - check->get_height()) / 2), check->get_size()); + draw_texture(check, check_rect.position); + ofs += check->get_width() + hsep; - String text; - Color text_color = color; - if (node && EditorNode::get_singleton()->get_editor_selection()->is_selected(node)) { - text_color = get_theme_color(SNAME("accent_color"), SNAME("Editor")); - } + Ref<Texture2D> type_icon = _get_key_type_icon(); + draw_texture(type_icon, Point2(ofs, int(get_size().height - type_icon->get_height()) / 2)); + ofs += type_icon->get_width() + hsep; - if (in_group) { - if (animation->track_get_type(track) == Animation::TYPE_METHOD) { - text = TTR("Functions:"); - } else if (animation->track_get_type(track) == Animation::TYPE_AUDIO) { - text = TTR("Audio Clips:"); - } else if (animation->track_get_type(track) == Animation::TYPE_ANIMATION) { - text = TTR("Anim Clips:"); - } else { - text += path.get_concatenated_subnames(); + NodePath path = animation->track_get_path(track); + Node *node = nullptr; + if (root && root->has_node(path)) { + node = root->get_node(path); } - text_color.a *= 0.7; - } else if (node) { - Ref<Texture2D> icon = EditorNode::get_singleton()->get_object_icon(node, "Node"); - draw_texture(icon, Point2(ofs, int(get_size().height - icon->get_height()) / 2)); - icon_cache = icon; + String text; + Color text_color = color; + if (node && EditorNode::get_singleton()->get_editor_selection()->is_selected(node)) { + text_color = get_theme_color(SNAME("accent_color"), SNAME("Editor")); + } - text = String() + node->get_name() + ":" + path.get_concatenated_subnames(); - ofs += hsep; - ofs += icon->get_width(); + if (in_group) { + if (animation->track_get_type(track) == Animation::TYPE_METHOD) { + text = TTR("Functions:"); + } else if (animation->track_get_type(track) == Animation::TYPE_AUDIO) { + text = TTR("Audio Clips:"); + } else if (animation->track_get_type(track) == Animation::TYPE_ANIMATION) { + text = TTR("Anim Clips:"); + } else { + text += path.get_concatenated_subnames(); + } + text_color.a *= 0.7; + } else if (node) { + Ref<Texture2D> icon = EditorNode::get_singleton()->get_object_icon(node, "Node"); - } else { - icon_cache = type_icon; + draw_texture(icon, Point2(ofs, int(get_size().height - icon->get_height()) / 2)); + icon_cache = icon; - text = path; - } + text = String() + node->get_name() + ":" + path.get_concatenated_subnames(); + ofs += hsep; + ofs += icon->get_width(); - path_cache = text; + } else { + icon_cache = type_icon; - path_rect = Rect2(ofs, 0, limit - ofs - hsep, get_size().height); + text = path; + } - Vector2 string_pos = Point2(ofs, (get_size().height - font->get_height(font_size)) / 2 + font->get_ascent(font_size)); - string_pos = string_pos.floor(); - draw_string(font, string_pos, text, HORIZONTAL_ALIGNMENT_LEFT, limit - ofs - hsep, font_size, text_color); + path_cache = text; - draw_line(Point2(limit, 0), Point2(limit, get_size().height), linecolor, Math::round(EDSCALE)); - } + path_rect = Rect2(ofs, 0, limit - ofs - hsep, get_size().height); + + Vector2 string_pos = Point2(ofs, (get_size().height - font->get_height(font_size)) / 2 + font->get_ascent(font_size)); + string_pos = string_pos.floor(); + draw_string(font, string_pos, text, HORIZONTAL_ALIGNMENT_LEFT, limit - ofs - hsep, font_size, text_color); - // KEYFRAMES // + draw_line(Point2(limit, 0), Point2(limit, get_size().height), linecolor, Math::round(EDSCALE)); + } - draw_bg(limit, get_size().width - timeline->get_buttons_width()); + // KEYFRAMES // - { - float scale = timeline->get_zoom_scale(); - int limit_end = get_size().width - timeline->get_buttons_width(); + draw_bg(limit, get_size().width - timeline->get_buttons_width()); - for (int i = 0; i < animation->track_get_key_count(track); i++) { - float offset = animation->track_get_key_time(track, i) - timeline->get_value(); - if (editor->is_key_selected(track, i) && editor->is_moving_selection()) { - offset = editor->snap_time(offset + editor->get_moving_selection_offset(), true); - } - offset = offset * scale + limit; - if (i < animation->track_get_key_count(track) - 1) { - float offset_n = animation->track_get_key_time(track, i + 1) - timeline->get_value(); - if (editor->is_key_selected(track, i + 1) && editor->is_moving_selection()) { - offset_n = editor->snap_time(offset_n + editor->get_moving_selection_offset()); + { + float scale = timeline->get_zoom_scale(); + int limit_end = get_size().width - timeline->get_buttons_width(); + + for (int i = 0; i < animation->track_get_key_count(track); i++) { + float offset = animation->track_get_key_time(track, i) - timeline->get_value(); + if (editor->is_key_selected(track, i) && editor->is_moving_selection()) { + offset = editor->snap_time(offset + editor->get_moving_selection_offset(), true); } - offset_n = offset_n * scale + limit; + offset = offset * scale + limit; + if (i < animation->track_get_key_count(track) - 1) { + float offset_n = animation->track_get_key_time(track, i + 1) - timeline->get_value(); + if (editor->is_key_selected(track, i + 1) && editor->is_moving_selection()) { + offset_n = editor->snap_time(offset_n + editor->get_moving_selection_offset()); + } + offset_n = offset_n * scale + limit; - draw_key_link(i, scale, int(offset), int(offset_n), limit, limit_end); - } + draw_key_link(i, scale, int(offset), int(offset_n), limit, limit_end); + } - draw_key(i, scale, int(offset), editor->is_key_selected(track, i), limit, limit_end); + draw_key(i, scale, int(offset), editor->is_key_selected(track, i), limit, limit_end); + } } - } - draw_fg(limit, get_size().width - timeline->get_buttons_width()); + draw_fg(limit, get_size().width - timeline->get_buttons_width()); - // BUTTONS // + // BUTTONS // - { - Ref<Texture2D> wrap_icon[2] = { - get_theme_icon(SNAME("InterpWrapClamp"), SNAME("EditorIcons")), - get_theme_icon(SNAME("InterpWrapLoop"), SNAME("EditorIcons")), - }; - - Ref<Texture2D> interp_icon[3] = { - get_theme_icon(SNAME("InterpRaw"), SNAME("EditorIcons")), - get_theme_icon(SNAME("InterpLinear"), SNAME("EditorIcons")), - get_theme_icon(SNAME("InterpCubic"), SNAME("EditorIcons")) - }; - Ref<Texture2D> cont_icon[4] = { - get_theme_icon(SNAME("TrackContinuous"), SNAME("EditorIcons")), - get_theme_icon(SNAME("TrackDiscrete"), SNAME("EditorIcons")), - get_theme_icon(SNAME("TrackTrigger"), SNAME("EditorIcons")), - get_theme_icon(SNAME("TrackCapture"), SNAME("EditorIcons")) - }; - - int ofs = get_size().width - timeline->get_buttons_width(); - - Ref<Texture2D> down_icon = get_theme_icon(SNAME("select_arrow"), SNAME("Tree")); - - draw_line(Point2(ofs, 0), Point2(ofs, get_size().height), linecolor, Math::round(EDSCALE)); - - ofs += hsep; { - // Callmode. + Ref<Texture2D> wrap_icon[2] = { + get_theme_icon(SNAME("InterpWrapClamp"), SNAME("EditorIcons")), + get_theme_icon(SNAME("InterpWrapLoop"), SNAME("EditorIcons")), + }; - Animation::UpdateMode update_mode; + Ref<Texture2D> interp_icon[3] = { + get_theme_icon(SNAME("InterpRaw"), SNAME("EditorIcons")), + get_theme_icon(SNAME("InterpLinear"), SNAME("EditorIcons")), + get_theme_icon(SNAME("InterpCubic"), SNAME("EditorIcons")) + }; + Ref<Texture2D> cont_icon[4] = { + get_theme_icon(SNAME("TrackContinuous"), SNAME("EditorIcons")), + get_theme_icon(SNAME("TrackDiscrete"), SNAME("EditorIcons")), + get_theme_icon(SNAME("TrackTrigger"), SNAME("EditorIcons")), + get_theme_icon(SNAME("TrackCapture"), SNAME("EditorIcons")) + }; - if (animation->track_get_type(track) == Animation::TYPE_VALUE) { - update_mode = animation->value_track_get_update_mode(track); - } else { - update_mode = Animation::UPDATE_CONTINUOUS; - } - - Ref<Texture2D> update_icon = cont_icon[update_mode]; + int ofs = get_size().width - timeline->get_buttons_width(); - update_mode_rect.position.x = ofs; - update_mode_rect.position.y = int(get_size().height - update_icon->get_height()) / 2; - update_mode_rect.size = update_icon->get_size(); + Ref<Texture2D> down_icon = get_theme_icon(SNAME("select_arrow"), SNAME("Tree")); - if (!animation->track_is_compressed(track) && animation->track_get_type(track) == Animation::TYPE_VALUE) { - draw_texture(update_icon, update_mode_rect.position); - } - // Make it easier to click. - 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; - - 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(); - } + draw_line(Point2(ofs, 0), Point2(ofs, get_size().height), linecolor, Math::round(EDSCALE)); - ofs += down_icon->get_width(); - draw_line(Point2(ofs + hsep * 0.5, 0), Point2(ofs + hsep * 0.5, get_size().height), linecolor, Math::round(EDSCALE)); ofs += hsep; - } + { + // Callmode. - { - // Interp. + Animation::UpdateMode update_mode; - Animation::InterpolationType interp_mode = animation->track_get_interpolation_type(track); + if (animation->track_get_type(track) == Animation::TYPE_VALUE) { + update_mode = animation->value_track_get_update_mode(track); + } else { + update_mode = Animation::UPDATE_CONTINUOUS; + } - Ref<Texture2D> icon = interp_icon[interp_mode]; + Ref<Texture2D> update_icon = cont_icon[update_mode]; - interp_mode_rect.position.x = ofs; - interp_mode_rect.position.y = int(get_size().height - icon->get_height()) / 2; - interp_mode_rect.size = icon->get_size(); + update_mode_rect.position.x = ofs; + update_mode_rect.position.y = int(get_size().height - update_icon->get_height()) / 2; + update_mode_rect.size = update_icon->get_size(); - 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(icon, interp_mode_rect.position); - } - // Make it easier to click. - interp_mode_rect.position.y = 0; - interp_mode_rect.size.y = get_size().height; + if (!animation->track_is_compressed(track) && animation->track_get_type(track) == Animation::TYPE_VALUE) { + draw_texture(update_icon, update_mode_rect.position); + } + // Make it easier to click. + update_mode_rect.position.y = 0; + update_mode_rect.size.y = get_size().height; - ofs += icon->get_width() + hsep; - interp_mode_rect.size.x += hsep; + ofs += update_icon->get_width() + hsep / 2; + update_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)); - interp_mode_rect.size.x += down_icon->get_width(); - } else { - interp_mode_rect = Rect2(); + 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(); + } 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(); + + update_mode_rect = Rect2(); + } else { + update_mode_rect = Rect2(); + } + + ofs += down_icon->get_width(); + draw_line(Point2(ofs + hsep * 0.5, 0), Point2(ofs + hsep * 0.5, get_size().height), linecolor, Math::round(EDSCALE)); + ofs += hsep; } - ofs += down_icon->get_width(); - draw_line(Point2(ofs + hsep * 0.5, 0), Point2(ofs + hsep * 0.5, get_size().height), linecolor, Math::round(EDSCALE)); - ofs += hsep; - } + { + // Interp. - { - // Loop. + Animation::InterpolationType interp_mode = animation->track_get_interpolation_type(track); - bool loop_wrap = animation->track_get_interpolation_loop_wrap(track); + Ref<Texture2D> icon = interp_icon[interp_mode]; - Ref<Texture2D> icon = wrap_icon[loop_wrap ? 1 : 0]; + interp_mode_rect.position.x = ofs; + interp_mode_rect.position.y = int(get_size().height - icon->get_height()) / 2; + interp_mode_rect.size = icon->get_size(); - loop_wrap_rect.position.x = ofs; - loop_wrap_rect.position.y = int(get_size().height - icon->get_height()) / 2; - loop_wrap_rect.size = icon->get_size(); + 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(icon, interp_mode_rect.position); + } + // Make it easier to click. + interp_mode_rect.position.y = 0; + interp_mode_rect.size.y = get_size().height; + + 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(icon, loop_wrap_rect.position); + 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)); + interp_mode_rect.size.x += down_icon->get_width(); + } else { + interp_mode_rect = Rect2(); + } + + ofs += down_icon->get_width(); + draw_line(Point2(ofs + hsep * 0.5, 0), Point2(ofs + hsep * 0.5, get_size().height), linecolor, Math::round(EDSCALE)); + ofs += hsep; } - loop_wrap_rect.position.y = 0; - loop_wrap_rect.size.y = get_size().height; + { + // Loop. - ofs += icon->get_width() + hsep; - loop_wrap_rect.size.x += hsep; + bool loop_wrap = animation->track_get_interpolation_loop_wrap(track); - 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)); - loop_wrap_rect.size.x += down_icon->get_width(); - } else { - loop_wrap_rect = Rect2(); - } + Ref<Texture2D> icon = wrap_icon[loop_wrap ? 1 : 0]; - ofs += down_icon->get_width(); - draw_line(Point2(ofs + hsep * 0.5, 0), Point2(ofs + hsep * 0.5, get_size().height), linecolor, Math::round(EDSCALE)); - ofs += hsep; - } + loop_wrap_rect.position.x = ofs; + loop_wrap_rect.position.y = int(get_size().height - icon->get_height()) / 2; + loop_wrap_rect.size = icon->get_size(); - { - // Erase. + 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(icon, loop_wrap_rect.position); + } - Ref<Texture2D> icon = get_theme_icon(animation->track_is_compressed(track) ? SNAME("Lock") : SNAME("Remove"), SNAME("EditorIcons")); + loop_wrap_rect.position.y = 0; + loop_wrap_rect.size.y = get_size().height; - remove_rect.position.x = ofs + ((get_size().width - ofs) - icon->get_width()) / 2; - remove_rect.position.y = int(get_size().height - icon->get_height()) / 2; - remove_rect.size = icon->get_size(); + ofs += icon->get_width() + hsep / 2; + loop_wrap_rect.size.x += hsep / 2; - draw_texture(icon, remove_rect.position); - } - } + 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)); + loop_wrap_rect.size.x += down_icon->get_width(); + } else { + loop_wrap_rect = Rect2(); + } - if (in_group) { - draw_line(Vector2(timeline->get_name_limit(), get_size().height), get_size(), linecolor, Math::round(EDSCALE)); - } else { - draw_line(Vector2(0, get_size().height), get_size(), linecolor, Math::round(EDSCALE)); - } + ofs += down_icon->get_width(); + draw_line(Point2(ofs + hsep * 0.5, 0), Point2(ofs + hsep * 0.5, get_size().height), linecolor, Math::round(EDSCALE)); + ofs += hsep; + } + + { + // Erase. + + Ref<Texture2D> icon = get_theme_icon(animation->track_is_compressed(track) ? SNAME("Lock") : SNAME("Remove"), SNAME("EditorIcons")); - if (dropping_at != 0) { - Color drop_color = get_theme_color(SNAME("accent_color"), SNAME("Editor")); - if (dropping_at < 0) { - draw_line(Vector2(0, 0), Vector2(get_size().width, 0), drop_color, Math::round(EDSCALE)); + 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(); + + draw_texture(icon, remove_rect.position); + } + } + + if (in_group) { + draw_line(Vector2(timeline->get_name_limit(), get_size().height), get_size(), linecolor, Math::round(EDSCALE)); } else { - draw_line(Vector2(0, get_size().height), get_size(), drop_color, Math::round(EDSCALE)); + draw_line(Vector2(0, get_size().height), get_size(), linecolor, Math::round(EDSCALE)); } - } - } - if (p_what == NOTIFICATION_MOUSE_EXIT || p_what == NOTIFICATION_DRAG_END) { - cancel_drop(); + if (dropping_at != 0) { + Color drop_color = get_theme_color(SNAME("accent_color"), SNAME("Editor")); + if (dropping_at < 0) { + draw_line(Vector2(0, 0), Vector2(get_size().width, 0), drop_color, Math::round(EDSCALE)); + } else { + draw_line(Vector2(0, get_size().height), get_size(), drop_color, Math::round(EDSCALE)); + } + } + } break; + + case NOTIFICATION_MOUSE_EXIT: + case NOTIFICATION_DRAG_END: { + cancel_drop(); + } break; } } @@ -2421,22 +2425,10 @@ void AnimationTrackEdit::set_animation_and_track(const Ref<Animation> &p_animati track = p_track; update(); - Ref<Texture2D> type_icons[9] = { - get_theme_icon(SNAME("KeyValue"), SNAME("EditorIcons")), - get_theme_icon(SNAME("KeyXPosition"), SNAME("EditorIcons")), - get_theme_icon(SNAME("KeyXRotation"), SNAME("EditorIcons")), - get_theme_icon(SNAME("KeyXScale"), SNAME("EditorIcons")), - get_theme_icon(SNAME("KeyBlendShape"), SNAME("EditorIcons")), - get_theme_icon(SNAME("KeyCall"), SNAME("EditorIcons")), - get_theme_icon(SNAME("KeyBezier"), SNAME("EditorIcons")), - get_theme_icon(SNAME("KeyAudio"), SNAME("EditorIcons")), - get_theme_icon(SNAME("KeyAnimation"), SNAME("EditorIcons")) - }; - ERR_FAIL_INDEX(track, animation->get_track_count()); node_path = animation->track_get_path(p_track); - type_icon = type_icons[animation->track_get_type(track)]; + type_icon = _get_key_type_icon(); selected_icon = get_theme_icon(SNAME("KeySelected"), SNAME("EditorIcons")); } @@ -2537,6 +2529,21 @@ bool AnimationTrackEdit::_is_value_key_valid(const Variant &p_key_value, Variant return (!prop_exists || Variant::can_convert(p_key_value.get_type(), r_valid_type)); } +Ref<Texture2D> AnimationTrackEdit::_get_key_type_icon() const { + Ref<Texture2D> type_icons[9] = { + get_theme_icon(SNAME("KeyValue"), SNAME("EditorIcons")), + get_theme_icon(SNAME("KeyTrackPosition"), SNAME("EditorIcons")), + get_theme_icon(SNAME("KeyTrackRotation"), SNAME("EditorIcons")), + get_theme_icon(SNAME("KeyTrackScale"), SNAME("EditorIcons")), + get_theme_icon(SNAME("KeyTrackBlendShape"), SNAME("EditorIcons")), + get_theme_icon(SNAME("KeyCall"), SNAME("EditorIcons")), + get_theme_icon(SNAME("KeyBezier"), SNAME("EditorIcons")), + get_theme_icon(SNAME("KeyAudio"), SNAME("EditorIcons")), + get_theme_icon(SNAME("KeyAnimation"), SNAME("EditorIcons")) + }; + return type_icons[animation->track_get_type(track)]; +} + String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const { if (check_rect.has_point(p_pos)) { return TTR("Toggle this track on/off."); @@ -2743,7 +2750,7 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) { menu->add_icon_item(get_theme_icon(SNAME("TrackDiscrete"), SNAME("EditorIcons")), TTR("Discrete"), MENU_CALL_MODE_DISCRETE); menu->add_icon_item(get_theme_icon(SNAME("TrackTrigger"), SNAME("EditorIcons")), TTR("Trigger"), MENU_CALL_MODE_TRIGGER); menu->add_icon_item(get_theme_icon(SNAME("TrackCapture"), SNAME("EditorIcons")), TTR("Capture"), MENU_CALL_MODE_CAPTURE); - menu->set_as_minsize(); + menu->reset_size(); Vector2 popup_pos = get_screen_position() + update_mode_rect.position + Vector2(0, update_mode_rect.size.height); menu->set_position(popup_pos); @@ -2761,7 +2768,7 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) { menu->add_icon_item(get_theme_icon(SNAME("InterpRaw"), SNAME("EditorIcons")), TTR("Nearest"), MENU_INTERPOLATION_NEAREST); menu->add_icon_item(get_theme_icon(SNAME("InterpLinear"), SNAME("EditorIcons")), TTR("Linear"), MENU_INTERPOLATION_LINEAR); menu->add_icon_item(get_theme_icon(SNAME("InterpCubic"), SNAME("EditorIcons")), TTR("Cubic"), MENU_INTERPOLATION_CUBIC); - menu->set_as_minsize(); + menu->reset_size(); Vector2 popup_pos = get_screen_position() + interp_mode_rect.position + Vector2(0, interp_mode_rect.size.height); menu->set_position(popup_pos); @@ -2778,7 +2785,7 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) { menu->clear(); menu->add_icon_item(get_theme_icon(SNAME("InterpWrapClamp"), SNAME("EditorIcons")), TTR("Clamp Loop Interp"), MENU_LOOP_CLAMP); menu->add_icon_item(get_theme_icon(SNAME("InterpWrapLoop"), SNAME("EditorIcons")), TTR("Wrap Loop Interp"), MENU_LOOP_WRAP); - menu->set_as_minsize(); + menu->reset_size(); Vector2 popup_pos = get_screen_position() + loop_wrap_rect.position + Vector2(0, loop_wrap_rect.size.height); menu->set_position(popup_pos); @@ -2792,11 +2799,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. @@ -2885,7 +2887,7 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) { menu->add_separator(); menu->add_icon_item(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), TTR("Delete Key(s)"), MENU_KEY_DELETE); } - menu->set_as_minsize(); + menu->reset_size(); menu->set_position(get_screen_position() + get_local_mouse_position()); menu->popup(); @@ -3189,7 +3191,7 @@ AnimationTrackEdit *AnimationTrackEditPlugin::create_value_track_edit(Object *p_ }; Callable::CallError ce; - return Object::cast_to<AnimationTrackEdit>(get_script_instance()->call("create_value_track_edit", (const Variant **)&argptrs, 6, ce).operator Object *()); + return Object::cast_to<AnimationTrackEdit>(get_script_instance()->callp("create_value_track_edit", (const Variant **)&argptrs, 6, ce).operator Object *()); } return nullptr; } @@ -3211,40 +3213,42 @@ AnimationTrackEdit *AnimationTrackEditPlugin::create_animation_track_edit(Object /////////////////////////////////////// void AnimationTrackEditGroup::_notification(int p_what) { - if (p_what == NOTIFICATION_DRAW) { - Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label")); - int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label")); - int separation = get_theme_constant(SNAME("hseparation"), SNAME("ItemList")); - Color color = get_theme_color(SNAME("font_color"), SNAME("Label")); - - if (root && root->has_node(node)) { - Node *n = root->get_node(node); - if (n && EditorNode::get_singleton()->get_editor_selection()->is_selected(n)) { - color = get_theme_color(SNAME("accent_color"), SNAME("Editor")); + switch (p_what) { + case NOTIFICATION_DRAW: { + Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label")); + int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label")); + int separation = get_theme_constant(SNAME("hseparation"), SNAME("ItemList")); + Color color = get_theme_color(SNAME("font_color"), SNAME("Label")); + + if (root && root->has_node(node)) { + Node *n = root->get_node(node); + if (n && EditorNode::get_singleton()->get_editor_selection()->is_selected(n)) { + color = get_theme_color(SNAME("accent_color"), SNAME("Editor")); + } } - } - Color bgcol = get_theme_color(SNAME("dark_color_2"), SNAME("Editor")); - bgcol.a *= 0.6; - draw_rect(Rect2(Point2(), get_size()), bgcol); - Color linecolor = color; - linecolor.a = 0.2; + Color bgcol = get_theme_color(SNAME("dark_color_2"), SNAME("Editor")); + bgcol.a *= 0.6; + draw_rect(Rect2(Point2(), get_size()), bgcol); + Color linecolor = color; + linecolor.a = 0.2; - draw_line(Point2(), Point2(get_size().width, 0), linecolor, Math::round(EDSCALE)); - draw_line(Point2(timeline->get_name_limit(), 0), Point2(timeline->get_name_limit(), get_size().height), linecolor, Math::round(EDSCALE)); - draw_line(Point2(get_size().width - timeline->get_buttons_width(), 0), Point2(get_size().width - timeline->get_buttons_width(), get_size().height), linecolor, Math::round(EDSCALE)); + draw_line(Point2(), Point2(get_size().width, 0), linecolor, Math::round(EDSCALE)); + draw_line(Point2(timeline->get_name_limit(), 0), Point2(timeline->get_name_limit(), get_size().height), linecolor, Math::round(EDSCALE)); + draw_line(Point2(get_size().width - timeline->get_buttons_width(), 0), Point2(get_size().width - timeline->get_buttons_width(), get_size().height), linecolor, Math::round(EDSCALE)); - int ofs = 0; - draw_texture(icon, Point2(ofs, int(get_size().height - icon->get_height()) / 2)); - ofs += separation + icon->get_width(); - draw_string(font, Point2(ofs, int(get_size().height - font->get_height(font_size)) / 2 + font->get_ascent(font_size)), node_name, HORIZONTAL_ALIGNMENT_LEFT, timeline->get_name_limit() - ofs, font_size, color); + int ofs = 0; + draw_texture(icon, Point2(ofs, int(get_size().height - icon->get_height()) / 2)); + ofs += separation + icon->get_width(); + draw_string(font, Point2(ofs, int(get_size().height - font->get_height(font_size)) / 2 + font->get_ascent(font_size)), node_name, HORIZONTAL_ALIGNMENT_LEFT, timeline->get_name_limit() - ofs, font_size, color); - int px = (-timeline->get_value() + timeline->get_play_position()) * timeline->get_zoom_scale() + timeline->get_name_limit(); + int px = (-timeline->get_value() + timeline->get_play_position()) * timeline->get_zoom_scale() + timeline->get_name_limit(); - if (px >= timeline->get_name_limit() && px < (get_size().width - timeline->get_buttons_width())) { - Color accent = get_theme_color(SNAME("accent_color"), SNAME("Editor")); - draw_line(Point2(px, 0), Point2(px, get_size().height), accent, Math::round(2 * EDSCALE)); - } + if (px >= timeline->get_name_limit() && px < (get_size().width - timeline->get_buttons_width())) { + Color accent = get_theme_color(SNAME("accent_color"), SNAME("Editor")); + draw_line(Point2(px, 0), Point2(px, get_size().height), accent, Math::round(2 * EDSCALE)); + } + } break; } } @@ -3326,10 +3330,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 +3358,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); } } @@ -3574,7 +3590,7 @@ void AnimationTrackEditor::commit_insert_queue() { } } - if (bool(EDITOR_DEF("editors/animation/confirm_insert_track", true)) && num_tracks > 0) { + if (bool(EDITOR_GET("editors/animation/confirm_insert_track")) && num_tracks > 0) { // Potentially a new key, does not exist. if (num_tracks == 1) { // TRANSLATORS: %s will be replaced by a phrase describing the target of track. @@ -4167,13 +4183,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 +4417,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)); @@ -4509,26 +4526,33 @@ MenuButton *AnimationTrackEditor::get_edit_menu() { } void AnimationTrackEditor::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE || p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) { - 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"))); - } + switch (p_what) { + case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { + 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"))); + } break; - if (p_what == NOTIFICATION_THEME_CHANGED || p_what == NOTIFICATION_ENTER_TREE) { - zoom_icon->set_texture(get_theme_icon(SNAME("Zoom"), SNAME("EditorIcons"))); - snap->set_icon(get_theme_icon(SNAME("Snap"), SNAME("EditorIcons"))); - view_group->set_icon(get_theme_icon(view_group->is_pressed() ? "AnimationTrackList" : "AnimationTrackGroup", "EditorIcons")); - selected_filter->set_icon(get_theme_icon(SNAME("AnimationFilter"), SNAME("EditorIcons"))); - imported_anim_warning->set_icon(get_theme_icon(SNAME("NodeWarning"), SNAME("EditorIcons"))); - main_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("bg"), SNAME("Tree"))); - edit->get_popup()->set_item_icon(edit->get_popup()->get_item_index(EDIT_APPLY_RESET), get_theme_icon(SNAME("Reload"), SNAME("EditorIcons"))); - } + case NOTIFICATION_ENTER_TREE: { + 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"))); + [[fallthrough]]; + } + case NOTIFICATION_THEME_CHANGED: { + 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"))); + imported_anim_warning->set_icon(get_theme_icon(SNAME("NodeWarning"), SNAME("EditorIcons"))); + main_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("bg"), SNAME("Tree"))); + edit->get_popup()->set_item_icon(edit->get_popup()->get_item_index(EDIT_APPLY_RESET), get_theme_icon(SNAME("Reload"), SNAME("EditorIcons"))); + } break; - if (p_what == NOTIFICATION_READY) { - EditorNode::get_singleton()->get_editor_selection()->connect("selection_changed", callable_mp(this, &AnimationTrackEditor::_selection_changed)); - } + case NOTIFICATION_READY: { + EditorNode::get_singleton()->get_editor_selection()->connect("selection_changed", callable_mp(this, &AnimationTrackEditor::_selection_changed)); + } break; - if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { - update_keying(); + case NOTIFICATION_VISIBILITY_CHANGED: { + update_keying(); + } break; } } @@ -4630,6 +4654,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")) { @@ -5294,6 +5319,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) { @@ -5312,16 +5351,20 @@ void AnimationTrackEditor::_pan_callback(Vector2 p_scroll_vec) { } void AnimationTrackEditor::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) { - if (p_scroll_vec.y < 0) { - timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() * 1.05); + double new_zoom_value; + double current_zoom_value = timeline->get_zoom()->get_value(); + if (current_zoom_value <= 0.1) { + new_zoom_value = MAX(0.01, current_zoom_value - 0.01 * SIGN(p_scroll_vec.y)); } else { - timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() / 1.05); + new_zoom_value = p_scroll_vec.y > 0 ? MAX(0.01, current_zoom_value / 1.05) : current_zoom_value * 1.05; } + timeline->get_zoom()->set_value(new_zoom_value); } void AnimationTrackEditor::_cancel_bezier_edit() { bezier_edit->hide(); scroll->show(); + bezier_edit_icon->set_pressed(false); } void AnimationTrackEditor::_bezier_edit(int p_for_track) { @@ -5493,8 +5536,8 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) { String text; Ref<Texture2D> icon = get_theme_icon(SNAME("Node"), SNAME("EditorIcons")); if (node) { - if (has_theme_icon(node->get_class(), "EditorIcons")) { - icon = get_theme_icon(node->get_class(), "EditorIcons"); + if (has_theme_icon(node->get_class(), SNAME("EditorIcons"))) { + icon = get_theme_icon(node->get_class(), SNAME("EditorIcons")); } text = node->get_name(); @@ -5513,31 +5556,35 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) { } } + String track_type; switch (animation->track_get_type(i)) { case Animation::TYPE_POSITION_3D: - text += " (Position)"; + track_type = TTR("Position"); break; case Animation::TYPE_ROTATION_3D: - text += " (Rotation)"; + track_type = TTR("Rotation"); break; case Animation::TYPE_SCALE_3D: - text += " (Scale)"; + track_type = TTR("Scale"); break; case Animation::TYPE_BLEND_SHAPE: - text += " (BlendShape)"; + track_type = TTR("BlendShape"); break; case Animation::TYPE_METHOD: - text += " (Methods)"; + track_type = TTR("Methods"); break; case Animation::TYPE_BEZIER: - text += " (Bezier)"; + track_type = TTR("Bezier"); break; case Animation::TYPE_AUDIO: - text += " (Audio)"; + track_type = TTR("Audio"); break; default: { }; } + if (!track_type.is_empty()) { + text += vformat(" (%s)", track_type); + } TreeItem *it = track_copy_select->create_item(troot); it->set_editable(0, true); @@ -5907,7 +5954,8 @@ 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() ? "AnimationTrackList" : "AnimationTrackGroup", "EditorIcons")); + 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 +6201,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. |