mod clock;
mod entry;
mod handle;
mod policy;
mod registration;
mod registry;
mod source;
mod target;
#[cfg(test)]
mod tests;
mod tick;
pub use clock::{AnimationClock, SystemClock};
pub use entry::AnimationPlaybackState;
pub use handle::AnimationHandle;
pub use policy::TickPolicy;
pub use registration::AnimationRegistration;
pub use target::{AnimationTargetId, TargetedPropertySnapshot};
pub use tick::AnimationTick;
use crate::runtime::clock::TestClock;
use crate::runtime::{
entry::ActiveAnimation, registry::AnimationRegistry, source::AnimationSource,
};
use crate::{
keyframes::Keyframes, property::PropertySnapshot, timeline::Timeline, timing::Duration,
};
#[derive(Debug, Clone)]
pub struct AnimationRuntime<C = SystemClock> {
registry: AnimationRegistry,
clock: C,
motion_policy: TickPolicy,
}
impl AnimationRuntime<SystemClock> {
#[must_use]
pub fn new() -> Self {
Self::with_clock(SystemClock::new())
}
}
impl Default for AnimationRuntime<SystemClock> {
fn default() -> Self {
Self::new()
}
}
impl AnimationRuntime<TestClock> {
#[must_use]
pub fn testing() -> Self {
Self::with_clock(TestClock::new())
}
pub const fn clock_mut(&mut self) -> &mut TestClock {
&mut self.clock
}
}
impl<C: AnimationClock> AnimationRuntime<C> {
#[must_use]
pub fn with_clock(clock: C) -> Self {
Self {
registry: AnimationRegistry::new(),
clock,
motion_policy: TickPolicy::default(),
}
}
pub(crate) fn register_target(
&mut self,
target: AnimationTargetId,
source: impl Into<AnimationSource>,
) -> AnimationRegistration {
let handle = self.registry.allocate_handle();
let now = self.clock.now();
let source = source.into();
let initial_snapshot = source.sample_at(Duration::ZERO);
let mut entry = ActiveAnimation::new(handle, target, source, now);
entry.set_last_snapshot(initial_snapshot.clone());
if entry.source().total_duration() == Some(Duration::ZERO) {
let completion_snapshot = entry.source().completion_snapshot();
entry.set_last_snapshot(completion_snapshot);
entry.mark_completed(now);
}
let registration = AnimationRegistration::from_entry(&entry);
self.registry.insert(target, entry);
#[cfg(feature = "tracing")]
tracing::debug!(
target: "aura_anim_iced::runtime",
handle = registration.handle().id(),
target_id = ?target,
state = ?registration.state(),
completed = registration.completed_at().is_some(),
"registered animation"
);
registration
}
pub fn register_keyframes(
&mut self,
target: AnimationTargetId,
keyframes: Keyframes,
) -> AnimationRegistration {
self.register_target(target, keyframes)
}
pub fn register_timeline(
&mut self,
target: AnimationTargetId,
timeline: Timeline,
) -> AnimationRegistration {
self.register_target(target, timeline)
}
pub fn tick(&mut self) -> AnimationTick {
let now = self.clock.now();
tick::tick_registry(&mut self.registry, now)
}
pub fn cancel_target(&mut self, target: AnimationTargetId) {
self.registry.cancel_target(target);
}
pub fn seek_target(&mut self, target: AnimationTargetId, pos: Duration) {
self.registry.seek_target(target, pos, self.clock.now());
}
pub fn pause_target(&mut self, target: AnimationTargetId) {
self.registry.pause_target(target, self.clock.now());
}
pub fn cancel(&mut self, target: AnimationTargetId, handle: AnimationHandle) -> bool {
self.registry.cancel(target, handle)
}
pub fn seek(
&mut self,
target: AnimationTargetId,
handle: AnimationHandle,
pos: Duration,
) -> bool {
self.registry.seek(target, handle, pos, self.clock.now())
}
pub fn pause(&mut self, target: AnimationTargetId, handle: AnimationHandle) -> bool {
self.registry.pause(target, handle, self.clock.now())
}
pub(crate) fn last_properties(
&self,
target: AnimationTargetId,
handle: AnimationHandle,
) -> Option<&PropertySnapshot> {
let entry = self.registry.get_by_handle(handle)?;
if entry.target() != target {
return None;
}
entry.last_snapshot()
}
pub(crate) fn contains(&self, target: AnimationTargetId, handle: AnimationHandle) -> bool {
self.registry
.get_by_handle(handle)
.is_some_and(|entry| entry.target() == target)
}
}
impl<C> AnimationRuntime<C> {
#[must_use]
pub const fn clock(&self) -> &C {
&self.clock
}
#[must_use]
pub const fn motion_policy(&self) -> TickPolicy {
self.motion_policy
}
pub const fn set_motion_policy(&mut self, motion_policy: TickPolicy) {
self.motion_policy = motion_policy;
}
#[must_use]
pub fn active_count(&self) -> usize {
self.registry.active_count()
}
#[must_use]
pub fn is_idle(&self) -> bool {
self.active_count() == 0
}
#[must_use]
pub fn should_tick(&self) -> bool {
self.registry
.entries()
.iter()
.any(ActiveAnimation::needs_tick)
}
#[must_use]
pub fn should_subscribe(&self) -> bool {
self.should_tick()
}
}