Skip to main content

argui_animation/
scheduler.rs

1use crate::{Duration, Time};
2
3/// Stable identity for one scheduled animation.
4#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
5pub struct AnimationId(u64);
6
7/// Timing shared by every animation sampled during one presentation frame.
8#[derive(Clone, Copy, Debug, Eq, PartialEq)]
9pub struct Frame {
10    pub now: Time,
11    pub elapsed: Duration,
12}
13
14/// Compact storage containing active animations only.
15#[derive(Debug, Default)]
16pub struct Scheduler {
17    active: Vec<AnimationId>,
18    next_id: u64,
19    last_frame: Option<Time>,
20}
21
22impl Scheduler {
23    #[must_use]
24    pub fn start(&mut self) -> AnimationId {
25        let id = AnimationId(self.next_id);
26        self.next_id = self.next_id.wrapping_add(1);
27        self.active.push(id);
28        id
29    }
30
31    pub fn stop(&mut self, id: AnimationId) -> bool {
32        let Some(index) = self.active.iter().position(|active| *active == id) else {
33            return false;
34        };
35        self.active.swap_remove(index);
36        if self.active.is_empty() {
37            self.last_frame = None;
38        }
39        true
40    }
41
42    #[must_use]
43    pub fn contains(&self, id: AnimationId) -> bool {
44        self.active.contains(&id)
45    }
46
47    #[must_use]
48    pub fn active(&self) -> &[AnimationId] {
49        &self.active
50    }
51
52    #[must_use]
53    pub fn needs_frame(&self) -> bool {
54        !self.active.is_empty()
55    }
56
57    pub fn frame(&mut self, now: Time) -> Option<Frame> {
58        self.needs_frame().then(|| {
59            let elapsed = self
60                .last_frame
61                .map_or(Duration::ZERO, |last| now.duration_since(last));
62            self.last_frame = Some(now);
63            Frame { now, elapsed }
64        })
65    }
66}