use super::animation::{AnimationConfig, AnimationDriver, AnimationId};
use crate::compat::{String, Vec};
pub struct AnimationGroup {
name: String,
parallel: Vec<AnimationId>,
sequential: Vec<AnimationConfig>,
current_seq_index: usize,
current_seq_id: Option<AnimationId>,
}
impl AnimationGroup {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
parallel: Vec::new(),
sequential: Vec::new(),
current_seq_index: 0,
current_seq_id: None,
}
}
pub fn add_parallel(&mut self, id: AnimationId) {
self.parallel.push(id);
}
pub fn add_sequential(&mut self, config: AnimationConfig) {
self.sequential.push(config);
}
pub fn name(&self) -> &str {
&self.name
}
pub fn is_completed(&self, driver: &AnimationDriver) -> bool {
let parallel_done = self.parallel.iter().all(|id| driver.is_finished(*id));
parallel_done && self.current_seq_index >= self.sequential.len()
}
pub fn len_parallel(&self) -> usize {
self.parallel.len()
}
pub fn len_sequential(&self) -> usize {
self.sequential.len()
}
pub fn current_seq_index(&self) -> usize {
self.current_seq_index
}
pub fn reset(&mut self) {
self.current_seq_index = 0;
self.current_seq_id = None;
}
pub fn advance(&mut self, driver: &mut AnimationDriver) {
if self.current_seq_index >= self.sequential.len() {
return;
}
let parallel_done = self.parallel.iter().all(|id| driver.is_finished(*id));
if !parallel_done {
return;
}
if self.current_seq_id.is_none() {
let config = self.sequential[self.current_seq_index].clone();
let id = driver.add(config, |_| {});
self.current_seq_id = Some(id);
return;
}
let done = self.current_seq_id.map(|id| driver.is_finished(id)).unwrap_or(true);
if done {
self.current_seq_id = None;
self.current_seq_index += 1;
if self.current_seq_index < self.sequential.len() {
let config = self.sequential[self.current_seq_index].clone();
let id = driver.add(config, |_| {});
self.current_seq_id = Some(id);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::style::animation::AnimationDirection;
use core::time::Duration;
#[test]
fn a_reverse_parallel_child_does_not_complete_the_group() {
let mut driver = AnimationDriver::new();
let mut group = AnimationGroup::new("reverse");
let id = driver.add(
AnimationConfig::new(Duration::from_millis(100))
.with_direction(AnimationDirection::Reverse),
|_| {},
);
group.add_parallel(id);
assert!(!group.is_completed(&driver), "a reverse child starts at 1.0 but has not finished");
driver.advance_by(Duration::from_millis(10_000));
assert!(group.is_completed(&driver), "once the child finishes the group completes");
}
#[test]
fn the_group_waits_for_a_multi_iteration_sequential_child() {
let mut driver = AnimationDriver::new();
let mut group = AnimationGroup::new("multi");
group.add_sequential(AnimationConfig::new(Duration::from_millis(100)).with_iterations(3));
group.add_sequential(AnimationConfig::new(Duration::from_millis(100)));
group.advance(&mut driver);
assert_eq!(group.current_seq_index(), 0);
driver.advance_by(Duration::from_millis(150));
group.advance(&mut driver);
assert_eq!(
group.current_seq_index(),
0,
"a 3-iteration child at 1.5 iterations must not advance the sequence"
);
driver.advance_by(Duration::from_millis(10_000));
group.advance(&mut driver);
assert_eq!(group.current_seq_index(), 1, "the finished child advances the sequence");
}
}