Skip to main content

ferroui_base/rendering/composition/brushes/
server_composition_brush.rs

1//! The server-side counterparts of the composition brushes.
2
3use crate::media::{
4    Color, GradientSpreadMethod, IBrush, IConicGradientBrush, IGradientBrush, IGradientStop, ILinearGradientBrush,
5    IRadialGradientBrush, ISolidColorBrush, ITransform,
6};
7use crate::rendering::composition::generated::{
8    ServerCompositionBrushHooks, ServerCompositionBrushProps,
9    ServerCompositionConicGradientBrushHooks, ServerCompositionConicGradientBrushProps,
10    ServerCompositionLinearGradientBrushHooks, ServerCompositionLinearGradientBrushProps,
11    ServerCompositionRadialGradientBrushHooks, ServerCompositionRadialGradientBrushProps,
12    ServerCompositionSolidColorBrushHooks, ServerCompositionSolidColorBrushProps,
13};
14use crate::rendering::composition::server::{
15    impl_server_render_resource, props_of, resolve_resource, CompositionProperty, CompositionPropertyOf,
16    IAnimatedServerObject, IServerObject, IServerRenderResource, IServerRenderResourceHost, ServerCompositor,
17    ServerRenderResource, ServerRenderResourceCore, ServerResourceRef,
18};
19use super::composition_gradient_winui::{with_winui_anchor, with_winui_transform, IServerBrushWinUiTransform};
20use crate::rendering::composition::server::composition_transform;
21use crate::rendering::composition::transport::BatchStreamReader;
22use crate::{Matrix, RelativePoint, RelativeScalar, Vector};
23use std::any::{Any, TypeId};
24use std::cell::{Cell, RefCell};
25use std::rc::{Rc, Weak};
26use std::sync::OnceLock;
27use std::time::Duration;
28
29/// The part of the server-side gradient brushes that upstream's
30/// hand-written `ServerCompositionGradientBrush` adds to the generated
31/// `ServerCompositionBrush`: the brush properties, the gradient stops and
32/// the spread method.
33pub struct ServerCompositionGradientBrush {
34    brush: ServerCompositionBrushProps,
35    gradient_stops: RefCell<Vec<ServerResourceRef<dyn IGradientStop>>>,
36    spread_method: Cell<GradientSpreadMethod>,
37}
38
39impl Default for ServerCompositionGradientBrush {
40    fn default() -> Self {
41        Self::new()
42    }
43}
44
45impl ServerCompositionGradientBrush {
46    pub fn new() -> Self {
47        Self {
48            brush: ServerCompositionBrushProps::new(),
49            gradient_stops: RefCell::new(Vec::new()),
50            spread_method: Cell::new(GradientSpreadMethod::default()),
51        }
52    }
53
54    /// The properties of the generated base class.
55    pub fn brush(&self) -> &ServerCompositionBrushProps {
56        &self.brush
57    }
58
59    /// This part or the block of its base class, by type.
60    pub fn find_props(&self, type_id: TypeId) -> Option<&dyn Any> {
61        if type_id == TypeId::of::<Self>() {
62            return Some(self);
63        }
64        self.brush.find_props(type_id)
65    }
66
67    /// `s_IdOfGradientStopsProperty`.
68    pub fn id_of_gradient_stops_property() -> &'static CompositionPropertyOf<Vec<Rc<dyn IGradientStop>>> {
69        static PROPERTY: OnceLock<CompositionPropertyOf<Vec<Rc<dyn IGradientStop>>>> = OnceLock::new();
70        PROPERTY.get_or_init(|| {
71            CompositionProperty::register::<ServerCompositionGradientBrush, Vec<Rc<dyn IGradientStop>>>(
72                "GradientStops",
73                "ServerCompositionGradientBrush",
74                |obj| props_of::<ServerCompositionGradientBrush>(obj).gradient_stops(),
75                |obj, v| {
76                    *props_of::<ServerCompositionGradientBrush>(obj).gradient_stops.borrow_mut() =
77                        v.into_iter().map(ServerResourceRef::from_value).collect()
78                },
79                None,
80                None,
81            )
82        })
83    }
84
85    pub fn gradient_stops(&self) -> Vec<Rc<dyn IGradientStop>> {
86        self.gradient_stops.borrow().iter().map(|stop| stop.value.clone()).collect()
87    }
88
89    /// Sets the gradient stops: the render resources among them are
90    /// observed by the brush (`SetValue` of the render resource).
91    pub fn set_gradient_stops(
92        &self,
93        host: &dyn IServerRenderResourceHost,
94        core: &ServerRenderResourceCore,
95        value: Vec<ServerResourceRef<dyn IGradientStop>>,
96    ) {
97        // Lists compare by reference, and every list read from a batch is
98        // a new one.
99        let old_children = self.gradient_stops.borrow().iter().filter_map(|stop| stop.resource.clone()).collect();
100        let new_children = value.iter().filter_map(|stop| stop.resource.clone()).collect();
101        let mut value = Some(value);
102        core.set_list_value(host, old_children, new_children, &mut || {
103            if let Some(value) = value.take() {
104                *self.gradient_stops.borrow_mut() = value;
105            }
106        });
107    }
108
109    pub fn spread_method(&self) -> GradientSpreadMethod {
110        self.spread_method.get()
111    }
112
113    /// `DeserializeChangesCore`: the generated base class, then the spread
114    /// method and the stops. `host` and `resource` are the brush.
115    pub fn deserialize_changes_core(
116        &self,
117        host: &dyn ServerCompositionBrushHooks,
118        resource: &dyn IServerRenderResourceHost,
119        core: &ServerRenderResourceCore,
120        reader: &mut BatchStreamReader<'_>,
121        committed_at: Duration,
122    ) {
123        self.brush.deserialize_changes_core(host, reader, committed_at);
124        self.spread_method.set(reader.read::<GradientSpreadMethod>());
125        let compositor = host.server_compositor();
126        let count = reader.read::<i32>();
127        let mut stops = Vec::with_capacity(count.max(0) as usize);
128        for _ in 0..count {
129            let read = resolve_resource(reader.read_resource::<dyn IGradientStop>(), compositor.as_ref(), |o| {
130                o.as_gradient_stop()
131            });
132            match read {
133                Some(read) => stops.push(read),
134                None => panic!("a batch lists a null gradient stop"),
135            }
136        }
137        self.set_gradient_stops(resource, core, stops);
138    }
139}
140
141/// Declares a server-side composition brush class: the struct, its
142/// constructor, the render resource contracts, `IServerObject` and `IBrush`.
143macro_rules! server_composition_brush {
144    (
145        $(#[$meta:meta])*
146        $name:ident { $($field:ident : $field_ty:ty = $field_init:expr),* $(,)? }
147        brush: |$b:ident| $brush:expr;
148        deserialize: |$d:ident, $reader:ident, $committed_at:ident| $deserialize:expr;
149        find: |$f:ident, $type_id:ident| $find:expr;
150        composition_property: $get:expr;
151        as: { $($as_fn:ident -> $as_trait:path),* $(,)? }
152    ) => {
153        $(#[$meta])*
154        pub struct $name {
155            base: ServerRenderResource,
156            $($field: $field_ty,)*
157        }
158
159        impl $name {
160            /// Creates the brush, active: the UI-thread brush activates its
161            /// server object when it is created (`InitializeDefaultsExtra`),
162            /// which is here done when the server object is created for it.
163            pub fn new(compositor: &Rc<ServerCompositor>) -> Rc<$name> {
164                let brush = Rc::new_cyclic(|this: &Weak<$name>| $name {
165                    base: ServerRenderResource::new(
166                        compositor,
167                        this.clone() as Weak<dyn IAnimatedServerObject>,
168                        this.clone() as Weak<dyn IServerRenderResource>,
169                    ),
170                    $($field: $field_init,)*
171                });
172                brush.base.object().activate();
173                brush
174            }
175
176            pub fn is_disposed(&self) -> bool {
177                self.base.is_disposed()
178            }
179
180            /// The properties of the generated `ServerCompositionBrush`.
181            pub fn brush_props(&self) -> &ServerCompositionBrushProps {
182                let $b = self;
183                $brush
184            }
185        }
186
187        impl_server_render_resource!($name, base, composition_property: $get);
188
189        impl ServerCompositionBrushHooks for $name {}
190
191        impl IServerObject for $name {
192            fn deserialize_changes_core(&self, $reader: &mut BatchStreamReader<'_>, $committed_at: Duration) {
193                let $d = self;
194                $deserialize
195            }
196
197            fn values_invalidated(&self) {
198                self.base.core().invalidated(self);
199            }
200
201            fn dispose(&self) {
202                self.base.core().dispose();
203            }
204
205            fn as_render_resource(self: Rc<Self>) -> Option<Rc<dyn IServerRenderResource>> {
206                Some(self)
207            }
208
209            fn as_brush(self: Rc<Self>) -> Option<Rc<dyn IBrush>> {
210                Some(self)
211            }
212
213            fn as_animated(self: Rc<Self>) -> Option<Rc<dyn IAnimatedServerObject>> {
214                Some(self)
215            }
216
217            fn get_props(&self, $type_id: TypeId) -> Option<&dyn Any> {
218                let $f = self;
219                $find
220            }
221
222            fn as_any(&self) -> &dyn Any {
223                self
224            }
225
226            fn into_any_rc(self: Rc<Self>) -> Rc<dyn Any> {
227                self
228            }
229        }
230
231        impl IBrush for $name {
232            fn opacity(&self) -> f64 {
233                self.brush_props().opacity()
234            }
235
236            /// The upstream transform with the WinUI brush transform (not
237            /// from upstream).
238            fn transform(&self) -> Option<Rc<dyn ITransform>> {
239                with_winui_transform(
240                    self.brush_props().transform().map(|transform| transform.value),
241                    IServerBrushWinUiTransform::winui_transform(self),
242                )
243            }
244
245            fn transform_origin(&self) -> RelativePoint {
246                self.brush_props().transform_origin()
247            }
248
249            /// The upstream relative transform with the WinUI anchor point
250            /// (not from upstream).
251            fn relative_transform(&self) -> Option<Rc<dyn ITransform>> {
252                with_winui_anchor(
253                    self.brush_props().relative_transform().map(|transform| transform.value),
254                    IServerBrushWinUiTransform::winui_anchor_point(self),
255                )
256            }
257
258            fn as_any(&self) -> &dyn Any {
259                self
260            }
261
262            $(
263                fn $as_fn(&self) -> Option<&dyn $as_trait> {
264                    Some(self)
265                }
266            )*
267        }
268    };
269}
270
271server_composition_brush! {
272    /// The server-side counterpart of a
273    /// [`CompositionSolidColorBrush`](super::CompositionSolidColorBrush).
274    ServerCompositionSolidColorBrush {
275        props: ServerCompositionSolidColorBrushProps = ServerCompositionSolidColorBrushProps::new(),
276    }
277    brush: |b| b.props.base();
278    deserialize: |d, reader, committed_at| d.props.deserialize_changes_core(d, reader, committed_at);
279    find: |f, type_id| f.props.find_props(type_id);
280    composition_property: ServerCompositionSolidColorBrushProps::get_composition_property;
281    as: { as_solid_color_brush -> ISolidColorBrush }
282}
283
284impl ServerCompositionSolidColorBrushHooks for ServerCompositionSolidColorBrush {}
285
286/// A colour brush has no WinUI brush transform.
287impl IServerBrushWinUiTransform for ServerCompositionSolidColorBrush {}
288
289impl ServerCompositionSolidColorBrush {
290    /// The properties of the brush.
291    pub fn props(&self) -> &ServerCompositionSolidColorBrushProps {
292        &self.props
293    }
294}
295
296impl ISolidColorBrush for ServerCompositionSolidColorBrush {
297    fn color(&self) -> Color {
298        self.props.color()
299    }
300}
301
302/// Declares a server-side gradient brush class on top of
303/// [`server_composition_brush!`].
304macro_rules! server_composition_gradient_brush {
305    ($(#[$meta:meta])* $name:ident, $props:ident, $hooks:ident, $as_fn:ident -> $as_trait:path) => {
306        server_composition_brush! {
307            $(#[$meta])*
308            $name {
309                gradient: ServerCompositionGradientBrush = ServerCompositionGradientBrush::new(),
310                props: $props = $props::new(),
311            }
312            brush: |b| b.gradient.brush();
313            deserialize: |d, reader, committed_at| d.props.deserialize_changes_core(d, reader, committed_at);
314            find: |f, type_id| f.props.find_props(type_id).or_else(|| f.gradient.find_props(type_id));
315            composition_property: |name: &str| {
316                $props::get_composition_property(name).or_else(|| ServerCompositionBrushProps::get_composition_property(name))
317            };
318            as: { as_gradient_brush -> IGradientBrush, $as_fn -> $as_trait }
319        }
320
321        impl $hooks for $name {
322            fn base_deserialize_changes_core(&self, reader: &mut BatchStreamReader<'_>, committed_at: Duration) {
323                self.gradient.deserialize_changes_core(self, self, self.base.core(), reader, committed_at);
324            }
325        }
326
327        impl $name {
328            /// The properties of the brush.
329            pub fn props(&self) -> &$props {
330                &self.props
331            }
332
333            /// The gradient part of the brush.
334            pub fn gradient(&self) -> &ServerCompositionGradientBrush {
335                &self.gradient
336            }
337
338            pub fn gradient_stops(&self) -> Vec<Rc<dyn IGradientStop>> {
339                self.gradient.gradient_stops()
340            }
341
342            pub fn spread_method(&self) -> GradientSpreadMethod {
343                self.gradient.spread_method()
344            }
345        }
346
347        /// The WinUI brush transform of the gradient (not from upstream).
348        impl IServerBrushWinUiTransform for $name {
349            fn winui_transform(&self) -> Option<Matrix> {
350                composition_transform(
351                    Vector::default(),
352                    Vector::default(),
353                    self.props.center_point(),
354                    self.props.transform_matrix(),
355                    self.props.scale(),
356                    self.props.rotation_angle(),
357                    self.props.offset(),
358                )
359            }
360
361            fn winui_anchor_point(&self) -> Vector {
362                self.props.anchor_point()
363            }
364        }
365
366        impl IGradientBrush for $name {
367            fn gradient_stops(&self) -> Vec<Rc<dyn IGradientStop>> {
368                self.gradient.gradient_stops()
369            }
370
371            fn spread_method(&self) -> GradientSpreadMethod {
372                self.gradient.spread_method()
373            }
374        }
375    };
376}
377
378server_composition_gradient_brush!(
379    /// The server-side counterpart of a
380    /// [`CompositionConicGradientBrush`](super::CompositionConicGradientBrush).
381    ServerCompositionConicGradientBrush,
382    ServerCompositionConicGradientBrushProps,
383    ServerCompositionConicGradientBrushHooks,
384    as_conic_gradient_brush -> IConicGradientBrush
385);
386
387impl IConicGradientBrush for ServerCompositionConicGradientBrush {
388    fn center(&self) -> RelativePoint {
389        self.props.center()
390    }
391
392    fn angle(&self) -> f64 {
393        self.props.angle()
394    }
395}
396
397server_composition_gradient_brush!(
398    /// The server-side counterpart of a
399    /// [`CompositionLinearGradientBrush`](super::CompositionLinearGradientBrush).
400    ServerCompositionLinearGradientBrush,
401    ServerCompositionLinearGradientBrushProps,
402    ServerCompositionLinearGradientBrushHooks,
403    as_linear_gradient_brush -> ILinearGradientBrush
404);
405
406impl ILinearGradientBrush for ServerCompositionLinearGradientBrush {
407    fn start_point(&self) -> RelativePoint {
408        self.props.start_point()
409    }
410
411    fn end_point(&self) -> RelativePoint {
412        self.props.end_point()
413    }
414}
415
416server_composition_gradient_brush!(
417    /// The server-side counterpart of a
418    /// [`CompositionRadialGradientBrush`](super::CompositionRadialGradientBrush).
419    ServerCompositionRadialGradientBrush,
420    ServerCompositionRadialGradientBrushProps,
421    ServerCompositionRadialGradientBrushHooks,
422    as_radial_gradient_brush -> IRadialGradientBrush
423);
424
425impl ServerCompositionRadialGradientBrush {
426    pub fn radius(&self) -> f64 {
427        self.props.radius_x().scalar
428    }
429}
430
431impl IRadialGradientBrush for ServerCompositionRadialGradientBrush {
432    fn center(&self) -> RelativePoint {
433        self.props.center()
434    }
435
436    fn gradient_origin(&self) -> RelativePoint {
437        self.props.gradient_origin()
438    }
439
440    fn radius_x(&self) -> RelativeScalar {
441        self.props.radius_x()
442    }
443
444    fn radius_y(&self) -> RelativeScalar {
445        self.props.radius_y()
446    }
447}
448