Route animation timeline scrubbing through app core

This commit is contained in:
2026-06-05 00:28:06 +02:00
parent 59210c28ea
commit a9e12f2219
8 changed files with 194 additions and 5 deletions

View File

@@ -3,6 +3,7 @@
#include "foundation/result.h"
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <limits>
@@ -70,6 +71,15 @@ struct DocumentAnimationOnionFrameRange {
int last_frame = 0;
};
inline constexpr float document_animation_timeline_frame_width = 35.0F;
struct DocumentAnimationTimelineScrubPlan {
int total_duration = 1;
float cursor_x = 0.0F;
float frame_width = document_animation_timeline_frame_width;
int target_frame = 0;
};
class DocumentAnimationServices {
public:
virtual ~DocumentAnimationServices() = default;
@@ -162,6 +172,37 @@ public:
});
}
[[nodiscard]] inline pp::foundation::Result<DocumentAnimationTimelineScrubPlan> plan_animation_timeline_scrub(
int total_duration,
float cursor_x,
float frame_width = document_animation_timeline_frame_width)
{
if (total_duration <= 0) {
return pp::foundation::Result<DocumentAnimationTimelineScrubPlan>::failure(
pp::foundation::Status::invalid_argument("animation timeline duration must be greater than zero"));
}
if (!std::isfinite(cursor_x)) {
return pp::foundation::Result<DocumentAnimationTimelineScrubPlan>::failure(
pp::foundation::Status::invalid_argument("animation timeline cursor position must be finite"));
}
if (!std::isfinite(frame_width) || frame_width <= 0.0F) {
return pp::foundation::Result<DocumentAnimationTimelineScrubPlan>::failure(
pp::foundation::Status::invalid_argument("animation timeline frame width must be positive and finite"));
}
const auto raw_frame = static_cast<std::int64_t>(std::floor(cursor_x / frame_width));
const auto target_frame = std::clamp<std::int64_t>(raw_frame, 0, total_duration - 1);
return pp::foundation::Result<DocumentAnimationTimelineScrubPlan>::success(
DocumentAnimationTimelineScrubPlan {
.total_duration = total_duration,
.cursor_x = cursor_x,
.frame_width = frame_width,
.target_frame = static_cast<int>(target_frame),
});
}
[[nodiscard]] inline float animation_onion_frame_alpha(
const DocumentAnimationOnionFrameRange& range,
int frame) noexcept

View File

@@ -347,10 +347,13 @@ kEventResult NodeAnimationTimeline::handle_event(Event* e)
parent::handle_event(e);
static int signaled_frame = -1;
auto me = static_cast<MouseEvent*>(e);
auto ge = static_cast<GestureEvent*>(e);
auto update = [&](){
auto loc = me->m_pos - m_pos;
m_frame = glm::clamp((int)glm::floor(loc.x / 35.f), 0, Canvas::I->anim_duration() - 1);
const int total_duration = Canvas::I ? Canvas::I->anim_duration() : 0;
const auto scrub = pp::app::plan_animation_timeline_scrub(total_duration, loc.x);
if (!scrub)
return;
m_frame = scrub.value().target_frame;
if (on_frame_changed && signaled_frame != m_frame)
on_frame_changed(this, m_frame);
signaled_frame = m_frame;