Skip to main content

ferroui_base/rendering/composition/animations/
implicit_animation_collection.rs

1use super::ICompositionAnimationBase;
2use crate::rendering::composition::{AsCompositionObject, CompositionObject, Compositor};
3use std::cell::RefCell;
4use std::collections::HashMap;
5use std::rc::Rc;
6
7fn same(a: &Rc<dyn ICompositionAnimationBase>, b: &Rc<dyn ICompositionAnimationBase>) -> bool {
8    std::ptr::addr_eq(Rc::as_ptr(a), Rc::as_ptr(b))
9}
10
11/// A collection of animations triggered when a condition is met.
12///
13/// Implicit animations let you drive animations by specifying trigger conditions rather than requiring the manual definition of animation behavior.
14/// They help decouple animation start logic from core app logic. You define animations and the events that should trigger these animations.
15/// Currently the only available trigger is animated property change.
16///
17/// When expression is used in ImplicitAnimationCollection a special keyword `this.FinalValue` will represent
18/// the final value of the animated property that was changed
19///
20/// Upstream the class is an `IDictionary<string, ICompositionAnimationBase>`;
21/// here the dictionary members are methods of the class. Entries are kept
22/// in insertion order, which is the enumeration order of a dictionary that
23/// has not had entries removed.
24pub struct ImplicitAnimationCollection {
25    object: CompositionObject,
26    inner: RefCell<Vec<(String, Rc<dyn ICompositionAnimationBase>)>>,
27}
28
29impl ImplicitAnimationCollection {
30    pub(crate) fn new(compositor: &Rc<Compositor>) -> Rc<ImplicitAnimationCollection> {
31        Rc::new(ImplicitAnimationCollection {
32            object: CompositionObject::new(compositor, None),
33            inner: RefCell::new(Vec::new()),
34        })
35    }
36
37    fn index_of(&self, key: &str) -> Option<usize> {
38        self.inner.borrow().iter().position(|(k, _)| k == key)
39    }
40
41    /// The entries of the collection (`GetEnumerator`).
42    pub fn items(&self) -> Vec<(String, Rc<dyn ICompositionAnimationBase>)> {
43        self.inner.borrow().clone()
44    }
45
46    /// `ICollection<KeyValuePair<..>>.Add`.
47    pub fn add_pair(&self, item: (String, Rc<dyn ICompositionAnimationBase>)) {
48        self.add(&item.0, item.1)
49    }
50
51    pub fn clear(&self) {
52        self.inner.borrow_mut().clear()
53    }
54
55    /// `ICollection<KeyValuePair<..>>.Contains`: whether the key is present
56    /// with that very animation.
57    pub fn contains_pair(&self, key: &str, value: &Rc<dyn ICompositionAnimationBase>) -> bool {
58        self.inner.borrow().iter().any(|(k, v)| k == key && same(v, value))
59    }
60
61    /// `ICollection<KeyValuePair<..>>.CopyTo`. Panics when the entries do
62    /// not fit in `array` from `array_index` on.
63    pub fn copy_to(&self, array: &mut [Option<(String, Rc<dyn ICompositionAnimationBase>)>], array_index: usize) {
64        let inner = self.inner.borrow();
65        if array_index > array.len() || array.len() - array_index < inner.len() {
66            panic!("Destination array is not long enough to copy all the items in the collection. Check array index and length.");
67        }
68        for (offset, item) in inner.iter().enumerate() {
69            array[array_index + offset] = Some(item.clone());
70        }
71    }
72
73    /// `ICollection<KeyValuePair<..>>.Remove`: removes the key only when it
74    /// is present with that very animation.
75    pub fn remove_pair(&self, key: &str, value: &Rc<dyn ICompositionAnimationBase>) -> bool {
76        if !self.contains_pair(key, value) {
77            return false;
78        }
79        self.remove(key)
80    }
81
82    pub fn count(&self) -> usize {
83        self.inner.borrow().len()
84    }
85
86    /// `ICollection<KeyValuePair<..>>.IsReadOnly`.
87    pub fn is_read_only(&self) -> bool {
88        false
89    }
90
91    /// Adds an entry. Panics when the key is already present.
92    pub fn add(&self, key: &str, value: Rc<dyn ICompositionAnimationBase>) {
93        if self.index_of(key).is_some() {
94            panic!("An item with the same key has already been added. Key: {key}");
95        }
96        self.inner.borrow_mut().push((key.to_owned(), value));
97    }
98
99    pub fn contains_key(&self, key: &str) -> bool {
100        self.index_of(key).is_some()
101    }
102
103    pub fn remove(&self, key: &str) -> bool {
104        match self.index_of(key) {
105            Some(index) => {
106                self.inner.borrow_mut().remove(index);
107                true
108            }
109            None => false,
110        }
111    }
112
113    pub fn try_get_value(&self, key: &str) -> Option<Rc<dyn ICompositionAnimationBase>> {
114        self.inner.borrow().iter().find(|(k, _)| k == key).map(|(_, v)| v.clone())
115    }
116
117    /// `this[key]`. Panics when the key is not present.
118    pub fn get(&self, key: &str) -> Rc<dyn ICompositionAnimationBase> {
119        match self.try_get_value(key) {
120            Some(value) => value,
121            None => panic!("The given key '{key}' was not present in the dictionary."),
122        }
123    }
124
125    /// `this[key] = value`: adds the entry or replaces its animation.
126    pub fn set(&self, key: &str, value: Rc<dyn ICompositionAnimationBase>) {
127        match self.index_of(key) {
128            Some(index) => self.inner.borrow_mut()[index].1 = value,
129            None => self.inner.borrow_mut().push((key.to_owned(), value)),
130        }
131    }
132
133    pub fn keys(&self) -> Vec<String> {
134        self.inner.borrow().iter().map(|(k, _)| k.clone()).collect()
135    }
136
137    pub fn values(&self) -> Vec<Rc<dyn ICompositionAnimationBase>> {
138        self.inner.borrow().iter().map(|(_, v)| v.clone()).collect()
139    }
140
141    // UWP compat
142    pub fn size(&self) -> u32 {
143        self.count() as u32
144    }
145
146    pub fn get_view(&self) -> HashMap<String, Rc<dyn ICompositionAnimationBase>> {
147        self.inner.borrow().iter().cloned().collect()
148    }
149
150    pub fn has_key(&self, key: &str) -> bool {
151        self.contains_key(key)
152    }
153
154    pub fn insert(&self, key: &str, animation: Rc<dyn ICompositionAnimationBase>) {
155        self.add(key, animation)
156    }
157
158    pub fn lookup(&self, key: &str) -> Option<Rc<dyn ICompositionAnimationBase>> {
159        self.try_get_value(key)
160    }
161}
162
163impl AsCompositionObject for ImplicitAnimationCollection {
164    fn as_composition_object(&self) -> &CompositionObject {
165        &self.object
166    }
167
168    fn composition_type_name(&self) -> &'static str {
169        "ImplicitAnimationCollection"
170    }
171}