use crate::prelude::*;
use instant::Instant;
use savory::prelude::*;
use savory_style::{self as style, prelude::*};
use std::{any::Any, ops::Deref};
#[derive(Rich, Element)]
#[element(config_bound = "S: 'static")]
pub struct Animator<S> {
#[element(config(required))]
init_state: Box<S>,
last_instant: Instant,
#[element(config(default = "vec![]", no_pub))]
timeline: Vec<(u32, Box<S>)>,
#[element(config(default = "1000u32", no_pub))]
replay_delay: u32,
playing: Playing<S>,
}
pub enum Playing<S> {
Off,
On {
cursor: Cursor,
queued: Vec<(u32, Box<S>)>,
replay: bool,
},
}
pub enum Cursor {
AtInit,
At(usize),
}
pub enum Msg {
Update(Box<dyn Any>),
Replay,
ReplayLoop,
ReplayDelay(u32),
Stop,
ResetTimeline,
Playing(Cursor),
}
impl<S: 'static> Element for Animator<S> {
type Message = Msg;
type Config = Config<S>;
fn init(config: Self::Config, _: &mut impl Orders<Msg>) -> Self {
Self {
init_state: config.init_state,
last_instant: Instant::now(),
timeline: config.timeline,
replay_delay: 1000,
playing: Playing::Off,
}
}
fn update(&mut self, msg: Msg, orders: &mut impl Orders<Msg>) {
match msg {
Msg::Update(val) => {
if let Ok(val) = val.downcast::<S>() {
let now = Instant::now();
let duration = now.duration_since(self.last_instant);
self.last_instant = now;
let ms = duration.as_millis() as u32;
match self.playing {
Playing::Off => self.timeline.push((ms, val)),
Playing::On { ref mut queued, .. } => queued.push((ms, val)),
}
}
}
Msg::Playing(cursor) => self.playing(cursor, orders),
Msg::Replay => self.replay(false, orders),
Msg::ReplayDelay(ms) => self.replay_delay = ms,
Msg::ReplayLoop => self.replay(true, orders),
Msg::Stop => self.stop(),
Msg::ResetTimeline => self.reset_timeline(),
}
}
}
impl<S> Animator<S> {
pub fn animate<PMsg>(
&self,
node: Node<PMsg>,
styler: impl FnOnce(&S, style::Style) -> style::Style,
) -> Node<PMsg> {
let style = styler(self.current(), style::Style::default());
node.style(style)
}
pub fn current(&self) -> &S {
let last = self.timeline.last().map(|(_, s)| s);
match self.playing {
Playing::Off => last.unwrap_or(&self.init_state),
Playing::On { ref cursor, .. } => match cursor {
Cursor::AtInit => &self.init_state,
Cursor::At(index) => self
.timeline
.get(*index)
.map(|(_, s)| s)
.unwrap_or_else(|| last.unwrap_or(&self.init_state)),
},
}
}
pub fn previous(&self) -> Option<&S> {
match self.playing {
Playing::Off => match self.timeline.len() {
0 => None,
1 => Some(&self.init_state),
len => self.timeline.get(len - 1).map(|(_, s)| s.deref()),
},
Playing::On { ref cursor, .. } => match cursor {
Cursor::AtInit | Cursor::At(0) => None,
Cursor::At(index) => match index {
0 => Some(&self.init_state),
index => self.timeline.get(*index).map(|(_, s)| s.deref()),
},
},
}
}
fn replay(&mut self, replay: bool, orders: &mut impl Orders<Msg>) {
if let Playing::Off = self.playing {
if self.timeline.is_empty() {
return;
}
orders.send(Msg::Playing(Cursor::AtInit));
self.playing = Playing::On {
queued: vec![],
replay,
cursor: Cursor::AtInit,
};
}
}
fn stop(&mut self) {
if let Playing::On { ref mut queued, .. } = self.playing {
self.timeline.extend(queued.drain(..));
self.playing = Playing::Off;
}
}
fn playing(&mut self, val: Cursor, orders: &mut impl Orders<Msg>) {
if let Playing::On {
ref mut cursor,
replay,
..
} = self.playing
{
*cursor = match val {
Cursor::AtInit => {
if let Some((ms, _)) = self.timeline.first() {
orders.send_after(*ms, || Msg::Playing(Cursor::At(0)));
val
} else {
self.stop();
return;
}
}
Cursor::At(index) => {
if let Some((ms, _)) = self.timeline.get(index + 1) {
orders.send_after(*ms, move || Msg::Playing(Cursor::At(index + 1)));
val
} else {
if replay {
orders.send_after(self.replay_delay, move || {
Msg::Playing(Cursor::AtInit)
});
val
} else {
self.stop();
return;
}
}
}
};
}
}
fn reset_timeline(&mut self) {
self.last_instant = Instant::now();
self.stop();
self.timeline = vec![];
}
}
impl<S> Config<S> {
pub fn timeline(mut self, states: impl IntoIterator<Item = (u32, S)>) -> Self {
self.timeline = states
.into_iter()
.map(|(ms, s)| (ms, Box::new(s)))
.collect();
self
}
pub fn replay_delay(mut self, ms: u32) -> Self {
self.replay_delay = ms;
self
}
}
impl Msg {
pub fn update<S: 'static>(state: S) -> Msg {
Msg::Update(Box::new(state))
}
pub fn play() -> Msg {
Msg::Replay
}
pub fn replay_loop() -> Msg {
Msg::ReplayLoop
}
pub fn stop() -> Msg {
Msg::Stop
}
pub fn rest_timeline() -> Msg {
Msg::ResetTimeline
}
pub fn playing(cursor: Cursor) -> Msg {
Msg::Playing(cursor)
}
}