ferroui_base/rendering/composition/server/
server_object_animations.rs1use super::{CompositionProperty, IAnimatedServerObject, ServerCompositor};
2use crate::rendering::composition::animations::IAnimationInstance;
3use crate::rendering::composition::expressions::ExpressionVariant;
4use std::cell::{Cell, RefCell};
5use std::rc::{Rc, Weak};
6use std::time::Duration;
7
8struct ServerObjectSubscriptionStore {
9 is_valid: Cell<bool>,
10 subscribers: RefCell<Vec<(Rc<dyn IAnimationInstance>, u32)>>,
12}
13
14impl ServerObjectSubscriptionStore {
15 fn new() -> Self {
16 Self { is_valid: Cell::new(true), subscribers: RefCell::new(Vec::new()) }
17 }
18
19 fn invalidate(&self) {
20 if !self.is_valid.get() {
21 return;
22 }
23 self.is_valid.set(false);
24 let subscribers: Vec<_> = self.subscribers.borrow().iter().map(|(s, _)| s.clone()).collect();
25 for subscriber in subscribers {
26 subscriber.invalidate();
27 }
28 }
29}
30
31struct ServerObjectAnimationInstance {
32 property: &'static CompositionProperty,
33 cached_variant: Cell<ExpressionVariant>,
34 is_dirty: Cell<bool>,
35 needs_update: Cell<bool>,
36 animation: Rc<dyn IAnimationInstance>,
37}
38
39fn same_animation(a: &Rc<dyn IAnimationInstance>, b: &Rc<dyn IAnimationInstance>) -> bool {
40 std::ptr::addr_eq(Rc::as_ptr(a), Rc::as_ptr(b))
41}
42
43pub struct ServerObjectAnimations {
46 owner: Weak<dyn IAnimatedServerObject>,
47 compositor: Weak<ServerCompositor>,
48 subscriptions: RefCell<Vec<(i32, Rc<ServerObjectSubscriptionStore>)>>,
49 animations: RefCell<Vec<Rc<ServerObjectAnimationInstance>>>,
50}
51
52impl ServerObjectAnimations {
53 pub fn new(owner: Weak<dyn IAnimatedServerObject>, compositor: Weak<ServerCompositor>) -> Self {
54 Self { owner, compositor, subscriptions: RefCell::new(Vec::new()), animations: RefCell::new(Vec::new()) }
55 }
56
57 fn owner_is_active(&self) -> bool {
58 self.owner.upgrade().is_some_and(|owner| owner.server_object().is_active())
59 }
60
61 fn subscription(&self, property: &CompositionProperty) -> Option<Rc<ServerObjectSubscriptionStore>> {
62 self.subscriptions.borrow().iter().find(|(id, _)| *id == property.id()).map(|(_, store)| store.clone())
63 }
64
65 fn animation(&self, property: &CompositionProperty) -> Option<Rc<ServerObjectAnimationInstance>> {
66 self.animations.borrow().iter().find(|a| a.property.id() == property.id()).cloned()
67 }
68
69 fn get_variant(&self, instance: &ServerObjectAnimationInstance) -> ExpressionVariant {
70 if !instance.is_dirty.get() {
71 return instance.cached_variant.get();
72 }
73 instance.is_dirty.set(false);
76 let now = self.compositor.upgrade().map_or(Duration::ZERO, |c| c.server_now());
77 let value = instance.animation.evaluate(now, instance.cached_variant.get());
78 instance.cached_variant.set(value);
79 value
80 }
81
82 fn update_target_property(&self, instance: &ServerObjectAnimationInstance) {
83 if !instance.needs_update.get() {
84 return;
85 }
86 instance.needs_update.set(false);
87 let Some(owner) = self.owner.upgrade() else { return };
88 let value = self.get_variant(instance);
89 if let Some(set) = instance.property.set_variant() {
90 set(owner.as_server_object_dyn(), value);
91 }
92 owner.notify_animated_value_changed(instance.property);
93 self.on_set_direct_value(instance.property);
94 }
95
96 pub fn activated(&self) {
97 let animations = self.animations.borrow().clone();
98 for instance in animations {
99 instance.animation.activate();
100 }
101 }
102
103 pub fn deactivated(&self) {
104 let animations = self.animations.borrow().clone();
105 for instance in animations {
106 instance.animation.deactivate();
107 }
108 }
109
110 pub fn on_set_direct_value(&self, property: &CompositionProperty) {
111 if let Some(subscriptions) = self.subscription(property) {
112 subscriptions.invalidate();
113 }
114 }
115
116 pub fn on_set_animated_value(
117 &self,
118 property: &'static CompositionProperty,
119 current_value: ExpressionVariant,
120 committed_at: Duration,
121 animation: Rc<dyn IAnimationInstance>,
122 ) {
123 let is_active = self.owner_is_active();
124 let old = self.animation(property);
125 if let (true, Some(old)) = (is_active, &old) {
126 old.animation.deactivate();
127 }
128 {
129 let mut animations = self.animations.borrow_mut();
130 animations.retain(|a| a.property.id() != property.id());
131 animations.push(Rc::new(ServerObjectAnimationInstance {
132 property,
133 cached_variant: Cell::new(ExpressionVariant::default()),
134 is_dirty: Cell::new(true),
135 needs_update: Cell::new(true),
136 animation: animation.clone(),
137 }));
138 }
139 if let Some(compositor) = self.compositor.upgrade() {
142 animation.resolve(&compositor);
143 }
144 animation.initialize(committed_at, current_value, property);
145 if is_active {
146 animation.activate();
147 }
148 self.on_set_direct_value(property);
149 }
150
151 pub fn remove_animation_for_property(&self, property: &CompositionProperty) {
152 let removed = {
153 let mut animations = self.animations.borrow_mut();
154 let index = animations.iter().position(|a| a.property.id() == property.id());
155 index.map(|index| animations.remove(index))
156 };
157 if let Some(removed) = removed {
158 if self.owner_is_active() {
159 removed.animation.deactivate();
160 }
161 }
162 self.on_set_direct_value(property);
163 }
164
165 pub fn subscribe_to_invalidation(&self, member: &CompositionProperty, animation: Rc<dyn IAnimationInstance>) {
166 let store = match self.subscription(member) {
167 Some(store) => store,
168 None => {
169 let store = Rc::new(ServerObjectSubscriptionStore::new());
170 self.subscriptions.borrow_mut().push((member.id(), store.clone()));
171 store
172 }
173 };
174 let mut subscribers = store.subscribers.borrow_mut();
175 match subscribers.iter_mut().find(|(s, _)| same_animation(s, &animation)) {
176 Some(entry) => entry.1 += 1,
177 None => subscribers.push((animation, 1)),
178 }
179 }
180
181 pub fn unsubscribe_from_invalidation(&self, member: &CompositionProperty, animation: &Rc<dyn IAnimationInstance>) {
182 if let Some(store) = self.subscription(member) {
183 let mut subscribers = store.subscribers.borrow_mut();
184 if let Some(index) = subscribers.iter().position(|(s, _)| same_animation(s, animation)) {
185 subscribers[index].1 -= 1;
186 if subscribers[index].1 == 0 {
187 subscribers.remove(index);
188 }
189 }
190 }
191 }
192
193 pub fn get_property_for_animation(&self, name: &str) -> ExpressionVariant {
194 let Some(owner) = self.owner.upgrade() else { return ExpressionVariant::default() };
195 let Some(property) = owner.get_composition_property(name) else { return ExpressionVariant::default() };
196 if let Some(subscriptions) = self.subscription(property) {
197 subscriptions.is_valid.set(true);
198 }
199 if let Some(animation) = self.animation(property) {
200 return self.get_variant(&animation);
201 }
202 property.get_variant().map(|get| get(owner.as_server_object_dyn())).unwrap_or_default()
203 }
204
205 pub fn evaluate_animations(&self) {
206 let animations = self.animations.borrow().clone();
207 for animation in animations {
208 if animation.is_dirty.get() {
209 self.update_target_property(&animation);
210 }
211 }
212 }
213
214 pub fn notify_animation_instance_invalidated(this: &Rc<Self>, property: &CompositionProperty) {
216 match this.animation(property) {
217 Some(instance) => {
218 instance.is_dirty.set(true);
219 instance.needs_update.set(true);
220 if let Some(compositor) = this.compositor.upgrade() {
221 compositor.animations().add_dirty_animated_object(this.clone());
222 }
223 }
224 None => debug_assert!(false, "the property has no animation"),
225 }
226 }
227}