Skip to main content

ferroui_base/rendering/composition/server/
server_property_host.rs

1//! The seam between the generated property blocks of server objects and the
2//! hand-written classes that embed them.
3
4use super::{CompositionProperty, IServerObject, IServerRenderResource, ServerCompositor};
5use crate::rendering::composition::animations::IAnimationInstance;
6use crate::rendering::composition::expressions::ExpressionVariant;
7use crate::rendering::composition::transport::{BatchResource, BatchStreamReader};
8use std::rc::Rc;
9use std::time::Duration;
10
11/// One assignment of a property field, in the form `SetValue(prop, ref
12/// field, value)` takes it upstream: the class decides what else happens
13/// around the assignment.
14pub struct ServerValueChange<'a> {
15    /// Whether the new value equals the current one.
16    pub equal: bool,
17    /// The render resource behind the current value, if it is one.
18    pub old_resource: Option<Rc<dyn IServerRenderResource>>,
19    /// The render resource behind the new value, if it is one.
20    pub new_resource: Option<Rc<dyn IServerRenderResource>>,
21    /// Performs `field = value`.
22    pub assign: &'a mut dyn FnMut(),
23}
24
25/// What a generated server property block needs from the object that embeds
26/// it (upstream `SimpleServerObject`).
27pub trait IServerPropertyHost {
28    /// The compositor of the object, while it is alive. Used to resolve the
29    /// server objects a batch refers to.
30    fn server_compositor(&self) -> Option<Rc<ServerCompositor>>;
31
32    /// `SetValue(prop, ref field, value)` as the class of the object
33    /// defines it. A plain server object assigns; an animated object also
34    /// invalidates the subscriptions of the property; a render resource
35    /// goes through [`super::ServerRenderResourceCore::set_value`].
36    fn set_value(&self, property: &'static CompositionProperty, change: ServerValueChange<'_>) {
37        let _ = property;
38        (change.assign)();
39    }
40}
41
42/// What the generated property block of an animatable server object needs
43/// from the object that embeds it (the animation members of upstream
44/// `ServerObject`).
45pub trait IServerAnimatedPropertyHost: IServerPropertyHost {
46    /// `SetAnimatedValue(prop, ref field, committedAt, animation)`: starts
47    /// `animation` on the property. `current_value` is the value of the
48    /// field as a variant; the animation writes the field through
49    /// [`CompositionProperty::set_variant`].
50    fn set_animated_value(
51        &self,
52        property: &'static CompositionProperty,
53        current_value: ExpressionVariant,
54        committed_at: Duration,
55        animation: Rc<dyn IAnimationInstance>,
56    );
57
58    /// The tail of `SetAnimatedValue(prop, out field, value)`: the field
59    /// has been assigned directly, so the animation of the property, if
60    /// any, is removed.
61    fn remove_animation_for_property(&self, property: &'static CompositionProperty);
62
63    /// An animation wrote a new value into the field of `property`.
64    fn notify_animated_value_changed(&self, property: &'static CompositionProperty);
65}
66
67/// A reference-typed property value of a server object that may be a
68/// server-side render resource (a brush or a transform): the value as its
69/// media interface, and the render resource behind it when it is one.
70pub struct ServerResourceRef<T: ?Sized> {
71    pub value: Rc<T>,
72    pub resource: Option<Rc<dyn IServerRenderResource>>,
73}
74
75impl<T: ?Sized> Clone for ServerResourceRef<T> {
76    fn clone(&self) -> Self {
77        Self { value: self.value.clone(), resource: self.resource.clone() }
78    }
79}
80
81impl<T: ?Sized> ServerResourceRef<T> {
82    /// A value that is not a server object.
83    pub fn from_value(value: Rc<T>) -> Self {
84        Self { value, resource: None }
85    }
86
87    pub fn ptr_eq(&self, other: &Self) -> bool {
88        std::ptr::addr_eq(Rc::as_ptr(&self.value), Rc::as_ptr(&other.value))
89    }
90}
91
92/// Resolves a resource reference read from a batch: a value is taken as it
93/// is, the id of a server object is looked up and viewed through `cast`.
94pub fn resolve_resource<T: ?Sized + 'static>(
95    resource: Option<BatchResource<T>>,
96    compositor: Option<&Rc<ServerCompositor>>,
97    cast: fn(Rc<dyn IServerObject>) -> Option<Rc<T>>,
98) -> Option<ServerResourceRef<T>> {
99    match resource? {
100        BatchResource::Value(value) => Some(ServerResourceRef::from_value(value)),
101        BatchResource::Server(id) => {
102            let Some(object) = compositor.and_then(|c| c.get_object(id)) else {
103                panic!("a batch refers to server object {id:?}, which does not exist");
104            };
105            let resource = object.clone().as_render_resource();
106            match cast(object) {
107                Some(value) => Some(ServerResourceRef { value, resource }),
108                None => panic!("server object {id:?} is not of the type the property expects"),
109            }
110        }
111    }
112}
113
114/// Reads a resource reference from a batch and resolves it.
115pub fn read_resource<T: ?Sized + crate::rendering::composition::transport::BatchResourceValue>(
116    reader: &mut BatchStreamReader<'_>,
117    compositor: Option<&Rc<ServerCompositor>>,
118    cast: fn(Rc<dyn IServerObject>) -> Option<Rc<T>>,
119) -> Option<ServerResourceRef<T>> {
120    resolve_resource(reader.read_resource::<T>(), compositor, cast)
121}
122
123/// Reads a reference to a server object from a batch and resolves it.
124pub fn read_server_object(
125    reader: &mut BatchStreamReader<'_>,
126    compositor: Option<&Rc<ServerCompositor>>,
127) -> Option<Rc<dyn IServerObject>> {
128    let id = reader.read_server_object()?;
129    match compositor.and_then(|c| c.get_object(id)) {
130        Some(object) => Some(object),
131        None => panic!("a batch refers to server object {id:?}, which does not exist"),
132    }
133}
134
135/// Equality of property values as the generated setters need it.
136///
137/// `differs` is the `!=` of the setter; `default_equals` is the
138/// `EqualityComparer<T>.Default.Equals` of `ServerRenderResourceCore`. They
139/// disagree for NaN.
140pub trait ServerPropertyValue {
141    fn differs(&self, other: &Self) -> bool;
142
143    fn default_equals(&self, other: &Self) -> bool {
144        !self.differs(other)
145    }
146}
147
148macro_rules! impl_server_property_value {
149    ($($ty:ty),* $(,)?) => {$(
150        impl ServerPropertyValue for $ty {
151            #[inline]
152            fn differs(&self, other: &Self) -> bool {
153                self != other
154            }
155        }
156    )*};
157}
158
159impl_server_property_value!(
160    bool,
161    crate::Vector,
162    crate::Vector3D,
163    crate::Size,
164    crate::Matrix,
165    crate::CornerRadius,
166    crate::RelativePoint,
167    crate::RelativeScalar,
168    crate::RelativeRect,
169    crate::numerics::Quaternion,
170    crate::media::Color,
171    crate::media::RenderOptions,
172    crate::media::TextOptions,
173    crate::media::ImmutableExperimentalAcrylicMaterial,
174    crate::media::PenLineCap,
175    crate::media::PenLineJoin,
176    crate::media::AlignmentX,
177    crate::media::AlignmentY,
178    crate::media::Stretch,
179    crate::media::TileMode,
180    crate::rendering::RendererDebugOverlays,
181    crate::rendering::LayoutPassTiming,
182    crate::rendering::composition::CompositionTransparencyLevel,
183);
184
185impl ServerPropertyValue for f32 {
186    fn differs(&self, other: &Self) -> bool {
187        self != other
188    }
189    fn default_equals(&self, other: &Self) -> bool {
190        self == other || (self.is_nan() && other.is_nan())
191    }
192}
193
194impl ServerPropertyValue for f64 {
195    fn differs(&self, other: &Self) -> bool {
196        self != other
197    }
198    fn default_equals(&self, other: &Self) -> bool {
199        self == other || (self.is_nan() && other.is_nan())
200    }
201}
202
203/// References compare by identity.
204impl<T: ?Sized> ServerPropertyValue for Option<Rc<T>> {
205    fn differs(&self, other: &Self) -> bool {
206        match (self, other) {
207            (Some(a), Some(b)) => !std::ptr::addr_eq(Rc::as_ptr(a), Rc::as_ptr(b)),
208            (None, None) => false,
209            _ => true,
210        }
211    }
212}
213
214/// Shared, thread-safe references compare by identity.
215impl<T: ?Sized> ServerPropertyValue for Option<std::sync::Arc<T>> {
216    fn differs(&self, other: &Self) -> bool {
217        match (self, other) {
218            (Some(a), Some(b)) => !std::ptr::addr_eq(std::sync::Arc::as_ptr(a), std::sync::Arc::as_ptr(b)),
219            (None, None) => false,
220            _ => true,
221        }
222    }
223}
224
225impl<T: ?Sized> ServerPropertyValue for Option<ServerResourceRef<T>> {
226    fn differs(&self, other: &Self) -> bool {
227        match (self, other) {
228            (Some(a), Some(b)) => !a.ptr_eq(b),
229            (None, None) => false,
230            _ => true,
231        }
232    }
233}
234
235impl<T: ?Sized> ServerPropertyValue for Option<BatchResource<T>> {
236    fn differs(&self, other: &Self) -> bool {
237        match (self, other) {
238            (Some(BatchResource::Value(a)), Some(BatchResource::Value(b))) => {
239                !std::ptr::addr_eq(Rc::as_ptr(a), Rc::as_ptr(b))
240            }
241            (Some(BatchResource::Server(a)), Some(BatchResource::Server(b))) => a != b,
242            (None, None) => false,
243            _ => true,
244        }
245    }
246}
247
248/// The render resource behind a property value, when it is one.
249pub trait AsServerRenderResource {
250    fn as_server_render_resource(&self) -> Option<Rc<dyn IServerRenderResource>>;
251}
252
253impl AsServerRenderResource for Option<Rc<dyn IServerObject>> {
254    fn as_server_render_resource(&self) -> Option<Rc<dyn IServerRenderResource>> {
255        self.clone().and_then(|object| object.as_render_resource())
256    }
257}
258
259impl<T: ?Sized> AsServerRenderResource for Option<ServerResourceRef<T>> {
260    fn as_server_render_resource(&self) -> Option<Rc<dyn IServerRenderResource>> {
261        self.as_ref().and_then(|value| value.resource.clone())
262    }
263}