Skip to main content

ferroui_base/rendering/composition/server/
server_composition_property_set.rs

1use super::{
2    CompositionProperty, IAnimatedServerObject, IServerAnimatedPropertyHost, IServerObject, IServerPropertyHost,
3    ServerCompositionSurfaceChanged, ServerCompositor, ServerObject, ServerValueChange,
4};
5use crate::rendering::composition::animations::IAnimationInstance;
6use crate::rendering::composition::animations::AnimationInstanceFactory;
7use crate::rendering::composition::expressions::ExpressionVariant;
8use crate::rendering::composition::transport::BatchStreamReader;
9use std::any::Any;
10use std::cell::RefCell;
11use std::collections::HashMap;
12use std::rc::{Rc, Weak};
13use std::time::Duration;
14
15/// The changes of a property set that one batch carries: the entries that were inserted, in
16/// the order of insertion, then the animations that were started.
17#[derive(Default)]
18pub(crate) struct PropertySetChanges {
19    pub(crate) values: Vec<(String, ExpressionVariant)>,
20    pub(crate) animations: Vec<(String, AnimationInstanceFactory)>,
21}
22
23/// The server-side counterpart of a live
24/// [`CompositionPropertySet`](crate::rendering::composition::CompositionPropertySet): its
25/// entries, animatable, readable by the expressions that refer to the set.
26///
27/// Not from upstream, whose property sets have no server side: an expression sees a copy of a
28/// referenced set taken when its animation starts. WinUI's property sets are live and their
29/// entries can be animated, which the WinUI composition API relies on (the property sets of
30/// `ElementCompositionPreview`, the effect property set of a connected animation, the
31/// interaction tracker).
32pub struct ServerCompositionPropertySet {
33    object: ServerObject,
34    values: RefCell<HashMap<i32, ExpressionVariant>>,
35    /// Raised when an entry changed, directly or by an animation: what reads
36    /// the entries outside expressions follows them (the effect brushes,
37    /// whose `Properties` hold their animatable properties).
38    changed: ServerCompositionSurfaceChanged,
39}
40
41impl ServerCompositionPropertySet {
42    /// Creates the property set, active: its animations run from the start, as a property set
43    /// is not part of a tree that activates it.
44    pub fn new(compositor: &Rc<ServerCompositor>) -> Rc<ServerCompositionPropertySet> {
45        let set = Rc::new_cyclic(|this: &Weak<ServerCompositionPropertySet>| ServerCompositionPropertySet {
46            object: ServerObject::new(compositor, this.clone() as Weak<dyn IAnimatedServerObject>),
47            values: RefCell::new(HashMap::new()),
48            changed: ServerCompositionSurfaceChanged::new(),
49        });
50        set.object.activate();
51        set
52    }
53
54    /// The current value of an entry, animated or not; the default variant for an entry the set
55    /// does not have.
56    pub fn get_value(&self, name: &str) -> ExpressionVariant {
57        self.object.get_property(name)
58    }
59
60    fn property(name: &str) -> &'static CompositionProperty {
61        CompositionProperty::dynamic(name)
62    }
63
64    /// Raised when an entry of the set changed, by a batch or by an
65    /// animation (not from upstream; the effect brushes follow the entries
66    /// that are their animatable properties).
67    pub fn changed(&self) -> &ServerCompositionSurfaceChanged {
68        &self.changed
69    }
70}
71
72
73impl IServerObject for ServerCompositionPropertySet {
74    fn deserialize_changes_core(&self, reader: &mut BatchStreamReader<'_>, committed_at: Duration) {
75        let Some(changes) = reader.read_value::<PropertySetChanges>() else { return };
76        for (name, value) in changes.values {
77            let property = Self::property(&name);
78            self.values.borrow_mut().insert(property.id(), value);
79            // A value set directly replaces the animation of the entry, and the expressions that
80            // read the entry are invalidated (`SetValue` of a declared property).
81            self.object.remove_animation_for_property(property);
82        }
83        for (name, factory) in changes.animations {
84            let property = Self::property(&name);
85            let current = self.values.borrow().get(&property.id()).copied().unwrap_or_default();
86            self.object.set_animated_value(property, current, committed_at, factory.create());
87        }
88        self.changed.invoke();
89    }
90
91    fn as_animated(self: Rc<Self>) -> Option<Rc<dyn IAnimatedServerObject>> {
92        Some(self)
93    }
94
95    fn as_any(&self) -> &dyn Any {
96        self
97    }
98
99    fn into_any_rc(self: Rc<Self>) -> Rc<dyn Any> {
100        self
101    }
102}
103
104impl IServerPropertyHost for ServerCompositionPropertySet {
105    fn server_compositor(&self) -> Option<Rc<ServerCompositor>> {
106        self.object.compositor()
107    }
108
109    fn set_value(&self, property: &'static CompositionProperty, change: ServerValueChange<'_>) {
110        self.object.set_value(property, change);
111    }
112}
113
114impl IServerAnimatedPropertyHost for ServerCompositionPropertySet {
115    fn set_animated_value(
116        &self,
117        property: &'static CompositionProperty,
118        current_value: ExpressionVariant,
119        committed_at: Duration,
120        animation: Rc<dyn IAnimationInstance>,
121    ) {
122        self.object.set_animated_value(property, current_value, committed_at, animation);
123    }
124
125    fn remove_animation_for_property(&self, property: &'static CompositionProperty) {
126        self.object.remove_animation_for_property(property);
127    }
128
129    fn notify_animated_value_changed(&self, _property: &'static CompositionProperty) {
130        self.changed.invoke();
131    }
132}
133
134impl IAnimatedServerObject for ServerCompositionPropertySet {
135    fn server_object(&self) -> &ServerObject {
136        &self.object
137    }
138
139    /// Every name is an entry of a property set; one that was never inserted reads as the
140    /// default variant, as a missing parameter does.
141    fn get_composition_property(&self, field_name: &str) -> Option<&'static CompositionProperty> {
142        Some(Self::property(field_name))
143    }
144
145    fn as_server_object_dyn(&self) -> &dyn IServerObject {
146        self
147    }
148
149    fn read_property_variant(&self, property: &'static CompositionProperty) -> ExpressionVariant {
150        self.values.borrow().get(&property.id()).copied().unwrap_or_default()
151    }
152
153    fn write_property_variant(&self, property: &'static CompositionProperty, value: ExpressionVariant) {
154        self.values.borrow_mut().insert(property.id(), value);
155    }
156}