ferroui_base/rendering/composition/server/
server_sprite_visual.rs1use super::{
2 paint_composition_brush, CompositionProperty, IServerObject, IServerRenderResourceObserver, IServerVisualContent,
3 ServerCompositionVisual, ServerDropShadow, ServerSizeDependantVisual,
4 ServerVisualRenderContext,
5};
6use crate::platform::LtrbRect;
7use crate::rendering::composition::generated::{
8 ServerCompositionContainerVisualProps, ServerSpriteVisualHooks, ServerSpriteVisualProps,
9};
10use crate::rendering::composition::transport::BatchStreamReader;
11use crate::Rect;
12use std::any::{Any, TypeId};
13use std::rc::Rc;
14use std::time::Duration;
15
16#[derive(Default)]
26pub struct ServerSpriteVisual {
27 props: ServerSpriteVisualProps,
28}
29
30impl ServerSpriteVisual {
31 pub fn new() -> Self {
32 Self::default()
33 }
34
35 pub fn props(&self) -> &ServerSpriteVisualProps {
37 &self.props
38 }
39
40 pub fn brush(&self) -> Option<Rc<dyn IServerObject>> {
42 self.props.brush()
43 }
44
45 pub fn shadow(&self) -> Option<Rc<ServerDropShadow>> {
47 self.props.shadow().and_then(|shadow| shadow.into_any_rc().downcast::<ServerDropShadow>().ok())
48 }
49
50 fn observe(visual: &ServerCompositionVisual, resource: Option<Rc<dyn IServerObject>>, observe: bool) {
52 let Some(resource) = resource.and_then(|resource| resource.as_render_resource()) else { return };
53 let Some(this) = visual.weak().upgrade() else { return };
54 let observer: Rc<dyn IServerRenderResourceObserver> = this;
55 if observe {
56 resource.add_observer(&observer);
57 } else {
58 resource.remove_observer(&observer);
59 }
60 }
61
62 fn rect(visual: &ServerCompositionVisual) -> Rect {
63 let size = visual.size();
64 Rect::new(0.0, 0.0, size.x, size.y)
65 }
66}
67
68impl ServerSpriteVisualHooks for ServerCompositionVisual {
69 fn base_deserialize_changes_core(&self, reader: &mut BatchStreamReader<'_>, committed_at: Duration) {
70 ServerCompositionVisual::base_deserialize_changes_core(self, reader, committed_at);
71 }
72
73 fn on_brush_changing(&self) {
74 if let Some(content) = self.content_as::<ServerSpriteVisual>() {
75 ServerSpriteVisual::observe(self, content.brush(), false);
76 }
77 }
78
79 fn on_brush_changed(&self) {
80 if let Some(content) = self.content_as::<ServerSpriteVisual>() {
81 ServerSpriteVisual::observe(self, content.brush(), true);
82 }
83 self.invalidate_content();
84 }
85
86 fn on_shadow_changing(&self) {
87 if let Some(content) = self.content_as::<ServerSpriteVisual>() {
88 ServerSpriteVisual::observe(self, content.props.shadow(), false);
89 }
90 }
91
92 fn on_shadow_changed(&self) {
93 if let Some(content) = self.content_as::<ServerSpriteVisual>() {
94 ServerSpriteVisual::observe(self, content.props.shadow(), true);
95 }
96 self.invalidate_content();
97 }
98}
99
100impl IServerVisualContent for ServerSpriteVisual {
101 fn deserialize_changes_core(
102 &self,
103 visual: &ServerCompositionVisual,
104 reader: &mut BatchStreamReader<'_>,
105 committed_at: Duration,
106 ) {
107 self.props.deserialize_changes_core(visual, reader, committed_at);
108 }
109
110 fn compute_own_content_bounds(&self, visual: &ServerCompositionVisual) -> Option<LtrbRect> {
112 let bounds = ServerSizeDependantVisual::compute_own_content_bounds(visual)?;
113 match self.shadow() {
114 Some(shadow) => Some(LtrbRect::from_rect(shadow.bounds_in(Some(visual), bounds.to_rect()))),
115 None => Some(bounds),
116 }
117 }
118
119 fn size_changed(&self, visual: &ServerCompositionVisual) {
120 ServerSizeDependantVisual::size_changed(visual);
121 }
122
123 fn render_core(
124 &self,
125 visual: &ServerCompositionVisual,
126 context: &mut ServerVisualRenderContext<'_>,
127 _current_transformed_clip: LtrbRect,
128 ) {
129 let rect = Self::rect(visual);
130 if rect.width <= 0.0 || rect.height <= 0.0 {
131 return;
132 }
133 let brush = self.brush();
134 if let Some(shadow) = self.shadow().filter(|shadow| shadow.is_visible()) {
135 if shadow.inherits_from_visual_content() {
136 if let Some(brush) = &brush {
139 let drawn = shadow.render_shadow_of(context.canvas(), Some(visual), rect, |canvas| {
140 paint_composition_brush(canvas, brush, rect)
141 });
142 if !drawn {
143 shadow.render_under_rect_in(context.canvas(), Some(visual), rect);
144 }
145 }
146 } else {
147 shadow.render_under_rect_in(context.canvas(), Some(visual), rect);
149 }
150 }
151 if let Some(brush) = brush {
152 paint_composition_brush(context.canvas(), &brush, rect);
153 }
154 }
155
156 fn dependency_queued_invalidate(&self, visual: &ServerCompositionVisual) {
158 visual.invalidate_content();
159 }
160
161 fn dispose(&self, visual: &ServerCompositionVisual) {
162 ServerSpriteVisual::observe(visual, self.brush(), false);
163 ServerSpriteVisual::observe(visual, self.props.shadow(), false);
164 }
165
166 fn find_props(&self, type_id: TypeId) -> Option<&dyn Any> {
167 self.props.find_props(type_id)
168 }
169
170 fn get_composition_property(&self, name: &str) -> Option<&'static CompositionProperty> {
171 ServerCompositionContainerVisualProps::get_composition_property(name)
172 }
173
174 fn as_any(&self) -> &dyn Any {
175 self
176 }
177}