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