ferroui_base/rendering/composition/
composition_object.rs1use super::animations::{
2 extract_subchannel, parse_subchannel, AnimationController, ICompositionAnimation, ICompositionAnimationBase,
3 ImplicitAnimationCollection, PreparedAnimation, SubchannelAnimation,
4};
5use super::expressions::ExpressionVariant;
6use super::server::{CompositionProperty, ServerObjectId};
7use super::{CompositionPropertySet, Compositor, ICompositorSerializable, PendingAnimations};
8use std::cell::{Cell, RefCell};
9use std::rc::Rc;
10
11pub struct CompositionObject {
29 compositor: Rc<Compositor>,
30 server: Option<ServerObjectId>,
31 is_disposed: Cell<bool>,
32 registered_for_serialization: Cell<bool>,
33 pending_animations: PendingAnimations,
34 implicit_animations: RefCell<Option<Rc<ImplicitAnimationCollection>>>,
35 properties: RefCell<Option<Rc<CompositionPropertySet>>>,
37 comment: RefCell<String>,
39 animation_controllers: RefCell<Vec<(String, Option<Rc<AnimationController>>)>>,
43}
44
45impl CompositionObject {
46 pub fn new(compositor: &Rc<Compositor>, server: Option<ServerObjectId>) -> Self {
47 Self {
48 compositor: compositor.clone(),
49 server,
50 is_disposed: Cell::new(false),
51 registered_for_serialization: Cell::new(false),
52 pending_animations: PendingAnimations::new(),
53 implicit_animations: RefCell::new(None),
54 properties: RefCell::new(None),
55 comment: RefCell::new(String::new()),
56 animation_controllers: RefCell::new(Vec::new()),
57 }
58 }
59
60 pub fn properties(&self) -> Rc<CompositionPropertySet> {
71 self.properties
72 .borrow_mut()
73 .get_or_insert_with(|| CompositionPropertySet::new(&self.compositor))
74 .clone()
75 }
76
77 pub fn comment(&self) -> String {
80 self.comment.borrow().clone()
81 }
82
83 pub fn set_comment(&self, value: String) {
84 *self.comment.borrow_mut() = value;
85 }
86
87 pub(crate) fn note_animation_started(&self, property_name: &str, controller: Option<Rc<AnimationController>>) {
90 let mut controllers = self.animation_controllers.borrow_mut();
91 match controllers.iter_mut().find(|(name, _)| name == property_name) {
92 Some(entry) => entry.1 = controller,
93 None => controllers.push((property_name.to_owned(), controller)),
94 }
95 }
96
97 pub(crate) fn note_animation_stopped(&self, property_name: &str) {
99 self.animation_controllers.borrow_mut().retain(|(name, _)| name != property_name);
100 }
101
102 pub(crate) fn animation_controller_entry(&self, property_name: &str) -> Option<Option<Rc<AnimationController>>> {
106 self.animation_controllers
107 .borrow()
108 .iter()
109 .find(|(name, _)| name == property_name)
110 .map(|(_, controller)| controller.clone())
111 }
112
113 pub(crate) fn attach_animation_controller(
116 &self,
117 property_name: &str,
118 property: &'static CompositionProperty,
119 controller: Rc<AnimationController>,
120 ) {
121 let controller_id = controller.server();
122 self.note_animation_started(property_name, Some(controller));
123 let Some(server) = self.server else { return };
124 self.compositor.post_server_job(
125 move |compositor| {
126 if let Some(object) = compositor.get_animated_object(server) {
127 if let Some(animations) = object.server_object().animations() {
128 animations.attach_controller(property, controller_id);
129 }
130 }
131 },
132 false,
133 );
134 }
135
136 pub fn implicit_animations(&self) -> Option<Rc<ImplicitAnimationCollection>> {
138 self.implicit_animations.borrow().clone()
139 }
140
141 pub fn set_implicit_animations(&self, value: Option<Rc<ImplicitAnimationCollection>>) {
142 *self.implicit_animations.borrow_mut() = value;
143 }
144
145 pub fn implicit_animation(&self, property_name: &str) -> Option<Rc<dyn ICompositionAnimationBase>> {
149 let animations = self.implicit_animations.borrow().clone()?;
150 animations.try_get_value(property_name)
151 }
152
153 pub fn compositor(&self) -> &Rc<Compositor> {
154 &self.compositor
155 }
156
157 pub fn server(&self) -> Option<ServerObjectId> {
159 self.server
160 }
161
162 pub fn required_server(&self) -> ServerObjectId {
164 match self.server {
165 Some(server) => server,
166 None => panic!("There is no server-side counterpart for this object"),
167 }
168 }
169
170 pub fn try_get_server(&self, c: &Compositor) -> Option<ServerObjectId> {
173 debug_assert!(std::ptr::eq(c, &*self.compositor));
174 if self.is_disposed.get() {
175 return None;
176 }
177 Some(self.required_server())
178 }
179
180 pub fn is_disposed(&self) -> bool {
181 self.is_disposed.get()
182 }
183
184 pub fn pending_animations(&self) -> &PendingAnimations {
185 &self.pending_animations
186 }
187
188 pub fn dispose(&self) {
189 if !self.is_disposed.get() {
190 if let Some(server) = self.server {
191 self.compositor.dispose_and_keep_on_next_batch(server);
192 }
193 }
194 if let (false, Some(server), true) =
197 (self.is_disposed.get(), self.server, self.compositor.animation_batches().used())
198 {
199 self.compositor.post_server_job(
200 move |compositor| {
201 if let Some(object) = compositor.get_animated_object(server) {
202 if let Some(animations) = object.server_object().animations() {
203 animations.release_completions();
204 }
205 }
206 },
207 false,
208 );
209 }
210 self.is_disposed.set(true);
211 }
212
213 pub(crate) fn mark_disposed(&self) {
218 self.is_disposed.set(true);
219 }
220
221 pub fn register_for_serialization(&self, this: impl FnOnce() -> Option<Rc<dyn ICompositorSerializable>>) {
224 if self.server.is_none() {
225 panic!("The object doesn't have an associated server counterpart");
226 }
227 if self.registered_for_serialization.get() || self.is_disposed.get() {
228 return;
229 }
230 if let Some(this) = this() {
231 self.registered_for_serialization.set(true);
232 self.compositor.register_for_serialization(this);
233 }
234 }
235
236 pub fn begin_serialize_changes(&self, c: &Compositor) {
239 debug_assert!(std::ptr::eq(c, &*self.compositor));
240 self.registered_for_serialization.set(false);
241 }
242
243 pub fn stop_animation(&self, property: &'static CompositionProperty) {
248 let Some(server) = self.server else { return };
249 self.compositor.post_server_job(
250 move |compositor| {
251 if let Some(object) = compositor.get_animated_object(server) {
252 if let Some(animations) = object.server_object().animations() {
253 animations.stop_animation_for_property(property);
254 }
255 }
256 },
257 false,
258 );
259 }
260
261 pub fn stop_subchannel_animation(&self, property: &'static CompositionProperty, components: Vec<usize>) {
265 let Some(server) = self.server else { return };
266 self.compositor.post_server_job(
267 move |compositor| {
268 if let Some(object) = compositor.get_animated_object(server) {
269 if let Some(animations) = object.server_object().animations() {
270 animations.stop_subchannel_animation(property, &components);
271 }
272 }
273 },
274 false,
275 );
276 }
277}
278
279impl Drop for CompositionObject {
280 fn drop(&mut self) {
281 if let Some(server) = self.server {
282 if self.is_disposed.get() {
283 self.compositor.release_with_a_later_batch(server);
284 } else {
285 self.compositor.dispose_with_a_later_batch(server);
286 }
287 }
288 }
289}
290
291pub trait AsCompositionObject: 'static {
295 fn as_composition_object(&self) -> &CompositionObject;
297
298 fn composition_type_name(&self) -> &'static str;
300
301 fn as_property_set(self: Rc<Self>) -> Option<Rc<CompositionPropertySet>> {
304 None
305 }
306}
307
308pub trait ICompositionObjectAnimations {
311 fn try_start_animation(
314 &self,
315 property_name: &str,
316 animation: &dyn ICompositionAnimation,
317 final_value: Option<ExpressionVariant>,
318 ) -> bool;
319
320 fn get_composition_property(&self, property_name: &str) -> Option<&'static CompositionProperty>;
322
323 fn composition_object(&self) -> &CompositionObject;
325
326 fn start_animation(&self, property_name: &str, animation: &dyn ICompositionAnimation) {
329 self.start_animation_with_final_value(property_name, animation, None)
330 }
331
332 fn start_animation_with_final_value(
340 &self,
341 property_name: &str,
342 animation: &dyn ICompositionAnimation,
343 final_value: Option<ExpressionVariant>,
344 ) {
345 start_animation_core(self, property_name, animation, final_value, None)
346 }
347
348 fn start_animation_with_controller(
353 &self,
354 property_name: &str,
355 animation: &dyn ICompositionAnimation,
356 controller: &Rc<AnimationController>,
357 ) {
358 start_animation_core(self, property_name, animation, None, Some(controller))
359 }
360
361 fn try_get_animation_controller(&self, property_name: &str) -> Option<Rc<AnimationController>> {
371 let object = self.composition_object();
372 if let Some(controller) = object.animation_controller_entry(property_name)? {
373 return Some(controller);
374 }
375 let property = match parse_subchannel(property_name) {
376 Some((property, _)) => property,
377 None => property_name,
378 };
379 let property = self.get_composition_property(property)?;
380 let controller = AnimationController::new(object.compositor());
381 object.attach_animation_controller(property_name, property, controller.clone());
382 Some(controller)
383 }
384
385 fn stop_animation(&self, property_name: &str) {
390 self.composition_object().note_animation_stopped(property_name);
391 if let Some((property, components)) = parse_subchannel(property_name) {
392 let Some(property) = self.get_composition_property(property) else {
393 panic!("Unknown property {property_name}");
394 };
395 self.composition_object().stop_subchannel_animation(property, components);
396 return;
397 }
398 let Some(property) = self.get_composition_property(property_name) else {
399 panic!("Unknown property {property_name}");
400 };
401 self.composition_object().stop_animation(property);
402 }
403
404 fn start_animation_group(&self, grp: &dyn ICompositionAnimationBase) {
408 if let Some(animation) = grp.as_composition_animation() {
409 let Some(target) = animation.target() else { panic!("Animation Target can't be null") };
410 self.start_animation(&target, animation);
411 } else if let Some(group) = grp.as_composition_animation_group() {
412 for a in group.animations() {
413 let Some(target) = a.target() else { panic!("Animation Target can't be null") };
414 self.start_animation(&target, &*a);
415 }
416 }
417 }
418
419 fn start_animation_group_part(
421 &self,
422 animation: &dyn ICompositionAnimation,
423 target: &str,
424 final_value: ExpressionVariant,
425 ) -> bool {
426 let Some(animation_target) = animation.target() else { panic!("Animation Target can't be null") };
427 if animation_target == target {
428 self.start_animation_with_final_value(&animation_target, animation, Some(final_value));
429 true
430 } else {
431 self.start_animation(&animation_target, animation);
432 false
433 }
434 }
435
436 fn start_animation_group_for(
440 &self,
441 grp: &dyn ICompositionAnimationBase,
442 target: &str,
443 final_value: ExpressionVariant,
444 ) -> bool {
445 if let Some(animation) = grp.as_composition_animation() {
446 return self.start_animation_group_part(animation, target, final_value);
447 }
448 if let Some(group) = grp.as_composition_animation_group() {
449 let mut matched = false;
450 for a in group.animations() {
451 if a.target().is_none() {
452 panic!("Animation Target can't be null");
453 }
454 if self.start_animation_group_part(&*a, target, final_value) {
455 matched = true;
456 }
457 }
458 return matched;
459 }
460
461 panic!("the animation is neither an animation nor a group of animations");
462 }
463
464 fn stop_animation_group(&self, grp: &dyn ICompositionAnimationBase) {
466 if let Some(animation) = grp.as_composition_animation() {
467 let Some(target) = animation.target() else { panic!("Animation Target can't be null") };
468 self.stop_animation(&target);
469 } else if let Some(group) = grp.as_composition_animation_group() {
470 for a in group.animations() {
471 let Some(target) = a.target() else { panic!("Animation Target can't be null") };
472 self.stop_animation(&target);
473 }
474 }
475 }
476}
477
478fn start_animation_core<O: ICompositionObjectAnimations + ?Sized>(
481 this: &O,
482 property_name: &str,
483 animation: &dyn ICompositionAnimation,
484 final_value: Option<ExpressionVariant>,
485 controller: Option<&Rc<AnimationController>>,
486) {
487 if let Some((property, components)) = parse_subchannel(property_name) {
488 let target = this.composition_object().required_server();
492 let channel_final_value = final_value.map(|value| extract_subchannel(value, &components));
493 let mut inner = animation.create_instance(target, channel_final_value);
494 if let Some(controller) = controller {
495 inner = controller.wrap(inner);
496 }
497 let previous = this
501 .get_composition_property(property)
502 .and_then(|p| this.composition_object().pending_animations().get_and_remove(p));
503 let wrapper = SubchannelAnimation::new(components, inner, previous);
504 if !this.try_start_animation(property, &wrapper, None) {
505 panic!("Unknown property {property_name}");
506 }
507 } else if let Some(controller) = controller {
508 let Some(target) = this.composition_object().server() else { panic!("Unknown property {property_name}") };
511 let prepared = PreparedAnimation::new(controller.wrap(animation.create_instance(target, final_value)));
512 if !this.try_start_animation(property_name, &prepared, None) {
513 panic!("Unknown property {property_name}");
514 }
515 } else if !this.try_start_animation(property_name, animation, final_value) {
516 panic!("Unknown property {property_name}");
517 }
518 this.composition_object().note_animation_started(property_name, controller.cloned());
519}