ferroui_base/rendering/composition/animations/
animation_instance_base.rs1use super::{IAnimationInstance, PropertySetSnapshot};
2use crate::rendering::composition::expressions::{ExpressionKeywords, IExpressionObject};
3use crate::rendering::composition::server::{
4 CompositionProperty, IAnimatedServerObject, IServerClockItem, ServerCompositor, ServerExpressionObject,
5 ServerObjectAnimations, ServerObjectId,
6};
7use std::cell::{Cell, RefCell};
8use std::collections::HashSet;
9use std::rc::{Rc, Weak};
10
11pub struct AnimationInstanceBase {
23 this: Weak<dyn IAnimationInstance>,
24 tracked_objects: RefCell<Option<Vec<(Weak<dyn IAnimatedServerObject>, &'static CompositionProperty)>>>,
25 parameters: Rc<PropertySetSnapshot>,
26 target_object: ServerObjectId,
27 target: RefCell<Option<Weak<dyn IAnimatedServerObject>>>,
28 compositor: RefCell<Weak<ServerCompositor>>,
29 property: Cell<Option<&'static CompositionProperty>>,
30 invalidated: Cell<bool>,
31}
32
33impl AnimationInstanceBase {
34 pub fn new(this: Weak<dyn IAnimationInstance>, target: ServerObjectId, parameters: Rc<PropertySetSnapshot>) -> Self {
35 Self {
36 this,
37 tracked_objects: RefCell::new(None),
38 parameters,
39 target_object: target,
40 target: RefCell::new(None),
41 compositor: RefCell::new(Weak::new()),
42 property: Cell::new(None),
43 invalidated: Cell::new(false),
44 }
45 }
46
47 pub fn parameters(&self) -> &Rc<PropertySetSnapshot> {
48 &self.parameters
49 }
50
51 pub fn target_object(&self) -> ServerObjectId {
53 self.target_object
54 }
55
56 pub fn target(&self) -> Option<Rc<dyn IAnimatedServerObject>> {
59 self.target.borrow().as_ref().and_then(Weak::upgrade)
60 }
61
62 pub fn compositor(&self) -> Option<Rc<ServerCompositor>> {
65 self.compositor.borrow().upgrade()
66 }
67
68 pub fn property(&self) -> Option<&'static CompositionProperty> {
70 self.property.get()
71 }
72
73 pub fn this_clock_item(&self) -> Option<Rc<dyn IServerClockItem>> {
75 self.this.upgrade().map(|this| this as Rc<dyn IServerClockItem>)
76 }
77
78 pub fn resolve(&self, compositor: &Rc<ServerCompositor>) {
86 *self.compositor.borrow_mut() = Rc::downgrade(compositor);
87 *self.target.borrow_mut() =
88 compositor.get_animated_object(self.target_object).map(|target| Rc::downgrade(&target));
89 self.parameters.resolve(compositor);
90 }
91
92 pub fn with_target_expression_object<R>(&self, f: impl FnOnce(Option<&dyn IExpressionObject>) -> R) -> R {
94 match self.target() {
95 Some(target) => {
96 let target = ServerExpressionObject(target);
97 f(Some(&target))
98 }
99 None => f(None),
100 }
101 }
102
103 pub fn initialize(&self, property: &'static CompositionProperty, tracked_objects: &HashSet<(String, String)>) {
105 if !tracked_objects.is_empty() {
106 let mut tracked_list = Vec::new();
107 for (name, member) in tracked_objects {
108 let obj = if name == ExpressionKeywords::TARGET {
109 self.target()
110 } else {
111 self.parameters.get_server_object_parameter(name)
112 };
113 if let Some(tracked) = obj {
114 let Some(off) = tracked.get_composition_property(member) else {
115 if cfg!(debug_assertions) {
116 panic!("Attempting to subscribe to unknown field");
117 }
118 continue;
119 };
120 tracked_list.push((Rc::downgrade(&tracked), off));
121 }
122 }
123 *self.tracked_objects.borrow_mut() = Some(tracked_list);
124 }
125
126 self.property.set(Some(property));
127 }
128
129 pub fn begin_evaluate(&self) {
132 self.invalidated.set(false);
133 }
134
135 fn tracked(&self) -> Vec<(Rc<dyn IAnimatedServerObject>, &'static CompositionProperty)> {
136 self.tracked_objects
137 .borrow()
138 .iter()
139 .flatten()
140 .filter_map(|(obj, member)| obj.upgrade().map(|obj| (obj, *member)))
141 .collect()
142 }
143
144 pub fn activate(&self) {
145 let Some(this) = self.this.upgrade() else { return };
146 for (obj, member) in self.tracked() {
147 obj.server_object().get_or_create_animations().subscribe_to_invalidation(member, this.clone());
148 }
149 }
150
151 pub fn deactivate(&self) {
152 let Some(this) = self.this.upgrade() else { return };
153 for (obj, member) in self.tracked() {
154 if let Some(animations) = obj.server_object().animations() {
155 animations.unsubscribe_from_invalidation(member, &this);
156 }
157 }
158 }
159
160 pub fn invalidate(&self) {
161 if self.invalidated.get() {
162 return;
163 }
164 self.invalidated.set(true);
165 let (Some(target), Some(property)) = (self.target(), self.property.get()) else { return };
166 if let Some(animations) = target.server_object().animations() {
167 ServerObjectAnimations::notify_animation_instance_invalidated(&animations, property);
168 }
169 }
170
171 pub fn on_tick(&self) {
172 self.invalidate()
173 }
174}
175