1use crate::animations::core::Animatable;
4use crate::prelude::AnimationConfig;
5
6use std::sync::Arc;
7use std::sync::Mutex;
8use std::sync::atomic::{AtomicU8, Ordering};
9
10#[derive(Clone)]
11pub struct AnimationStep<T: Animatable> {
12 pub target: T,
13 pub config: Arc<AnimationConfig>,
14 pub predicted_next: Option<T>,
15}
16
17pub struct AnimationSequence<T: Animatable> {
20 steps: Arc<[AnimationStep<T>]>,
22 current_step: AtomicU8,
24 #[allow(clippy::type_complexity)]
26 on_complete: Arc<Mutex<Option<Box<dyn FnOnce() + Send>>>>,
27}
28
29impl<T: Animatable> AnimationSequence<T> {
30 pub fn new() -> Self {
32 Self {
33 steps: Arc::new([]),
34 current_step: AtomicU8::new(0),
35 on_complete: Arc::new(Mutex::new(None)),
36 }
37 }
38
39 pub fn with_capacity(_capacity: u8) -> Self {
42 Self::new()
43 }
44
45 pub fn from_steps(steps: Vec<AnimationStep<T>>) -> Self {
47 Self {
48 steps: steps.into(),
49 current_step: AtomicU8::new(0),
50 on_complete: Arc::new(Mutex::new(None)),
51 }
52 }
53
54 pub fn with_on_complete<F>(steps: Vec<AnimationStep<T>>, on_complete: F) -> Self
56 where
57 F: FnOnce() + Send + 'static,
58 {
59 Self {
60 steps: steps.into(),
61 current_step: AtomicU8::new(0),
62 on_complete: Arc::new(Mutex::new(Some(Box::new(on_complete)))),
63 }
64 }
65
66 pub fn reserve(&mut self, _additional: u8) {
68 }
70
71 pub fn then(self, target: T, config: AnimationConfig) -> Self {
74 let predicted_next = if self.steps.is_empty() {
75 None
76 } else {
77 self.steps
78 .last()
79 .map(|last_step| last_step.target.interpolate(&target, 0.5))
80 };
81
82 let new_step = AnimationStep {
83 target,
84 config: Arc::new(config),
85 predicted_next,
86 };
87
88 let mut new_steps: Vec<AnimationStep<T>> = self.steps.iter().cloned().collect();
90 new_steps.push(new_step);
91
92 Self {
93 steps: new_steps.into(),
94 current_step: AtomicU8::new(self.current_step.load(Ordering::Relaxed)),
95 on_complete: self.on_complete,
96 }
97 }
98
99 pub fn on_complete<F: FnOnce() + Send + 'static>(self, f: F) -> Self {
101 if let Ok(mut guard) = self.on_complete.lock() {
102 *guard = Some(Box::new(f));
103 }
104 self
105 }
106
107 pub fn advance_step(&self) -> bool {
110 let current = self.current_step.load(Ordering::Relaxed);
111 let total_steps = self.steps.len() as u8;
112
113 if current < total_steps.saturating_sub(1) {
114 self.current_step.store(current + 1, Ordering::Relaxed);
115 true
116 } else {
117 false
118 }
119 }
120
121 pub fn current_step_index(&self) -> u8 {
123 self.current_step.load(Ordering::Relaxed)
124 }
125
126 pub fn current_step(&self) -> u8 {
128 self.current_step_index()
129 }
130
131 pub fn current_config(&self) -> Option<&AnimationConfig> {
133 let current = self.current_step.load(Ordering::Relaxed) as usize;
134 self.steps.get(current).map(|step| step.config.as_ref())
135 }
136
137 pub fn current_target(&self) -> Option<T> {
139 let current = self.current_step.load(Ordering::Relaxed) as usize;
140 self.steps.get(current).map(|step| step.target)
141 }
142
143 pub fn current_step_data(&self) -> Option<&AnimationStep<T>> {
145 let current = self.current_step.load(Ordering::Relaxed) as usize;
146 self.steps.get(current)
147 }
148
149 pub fn steps(&self) -> &[AnimationStep<T>] {
151 &self.steps
152 }
153
154 pub fn is_complete(&self) -> bool {
156 let current = self.current_step.load(Ordering::Relaxed);
157 let total_steps = self.steps.len() as u8;
158 current >= total_steps.saturating_sub(1)
159 }
160
161 pub fn total_steps(&self) -> usize {
163 self.steps.len()
164 }
165
166 pub fn reset(&self) {
168 self.current_step.store(0, Ordering::Relaxed);
169 }
170
171 pub fn execute_completion(&self) {
174 if let Ok(mut guard) = self.on_complete.lock() {
175 if let Some(callback) = guard.take() {
176 callback();
177 }
178 }
179 }
180}
181
182impl<T: Animatable> Clone for AnimationSequence<T> {
183 fn clone(&self) -> Self {
184 Self {
185 steps: self.steps.clone(), current_step: AtomicU8::new(self.current_step.load(Ordering::Relaxed)),
187 on_complete: Arc::new(Mutex::new(None)), }
189 }
190}
191
192impl<T: Animatable> Default for AnimationSequence<T> {
193 fn default() -> Self {
194 Self::new()
195 }
196}
197
198#[cfg(test)]
199mod tests {
200 #![allow(clippy::unwrap_used)]
201 use super::*;
202 use crate::animations::core::AnimationMode;
203 use crate::animations::spring::Spring;
204 use std::sync::{Arc, Mutex};
205
206 #[test]
207 fn test_animation_sequence_basic() {
208 let steps = vec![
209 AnimationStep {
210 target: 10.0f32,
211 config: Arc::new(AnimationConfig::new(AnimationMode::Spring(
212 Spring::default(),
213 ))),
214 predicted_next: None,
215 },
216 AnimationStep {
217 target: 20.0f32,
218 config: Arc::new(AnimationConfig::new(AnimationMode::Spring(
219 Spring::default(),
220 ))),
221 predicted_next: None,
222 },
223 AnimationStep {
224 target: 30.0f32,
225 config: Arc::new(AnimationConfig::new(AnimationMode::Spring(
226 Spring::default(),
227 ))),
228 predicted_next: None,
229 },
230 ];
231
232 let sequence = AnimationSequence::from_steps(steps);
233
234 assert_eq!(sequence.current_step_index(), 0);
236 assert_eq!(sequence.current_target().unwrap(), 10.0f32);
237 assert!(!sequence.is_complete());
238 assert_eq!(sequence.total_steps(), 3);
239
240 assert!(sequence.advance_step());
242 assert_eq!(sequence.current_step_index(), 1);
243 assert_eq!(sequence.current_target().unwrap(), 20.0f32);
244 assert!(!sequence.is_complete());
245
246 assert!(sequence.advance_step());
247 assert_eq!(sequence.current_step_index(), 2);
248 assert_eq!(sequence.current_target().unwrap(), 30.0f32);
249 assert!(sequence.is_complete());
250
251 assert!(!sequence.advance_step());
253 assert_eq!(sequence.current_step_index(), 2);
254
255 sequence.reset();
257 assert_eq!(sequence.current_step_index(), 0);
258 assert!(!sequence.is_complete());
259 }
260
261 #[test]
262 fn test_animation_sequence_builder_pattern() {
263 let sequence = AnimationSequence::new()
264 .then(
265 10.0f32,
266 AnimationConfig::new(AnimationMode::Spring(Spring::default())),
267 )
268 .then(
269 20.0f32,
270 AnimationConfig::new(AnimationMode::Spring(Spring::default())),
271 )
272 .then(
273 30.0f32,
274 AnimationConfig::new(AnimationMode::Spring(Spring::default())),
275 );
276
277 assert_eq!(sequence.total_steps(), 3);
278 assert_eq!(sequence.current_target().unwrap(), 10.0f32);
279 assert!(!sequence.is_complete());
280
281 assert!(sequence.advance_step());
282 assert_eq!(sequence.current_target().unwrap(), 20.0f32);
283
284 assert!(sequence.advance_step());
285 assert_eq!(sequence.current_target().unwrap(), 30.0f32);
286 assert!(sequence.is_complete());
287 }
288
289 #[test]
290 fn test_animation_sequence_with_callback() {
291 let callback_executed = Arc::new(Mutex::new(false));
292 let callback_executed_clone = callback_executed.clone();
293
294 let steps = vec![AnimationStep {
295 target: 10.0f32,
296 config: Arc::new(AnimationConfig::new(AnimationMode::Spring(
297 Spring::default(),
298 ))),
299 predicted_next: None,
300 }];
301
302 let sequence = AnimationSequence::with_on_complete(steps, move || {
303 *callback_executed_clone.lock().unwrap() = true;
304 });
305
306 sequence.execute_completion();
308
309 assert!(*callback_executed.lock().unwrap());
310 }
311
312 #[test]
313 fn test_animation_sequence_callback_with_shared_references() {
314 let callback_executed = Arc::new(Mutex::new(false));
315 let callback_executed_clone = callback_executed.clone();
316
317 let steps = vec![AnimationStep {
318 target: 10.0f32,
319 config: Arc::new(AnimationConfig::new(AnimationMode::Spring(
320 Spring::default(),
321 ))),
322 predicted_next: None,
323 }];
324
325 let sequence = AnimationSequence::with_on_complete(steps, move || {
326 *callback_executed_clone.lock().unwrap() = true;
327 });
328
329 let sequence_arc1 = Arc::new(sequence);
331 let sequence_arc2 = sequence_arc1.clone();
332 let sequence_arc3 = sequence_arc1.clone();
333
334 assert!(Arc::try_unwrap(sequence_arc1.clone()).is_err());
336
337 sequence_arc1.execute_completion();
340
341 assert!(*callback_executed.lock().unwrap());
343
344 assert_eq!(sequence_arc2.current_step_index(), 0);
346 assert_eq!(sequence_arc3.current_step_index(), 0);
347 }
348
349 #[test]
350 fn test_animation_sequence_clone() {
351 let steps = vec![AnimationStep {
352 target: 10.0f32,
353 config: Arc::new(AnimationConfig::new(AnimationMode::Spring(
354 Spring::default(),
355 ))),
356 predicted_next: None,
357 }];
358
359 let sequence1 = AnimationSequence::from_steps(steps);
360 sequence1.advance_step(); let sequence2 = sequence1.clone();
363
364 assert_eq!(
366 sequence1.current_step_index(),
367 sequence2.current_step_index()
368 );
369 assert_eq!(sequence1.total_steps(), sequence2.total_steps());
370 assert_eq!(sequence1.current_target(), sequence2.current_target());
371 }
372
373 #[test]
374 fn test_animation_sequence_backward_compatibility() {
375 let sequence = AnimationSequence::new();
377 let sequence = sequence.then(
378 10.0f32,
379 AnimationConfig::new(AnimationMode::Spring(Spring::default())),
380 );
381 let sequence = sequence.then(
382 20.0f32,
383 AnimationConfig::new(AnimationMode::Spring(Spring::default())),
384 );
385
386 assert_eq!(sequence.current_step(), 0);
388 assert_eq!(sequence.steps().len(), 2);
389
390 let _sequence_with_capacity = AnimationSequence::<f32>::with_capacity(10);
392
393 let mut sequence_mut = sequence.clone();
395 sequence_mut.reserve(5);
396 }
397}