ferroui_base/rendering/composition/server/
server_shape_visual.rs1use super::{
2 render_shapes, CompositionProperty, IServerObject, IServerRenderResourceObserver, IServerVisualContent,
3 ServerCompositionShapeCollection, ServerCompositionViewBox, ServerCompositionVisual, ServerSizeDependantVisual,
4 ServerVisualRenderContext, ViewBoxMapping,
5};
6use crate::platform::LtrbRect;
7use crate::rendering::composition::generated::{
8 ServerCompositionContainerVisualProps, ServerShapeVisualHooks, ServerShapeVisualProps,
9};
10use crate::rendering::composition::transport::BatchStreamReader;
11use crate::{Matrix, Rect};
12use std::any::{Any, TypeId};
13use std::rc::Rc;
14use std::time::Duration;
15
16#[derive(Default)]
24pub struct ServerShapeVisual {
25 props: ServerShapeVisualProps,
26}
27
28impl ServerShapeVisual {
29 pub fn new() -> Self {
30 Self::default()
31 }
32
33 pub fn props(&self) -> &ServerShapeVisualProps {
35 &self.props
36 }
37
38 pub fn shapes(&self) -> Vec<Rc<dyn IServerObject>> {
40 self.props
41 .shapes()
42 .as_ref()
43 .and_then(ServerCompositionShapeCollection::of)
44 .map(ServerCompositionShapeCollection::shapes)
45 .unwrap_or_default()
46 }
47
48 pub fn view_box_mapping(&self, size: crate::Vector) -> Option<ViewBoxMapping> {
51 ServerCompositionViewBox::mapping_of(&self.props.view_box()?, size)
52 }
53
54 fn observe(visual: &ServerCompositionVisual, resource: Option<Rc<dyn IServerObject>>, observe: bool) {
56 let Some(resource) = resource.and_then(|resource| resource.as_render_resource()) else { return };
57 let Some(this) = visual.weak().upgrade() else { return };
58 let observer: Rc<dyn IServerRenderResourceObserver> = this;
59 if observe {
60 resource.add_observer(&observer);
61 } else {
62 resource.remove_observer(&observer);
63 }
64 }
65}
66
67impl ServerShapeVisualHooks for ServerCompositionVisual {
68 fn base_deserialize_changes_core(&self, reader: &mut BatchStreamReader<'_>, committed_at: Duration) {
69 ServerCompositionVisual::base_deserialize_changes_core(self, reader, committed_at);
70 }
71
72 fn on_shapes_changing(&self) {
73 if let Some(content) = self.content_as::<ServerShapeVisual>() {
74 ServerShapeVisual::observe(self, content.props.shapes(), false);
75 }
76 }
77
78 fn on_shapes_changed(&self) {
79 if let Some(content) = self.content_as::<ServerShapeVisual>() {
80 ServerShapeVisual::observe(self, content.props.shapes(), true);
81 }
82 self.invalidate_content();
83 }
84
85 fn on_view_box_changing(&self) {
86 if let Some(content) = self.content_as::<ServerShapeVisual>() {
87 ServerShapeVisual::observe(self, content.props.view_box(), false);
88 }
89 }
90
91 fn on_view_box_changed(&self) {
92 if let Some(content) = self.content_as::<ServerShapeVisual>() {
93 ServerShapeVisual::observe(self, content.props.view_box(), true);
94 }
95 self.invalidate_content();
96 }
97}
98
99impl IServerVisualContent for ServerShapeVisual {
100 fn deserialize_changes_core(
101 &self,
102 visual: &ServerCompositionVisual,
103 reader: &mut BatchStreamReader<'_>,
104 committed_at: Duration,
105 ) {
106 self.props.deserialize_changes_core(visual, reader, committed_at);
107 }
108
109 fn compute_own_content_bounds(&self, visual: &ServerCompositionVisual) -> Option<LtrbRect> {
111 ServerSizeDependantVisual::compute_own_content_bounds(visual)
112 }
113
114 fn size_changed(&self, visual: &ServerCompositionVisual) {
115 ServerSizeDependantVisual::size_changed(visual);
116 }
117
118 fn render_core(
125 &self,
126 visual: &ServerCompositionVisual,
127 context: &mut ServerVisualRenderContext<'_>,
128 _current_transformed_clip: LtrbRect,
129 ) {
130 let size = visual.size();
131 if size.x <= 0.0 || size.y <= 0.0 {
132 return;
133 }
134 let shapes = self.shapes();
135 if shapes.is_empty() {
136 return;
137 }
138 let mapping = self.view_box_mapping(size);
139 let canvas = context.canvas();
140 let base = canvas.transform();
141 canvas.push_clip(Rect::new(0.0, 0.0, size.x, size.y));
142 if let Some(mapping) = &mapping {
143 canvas.push_clip(mapping.clip);
144 }
145 let parent = mapping.map_or(Matrix::IDENTITY, |mapping| mapping.transform);
146 render_shapes(canvas, &shapes, parent, base);
147 canvas.set_transform(base);
148 if mapping.is_some() {
149 canvas.pop_clip();
150 }
151 canvas.pop_clip();
152 }
153
154 fn dependency_queued_invalidate(&self, visual: &ServerCompositionVisual) {
156 visual.invalidate_content();
157 }
158
159 fn dispose(&self, visual: &ServerCompositionVisual) {
160 ServerShapeVisual::observe(visual, self.props.shapes(), false);
161 ServerShapeVisual::observe(visual, self.props.view_box(), false);
162 }
163
164 fn find_props(&self, type_id: TypeId) -> Option<&dyn Any> {
165 self.props.find_props(type_id)
166 }
167
168 fn get_composition_property(&self, name: &str) -> Option<&'static CompositionProperty> {
169 ServerCompositionContainerVisualProps::get_composition_property(name)
170 }
171
172 fn as_any(&self) -> &dyn Any {
173 self
174 }
175}