#[cfg(not(feature = "std"))]
use alloc::{boxed::Box, vec::Vec};
use crate::clock::Clock;
use crate::driver::AnimationId;
use crate::traits::Update;
#[derive(Debug, Clone)]
pub struct ScrollClock {
start: f32,
end: f32,
position: f32,
pending_delta: f32,
}
impl ScrollClock {
pub fn new(start: f32, end: f32) -> Self {
Self {
start,
end,
position: start,
pending_delta: 0.0,
}
}
pub fn set_position(&mut self, position: f32) {
let range = self.end - self.start;
if range.abs() < 1e-10 {
return;
}
let old_progress = (self.position - self.start) / range;
let new_progress = (position - self.start) / range;
self.pending_delta += new_progress - old_progress;
self.position = position;
}
pub fn position(&self) -> f32 {
self.position
}
pub fn progress(&self) -> f32 {
let range = self.end - self.start;
if range.abs() < 1e-10 {
return 0.0;
}
((self.position - self.start) / range).clamp(0.0, 1.0)
}
pub fn start(&self) -> f32 {
self.start
}
pub fn end(&self) -> f32 {
self.end
}
pub fn set_range(&mut self, start: f32, end: f32) {
self.start = start;
self.end = end;
}
}
impl Clock for ScrollClock {
fn delta(&mut self) -> f32 {
let d = self.pending_delta;
self.pending_delta = 0.0;
d
}
}
pub struct ScrollDriver {
clock: ScrollClock,
animations: Vec<(AnimationId, Box<dyn Update>)>,
next_id: u64,
}
impl ScrollDriver {
pub fn new(start: f32, end: f32) -> Self {
Self {
clock: ScrollClock::new(start, end),
animations: Vec::new(),
next_id: 0,
}
}
pub fn add<A: Update + 'static>(&mut self, animation: A) -> AnimationId {
let id = AnimationId::new(self.next_id);
self.next_id += 1;
self.animations.push((id, Box::new(animation)));
id
}
pub fn set_position(&mut self, position: f32) {
self.clock.set_position(position);
let dt = self.clock.delta();
if dt.abs() > 1e-10 {
self.animations.retain_mut(|(_, anim)| anim.update(dt));
}
}
pub fn progress(&self) -> f32 {
self.clock.progress()
}
pub fn position(&self) -> f32 {
self.clock.position()
}
pub fn cancel(&mut self, id: AnimationId) {
self.animations.retain(|(aid, _)| *aid != id);
}
pub fn cancel_all(&mut self) {
self.animations.clear();
}
pub fn active_count(&self) -> usize {
self.animations.len()
}
pub fn clock(&self) -> &ScrollClock {
&self.clock
}
pub fn clock_mut(&mut self) -> &mut ScrollClock {
&mut self.clock
}
}
impl core::fmt::Debug for ScrollDriver {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ScrollDriver")
.field("clock", &self.clock)
.field("active_count", &self.animations.len())
.field("next_id", &self.next_id)
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tween::Tween;
#[test]
fn scroll_clock_basic_delta() {
let mut clock = ScrollClock::new(0.0, 1000.0);
clock.set_position(500.0);
let dt = clock.delta();
assert!((dt - 0.5).abs() < 1e-6, "Expected 0.5, got {dt}");
}
#[test]
fn scroll_clock_accumulates() {
let mut clock = ScrollClock::new(0.0, 100.0);
clock.set_position(25.0);
clock.set_position(75.0);
let dt = clock.delta();
assert!((dt - 0.75).abs() < 1e-6, "Expected 0.75, got {dt}");
}
#[test]
fn scroll_clock_resets_after_delta() {
let mut clock = ScrollClock::new(0.0, 100.0);
clock.set_position(50.0);
let _ = clock.delta();
let dt = clock.delta();
assert!((dt - 0.0).abs() < 1e-6);
}
#[test]
fn scroll_clock_backward_produces_negative_delta() {
let mut clock = ScrollClock::new(0.0, 100.0);
clock.set_position(80.0);
let _ = clock.delta(); clock.set_position(30.0);
let dt = clock.delta();
assert!((dt - (-0.5)).abs() < 1e-6, "Expected -0.5, got {dt}");
}
#[test]
fn scroll_clock_progress() {
let mut clock = ScrollClock::new(0.0, 200.0);
clock.set_position(100.0);
assert!((clock.progress() - 0.5).abs() < 1e-6);
}
#[test]
fn scroll_clock_zero_range_is_safe() {
let mut clock = ScrollClock::new(100.0, 100.0);
clock.set_position(100.0);
let dt = clock.delta();
assert!((dt - 0.0).abs() < 1e-6);
}
#[test]
fn scroll_driver_ticks_animations() {
let mut driver = ScrollDriver::new(0.0, 1.0);
driver.add(Tween::new(0.0_f32, 100.0).duration(1.0).build());
assert_eq!(driver.active_count(), 1);
driver.set_position(1.0);
assert_eq!(driver.active_count(), 0);
}
#[test]
fn scroll_driver_partial_scroll() {
let mut driver = ScrollDriver::new(0.0, 100.0);
driver.add(Tween::new(0.0_f32, 100.0).duration(1.0).build());
driver.set_position(50.0); assert_eq!(driver.active_count(), 1); }
#[test]
fn scroll_driver_cancel() {
let mut driver = ScrollDriver::new(0.0, 100.0);
let id = driver.add(Tween::new(0.0_f32, 100.0).duration(1.0).build());
assert_eq!(driver.active_count(), 1);
driver.cancel(id);
assert_eq!(driver.active_count(), 0);
}
}