ferroui_base/rendering/composition/
composition_object.rs1use super::animations::{ICompositionAnimation, ICompositionAnimationBase, ImplicitAnimationCollection};
2use super::expressions::ExpressionVariant;
3use super::server::{CompositionProperty, ServerObjectId};
4use super::{CompositionPropertySet, Compositor, ICompositorSerializable, PendingAnimations};
5use std::cell::{Cell, RefCell};
6use std::rc::Rc;
7
8pub struct CompositionObject {
26 compositor: Rc<Compositor>,
27 server: Option<ServerObjectId>,
28 is_disposed: Cell<bool>,
29 registered_for_serialization: Cell<bool>,
30 pending_animations: PendingAnimations,
31 implicit_animations: RefCell<Option<Rc<ImplicitAnimationCollection>>>,
32}
33
34impl CompositionObject {
35 pub fn new(compositor: &Rc<Compositor>, server: Option<ServerObjectId>) -> Self {
36 Self {
37 compositor: compositor.clone(),
38 server,
39 is_disposed: Cell::new(false),
40 registered_for_serialization: Cell::new(false),
41 pending_animations: PendingAnimations::new(),
42 implicit_animations: RefCell::new(None),
43 }
44 }
45
46 pub fn implicit_animations(&self) -> Option<Rc<ImplicitAnimationCollection>> {
48 self.implicit_animations.borrow().clone()
49 }
50
51 pub fn set_implicit_animations(&self, value: Option<Rc<ImplicitAnimationCollection>>) {
52 *self.implicit_animations.borrow_mut() = value;
53 }
54
55 pub fn implicit_animation(&self, property_name: &str) -> Option<Rc<dyn ICompositionAnimationBase>> {
59 let animations = self.implicit_animations.borrow().clone()?;
60 animations.try_get_value(property_name)
61 }
62
63 pub fn compositor(&self) -> &Rc<Compositor> {
64 &self.compositor
65 }
66
67 pub fn server(&self) -> Option<ServerObjectId> {
69 self.server
70 }
71
72 pub fn required_server(&self) -> ServerObjectId {
74 match self.server {
75 Some(server) => server,
76 None => panic!("There is no server-side counterpart for this object"),
77 }
78 }
79
80 pub fn try_get_server(&self, c: &Compositor) -> Option<ServerObjectId> {
83 debug_assert!(std::ptr::eq(c, &*self.compositor));
84 if self.is_disposed.get() {
85 return None;
86 }
87 Some(self.required_server())
88 }
89
90 pub fn is_disposed(&self) -> bool {
91 self.is_disposed.get()
92 }
93
94 pub fn pending_animations(&self) -> &PendingAnimations {
95 &self.pending_animations
96 }
97
98 pub fn dispose(&self) {
99 if !self.is_disposed.get() {
100 if let Some(server) = self.server {
101 self.compositor.dispose_and_keep_on_next_batch(server);
102 }
103 }
104 self.is_disposed.set(true);
105 }
106
107 pub(crate) fn mark_disposed(&self) {
112 self.is_disposed.set(true);
113 }
114
115 pub fn register_for_serialization(&self, this: impl FnOnce() -> Option<Rc<dyn ICompositorSerializable>>) {
118 if self.server.is_none() {
119 panic!("The object doesn't have an associated server counterpart");
120 }
121 if self.registered_for_serialization.get() || self.is_disposed.get() {
122 return;
123 }
124 if let Some(this) = this() {
125 self.registered_for_serialization.set(true);
126 self.compositor.register_for_serialization(this);
127 }
128 }
129
130 pub fn begin_serialize_changes(&self, c: &Compositor) {
133 debug_assert!(std::ptr::eq(c, &*self.compositor));
134 self.registered_for_serialization.set(false);
135 }
136
137 pub fn stop_animation(&self, property: &'static CompositionProperty) {
142 let Some(server) = self.server else { return };
143 self.compositor.post_server_job(
144 move |compositor| {
145 if let Some(object) = compositor.get_animated_object(server) {
146 if let Some(animations) = object.server_object().animations() {
147 animations.remove_animation_for_property(property);
148 }
149 }
150 },
151 false,
152 );
153 }
154}
155
156impl Drop for CompositionObject {
157 fn drop(&mut self) {
158 if let Some(server) = self.server {
159 if self.is_disposed.get() {
160 self.compositor.release_with_a_later_batch(server);
161 } else {
162 self.compositor.dispose_with_a_later_batch(server);
163 }
164 }
165 }
166}
167
168pub trait AsCompositionObject: 'static {
172 fn as_composition_object(&self) -> &CompositionObject;
174
175 fn composition_type_name(&self) -> &'static str;
177
178 fn as_property_set(self: Rc<Self>) -> Option<Rc<CompositionPropertySet>> {
181 None
182 }
183}
184
185pub trait ICompositionObjectAnimations {
188 fn try_start_animation(
191 &self,
192 property_name: &str,
193 animation: &dyn ICompositionAnimation,
194 final_value: Option<ExpressionVariant>,
195 ) -> bool;
196
197 fn get_composition_property(&self, property_name: &str) -> Option<&'static CompositionProperty>;
199
200 fn composition_object(&self) -> &CompositionObject;
202
203 fn start_animation(&self, property_name: &str, animation: &dyn ICompositionAnimation) {
206 self.start_animation_with_final_value(property_name, animation, None)
207 }
208
209 fn start_animation_with_final_value(
210 &self,
211 property_name: &str,
212 animation: &dyn ICompositionAnimation,
213 final_value: Option<ExpressionVariant>,
214 ) {
215 if !self.try_start_animation(property_name, animation, final_value) {
216 panic!("Unknown property {property_name}");
217 }
218 }
219
220 fn stop_animation(&self, property_name: &str) {
222 let Some(property) = self.get_composition_property(property_name) else {
223 panic!("Unknown property {property_name}");
224 };
225 self.composition_object().stop_animation(property);
226 }
227
228 fn start_animation_group(&self, grp: &dyn ICompositionAnimationBase) {
232 if let Some(animation) = grp.as_composition_animation() {
233 let Some(target) = animation.target() else { panic!("Animation Target can't be null") };
234 self.start_animation(&target, animation);
235 } else if let Some(group) = grp.as_composition_animation_group() {
236 for a in group.animations() {
237 let Some(target) = a.target() else { panic!("Animation Target can't be null") };
238 self.start_animation(&target, &*a);
239 }
240 }
241 }
242
243 fn start_animation_group_part(
245 &self,
246 animation: &dyn ICompositionAnimation,
247 target: &str,
248 final_value: ExpressionVariant,
249 ) -> bool {
250 let Some(animation_target) = animation.target() else { panic!("Animation Target can't be null") };
251 if animation_target == target {
252 self.start_animation_with_final_value(&animation_target, animation, Some(final_value));
253 true
254 } else {
255 self.start_animation(&animation_target, animation);
256 false
257 }
258 }
259
260 fn start_animation_group_for(
264 &self,
265 grp: &dyn ICompositionAnimationBase,
266 target: &str,
267 final_value: ExpressionVariant,
268 ) -> bool {
269 if let Some(animation) = grp.as_composition_animation() {
270 return self.start_animation_group_part(animation, target, final_value);
271 }
272 if let Some(group) = grp.as_composition_animation_group() {
273 let mut matched = false;
274 for a in group.animations() {
275 if a.target().is_none() {
276 panic!("Animation Target can't be null");
277 }
278 if self.start_animation_group_part(&*a, target, final_value) {
279 matched = true;
280 }
281 }
282 return matched;
283 }
284
285 panic!("the animation is neither an animation nor a group of animations");
286 }
287
288 fn stop_animation_group(&self, grp: &dyn ICompositionAnimationBase) {
290 if let Some(animation) = grp.as_composition_animation() {
291 let Some(target) = animation.target() else { panic!("Animation Target can't be null") };
292 self.stop_animation(&target);
293 } else if let Some(group) = grp.as_composition_animation_group() {
294 for a in group.animations() {
295 let Some(target) = a.target() else { panic!("Animation Target can't be null") };
296 self.stop_animation(&target);
297 }
298 }
299 }
300}