Skip to main content

ferroui_base/rendering/composition/drawing/
server_composition_simple_pen.rs

1use crate::media::immutable::{ImmutableDashStyle, ImmutablePen, ImmutableSolidColorBrush};
2use crate::media::{IBrush, IDashStyle, IImmutableBrush, IPen, PenLineCap, PenLineJoin};
3use crate::rendering::composition::generated::{ServerCompositionSimplePenHooks, ServerCompositionSimplePenProps};
4use crate::rendering::composition::server::{
5    impl_simple_server_render_resource, IServerObject, IServerRenderResource, ServerCompositor, ServerResourceRef,
6    SimpleServerRenderResource,
7};
8use crate::rendering::composition::transport::BatchStreamReader;
9use std::any::{Any, TypeId};
10use std::rc::Rc;
11use std::time::Duration;
12
13/// The server-side counterpart of a mutable pen.
14pub struct ServerCompositionSimplePen {
15    base: SimpleServerRenderResource,
16    props: ServerCompositionSimplePenProps,
17}
18
19impl ServerCompositionSimplePen {
20    pub fn new(compositor: &Rc<ServerCompositor>) -> Rc<ServerCompositionSimplePen> {
21        Rc::new_cyclic(|this| {
22            let this: std::rc::Weak<ServerCompositionSimplePen> = this.clone();
23            ServerCompositionSimplePen {
24                base: SimpleServerRenderResource::new(compositor, this),
25                props: ServerCompositionSimplePenProps::new(),
26            }
27        })
28    }
29
30    pub fn is_disposed(&self) -> bool {
31        self.base.is_disposed()
32    }
33
34    /// The properties of the pen.
35    pub fn props(&self) -> &ServerCompositionSimplePenProps {
36        &self.props
37    }
38
39    pub fn set_brush(&self, value: Option<ServerResourceRef<dyn IBrush>>) {
40        self.props.set_brush(self, value)
41    }
42
43    pub fn set_dash_style(&self, value: Option<std::sync::Arc<ImmutableDashStyle>>) {
44        self.props.set_dash_style(self, value)
45    }
46
47    pub fn set_line_cap(&self, value: PenLineCap) {
48        self.props.set_line_cap(self, value)
49    }
50
51    pub fn set_line_join(&self, value: PenLineJoin) {
52        self.props.set_line_join(self, value)
53    }
54
55    pub fn set_miter_limit(&self, value: f64) {
56        self.props.set_miter_limit(self, value)
57    }
58
59    pub fn set_thickness(&self, value: f64) {
60        self.props.set_thickness(self, value)
61    }
62}
63
64impl_simple_server_render_resource!(ServerCompositionSimplePen, base);
65
66impl ServerCompositionSimplePenHooks for ServerCompositionSimplePen {}
67
68impl IServerObject for ServerCompositionSimplePen {
69    fn deserialize_changes_core(&self, reader: &mut BatchStreamReader<'_>, committed_at: Duration) {
70        self.props.deserialize_changes_core(self, reader, committed_at);
71    }
72
73    fn values_invalidated(&self) {
74        self.base.core().invalidated(self);
75    }
76
77    fn dispose(&self) {
78        // Remove the pen from the brush observers.
79        // Without this, the pen was being retained in memory by long lived brush resources (e.g. those defined in
80        // the theme or app resources), hence was causing memory leaks.
81        let brush = self.props.brush();
82        self.base.core().remove_observers_from_property(self, brush.as_ref().and_then(|b| b.resource.as_ref()));
83        ServerCompositionSimplePenProps::id_of_brush_property().set_field(self, None);
84        self.base.core().dispose();
85    }
86
87    fn as_render_resource(self: Rc<Self>) -> Option<Rc<dyn IServerRenderResource>> {
88        Some(self)
89    }
90
91    fn as_pen(self: Rc<Self>) -> Option<Rc<dyn IPen>> {
92        Some(self)
93    }
94
95    fn get_props(&self, type_id: TypeId) -> Option<&dyn Any> {
96        self.props.find_props(type_id)
97    }
98
99    fn as_any(&self) -> &dyn Any {
100        self
101    }
102
103    fn into_any_rc(self: Rc<Self>) -> Rc<dyn Any> {
104        self
105    }
106}
107
108impl IPen for ServerCompositionSimplePen {
109    fn brush(&self) -> Option<Rc<dyn IBrush>> {
110        self.props.brush().map(|brush| brush.value)
111    }
112
113    fn dash_style(&self) -> Option<Rc<dyn IDashStyle>> {
114        // The contract hands out a handle of this thread to the shared value.
115        self.props.dash_style().map(|style| Rc::new((*style).clone()) as Rc<dyn IDashStyle>)
116    }
117
118    fn line_cap(&self) -> PenLineCap {
119        self.props.line_cap()
120    }
121
122    fn line_join(&self) -> PenLineJoin {
123        self.props.line_join()
124    }
125
126    fn miter_limit(&self) -> f64 {
127        self.props.miter_limit()
128    }
129
130    fn thickness(&self) -> f64 {
131        self.props.thickness()
132    }
133
134    fn as_any(&self) -> &dyn Any {
135        self
136    }
137
138    fn into_immutable_pen(self: Rc<Self>) -> Rc<ImmutablePen> {
139        // A snapshot of the brush: an immutable brush is taken as it is, a
140        // server-side solid color brush by value. Other server-side brushes
141        // have no immutable form here.
142        let brush: Option<Rc<dyn IImmutableBrush>> = IPen::brush(&*self).and_then(|brush| {
143            if let Some(solid) = brush.as_solid_color_brush() {
144                if brush.as_any().is::<ImmutableSolidColorBrush>() {
145                    return brush.clone().into_immutable_brush();
146                }
147                let snapshot: Rc<dyn IImmutableBrush> = Rc::new(ImmutableSolidColorBrush::from_brush(solid));
148                return Some(snapshot);
149            }
150            brush.into_immutable_brush()
151        });
152        Rc::new(ImmutablePen::new(
153            brush,
154            self.props.thickness(),
155            self.props.dash_style().map(|style| Rc::new((*style).clone())),
156            self.props.line_cap(),
157            self.props.line_join(),
158            self.props.miter_limit(),
159        ))
160    }
161}