use crate::{
ActiveRouteScreenTransition, ActiveRouteTransition, AnimationHandle, AnimationRuntime,
AnimationTargetId, RouteScreenTargets, RouteScreenTransitionRegistration, RouteTransition,
RouteTransitionRegistration, RouteTransitionSet, StateAnimator,
route::transition::RouteScreenTransition, runtime::AnimationClock,
};
#[derive(Debug, Clone, PartialEq)]
pub struct RouteAnimator<R>
where
R: Copy + Eq,
{
inner: StateAnimator<R>,
active_screen: Option<ActiveRouteScreenTransition<R>>,
}
impl<R> RouteAnimator<R>
where
R: Copy + Eq,
{
#[must_use]
pub const fn new(target: AnimationTargetId, initial: R) -> Self {
Self {
inner: StateAnimator::new(target, initial),
active_screen: None,
}
}
#[must_use]
pub const fn from_state_animator(inner: StateAnimator<R>) -> Self {
Self {
inner,
active_screen: None,
}
}
#[must_use]
pub const fn as_state_animator(&self) -> &StateAnimator<R> {
&self.inner
}
#[must_use]
pub fn into_state_animator(self) -> StateAnimator<R> {
self.inner
}
#[must_use]
pub fn target(&self) -> AnimationTargetId {
self.inner.target()
}
#[must_use]
pub fn current(&self) -> R {
self.inner.current()
}
#[must_use]
pub fn active_handle(&self) -> Option<AnimationHandle> {
self.inner.active_handle()
}
#[must_use]
pub fn is_active<C: AnimationClock>(&self, runtime: &AnimationRuntime<C>) -> bool {
self.inner.is_active(runtime)
}
#[must_use]
pub fn active_transition(&self) -> Option<&ActiveRouteTransition<R>> {
self.inner.active_transition()
}
#[must_use]
pub const fn active_screen_transition(&self) -> Option<&ActiveRouteScreenTransition<R>> {
self.active_screen.as_ref()
}
pub fn handle_completion<C: AnimationClock>(&mut self, runtime: &AnimationRuntime<C>) -> bool {
let route_changed = self.inner.handle_completion(runtime);
let screen_changed = self.invalidate_screen_if_stale(runtime);
route_changed || screen_changed
}
pub fn transition_with<C: AnimationClock>(
&mut self,
runtime: &mut AnimationRuntime<C>,
transition: &RouteTransition<R>,
) -> Option<RouteTransitionRegistration<R>> {
self.invalidate_screen_if_stale(runtime);
let registration = self.inner.transition_with(runtime, transition)?;
let replaced = self.active_screen.take();
cleanup_replaced_screen(runtime, replaced);
Some(registration)
}
pub fn transition_to<C: AnimationClock>(
&mut self,
runtime: &mut AnimationRuntime<C>,
to: R,
transitions: &RouteTransitionSet<R>,
) -> Option<RouteTransitionRegistration<R>> {
self.invalidate_screen_if_stale(runtime);
let registration = self.inner.transition_to(runtime, to, transitions)?;
let replaced = self.active_screen.take();
cleanup_replaced_screen(runtime, replaced);
Some(registration)
}
pub fn transition_screens_with<C: AnimationClock>(
&mut self,
runtime: &mut AnimationRuntime<C>,
transition: &RouteScreenTransition<R>,
targets: RouteScreenTargets,
) -> Option<RouteScreenTransitionRegistration<R>> {
self.invalidate_screen_if_stale(runtime);
let route_transition = transition.route_transition();
let route = self.inner.transition_with(runtime, &route_transition)?;
let replaced = self.active_screen.take();
cleanup_replaced_screen(runtime, replaced);
let outgoing = runtime.register_timeline(targets.outgoing(), transition.outgoing().clone());
let incoming = runtime.register_timeline(targets.incoming(), transition.incoming().clone());
let active_route = *self.inner.active_transition()?;
self.active_screen = Some(ActiveRouteScreenTransition::new(
active_route,
self.inner.target(),
targets,
outgoing.handle(),
incoming.handle(),
));
Some(RouteScreenTransitionRegistration::new(
route, outgoing, incoming, replaced,
))
}
fn invalidate_screen_if_stale<C: AnimationClock>(
&mut self,
runtime: &AnimationRuntime<C>,
) -> bool {
let Some(active) = self.active_screen else {
return false;
};
if runtime.contains(active.route_target(), active.route().handle())
|| runtime.contains(active.outgoing_target(), active.outgoing_handle())
|| runtime.contains(active.incoming_target(), active.incoming_handle())
{
return false;
}
self.active_screen = None;
true
}
}
fn cleanup_replaced_screen<C, R>(
runtime: &mut AnimationRuntime<C>,
replaced: Option<ActiveRouteScreenTransition<R>>,
) where
C: AnimationClock,
R: Copy + Eq,
{
if let Some(active) = replaced {
runtime.cancel(active.route_target(), active.route().handle());
runtime.cancel(active.outgoing_target(), active.outgoing_handle());
runtime.cancel(active.incoming_target(), active.incoming_handle());
}
}