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