Skip to main content

aura_anim_iced/runtime/
target.rs

1use rustc_hash::FxHashMap;
2use smallvec::SmallVec;
3use std::sync::atomic::{AtomicU64, Ordering};
4
5use crate::PropertySnapshot;
6
7static NEXT_TARGET_ID: AtomicU64 = AtomicU64::new(AnimationTargetId::FIRST_ID);
8
9/// Stable identity for one animated UI target.
10///
11/// A target usually maps to one widget, element, or view-model object in the
12/// application. Runtime ticks keep snapshots scoped by this ID so unrelated
13/// widgets never share a global property merge.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
15pub struct AnimationTargetId(u64);
16
17/// Property output grouped by animation target.
18///
19/// When multiple active sources target the same ID, their snapshots are merged
20/// inside that target only. Composition never crosses target boundaries.
21#[derive(Debug, Clone, PartialEq)]
22pub struct TargetedPropertySnapshot {
23    targets: FxHashMap<AnimationTargetId, PropertySnapshot>,
24    order: SmallVec<[AnimationTargetId; 16]>,
25}
26
27impl AnimationTargetId {
28    const FIRST_ID: u64 = 1;
29
30    /// Creates a new process-local target ID.
31    #[must_use]
32    pub fn new() -> Self {
33        Self(NEXT_TARGET_ID.fetch_add(1, Ordering::Relaxed))
34    }
35
36    /// Returns the numeric target ID.
37    #[must_use]
38    pub const fn id(self) -> u64 {
39        self.0
40    }
41}
42
43impl Default for AnimationTargetId {
44    fn default() -> Self {
45        Self::new()
46    }
47}
48
49impl TargetedPropertySnapshot {
50    /// Creates an empty target-scoped snapshot.
51    #[must_use]
52    pub fn new() -> Self {
53        Self {
54            targets: FxHashMap::default(),
55            order: SmallVec::new(),
56        }
57    }
58
59    /// Returns whether no target produced properties.
60    #[must_use]
61    pub fn is_empty(&self) -> bool {
62        self.targets.is_empty()
63    }
64
65    /// Returns the snapshot for `target_id`.
66    #[must_use]
67    pub fn get(&self, target_id: AnimationTargetId) -> Option<&PropertySnapshot> {
68        self.targets.get(&target_id)
69    }
70
71    pub(crate) fn merge_entries(
72        &mut self,
73        target: AnimationTargetId,
74        entries: &[crate::property::PropertyEntry],
75    ) {
76        if let Some(snapshot) = self.targets.get_mut(&target) {
77            snapshot.merge_entries_unsorted(entries);
78            snapshot.sort_by_composition_key();
79        } else {
80            let mut snapshot = PropertySnapshot::with_capacity(entries.len());
81            snapshot.merge_entries_unsorted(entries);
82            snapshot.sort_by_composition_key();
83            self.order.push(target);
84            self.targets.insert(target, snapshot);
85        }
86    }
87
88    pub(crate) fn clear(&mut self) {
89        self.targets.clear();
90        self.order.clear();
91    }
92
93    /// Returns all target snapshots in runtime merge order.
94    pub fn targets(&self) -> impl Iterator<Item = (AnimationTargetId, &PropertySnapshot)> + '_ {
95        self.order.iter().map(|id| (*id, &self.targets[id]))
96    }
97}
98
99impl Default for TargetedPropertySnapshot {
100    fn default() -> Self {
101        Self::new()
102    }
103}