Skip to main content

ferroui_base/rendering/composition/server/
server_composition_surface_visual.rs

1use super::{IServerCompositionSurface, IServerVisualContent, ServerCompositionTarget, ServerCompositionVisual, ServerSizeDependantVisual, ServerVisualRenderContext};
2use crate::platform::LtrbRect;
3use crate::rendering::composition::generated::{
4    ServerCompositionSurfaceVisualHooks, ServerCompositionSurfaceVisualProps,
5};
6use crate::rendering::composition::transport::BatchStreamReader;
7use crate::{Rect, Size};
8use std::any::{Any, TypeId};
9use std::rc::Rc;
10use std::time::Duration;
11
12/// Server-side counterpart of `CompositionSurfaceVisual`.
13#[derive(Default)]
14pub struct ServerCompositionSurfaceVisual {
15    props: ServerCompositionSurfaceVisualProps,
16}
17
18impl ServerCompositionSurfaceVisual {
19    pub fn new() -> Self {
20        Self::default()
21    }
22
23    pub fn surface(&self) -> Option<Rc<dyn IServerCompositionSurface>> {
24        self.props.surface().and_then(|surface| surface.as_surface())
25    }
26
27    fn subscribe(&self, visual: &ServerCompositionVisual) {
28        if let Some(surface) = self.surface() {
29            let weak = visual.weak();
30            surface.changed().add(
31                weak.as_ptr() as usize,
32                Rc::new(move || {
33                    // OnSurfaceInvalidated
34                    if let Some(visual) = weak.upgrade() {
35                        visual.invalidate_content();
36                    }
37                }),
38            );
39        }
40    }
41
42    fn unsubscribe(&self, visual: &ServerCompositionVisual) {
43        if let Some(surface) = self.surface() {
44            surface.changed().remove(visual.weak().as_ptr() as usize);
45        }
46    }
47}
48
49impl ServerCompositionSurfaceVisualHooks for ServerCompositionVisual {
50    fn base_deserialize_changes_core(&self, reader: &mut BatchStreamReader<'_>, committed_at: Duration) {
51        ServerCompositionVisual::base_deserialize_changes_core(self, reader, committed_at);
52    }
53
54    fn on_surface_changed(&self) {
55        if let Some(content) = self.content_as::<ServerCompositionSurfaceVisual>() {
56            content.subscribe(self);
57        }
58    }
59
60    fn on_surface_changing(&self) {
61        if let Some(content) = self.content_as::<ServerCompositionSurfaceVisual>() {
62            content.unsubscribe(self);
63        }
64    }
65}
66
67impl IServerVisualContent for ServerCompositionSurfaceVisual {
68    fn deserialize_changes_core(
69        &self,
70        visual: &ServerCompositionVisual,
71        reader: &mut BatchStreamReader<'_>,
72        committed_at: Duration,
73    ) {
74        self.props.deserialize_changes_core(visual, reader, committed_at);
75    }
76
77    fn compute_own_content_bounds(&self, visual: &ServerCompositionVisual) -> Option<LtrbRect> {
78        ServerSizeDependantVisual::compute_own_content_bounds(visual)
79    }
80
81    fn size_changed(&self, visual: &ServerCompositionVisual) {
82        ServerSizeDependantVisual::size_changed(visual);
83    }
84
85    fn render_core(
86        &self,
87        visual: &ServerCompositionVisual,
88        context: &mut ServerVisualRenderContext<'_>,
89        _current_transformed_clip: LtrbRect,
90    ) {
91        let Some(surface) = self.surface() else { return };
92
93        //TODO: add a way to always render the whole bitmap instead of just assuming 96 DPI
94        // The WinUI port draws through the surface, so a surface visual also
95        // shows the surfaces whose content is not a bitmap (a visual
96        // surface); for a bitmap surface the default draw is the one above.
97        let size = visual.size();
98        surface.draw(context.canvas(), Rect::from_size(Size::new(size.x, size.y)), 1.0);
99    }
100
101    fn on_attached_to_root(&self, visual: &ServerCompositionVisual, _target: &Rc<ServerCompositionTarget>) {
102        self.subscribe(visual);
103    }
104
105    fn on_detached_from_root(&self, visual: &ServerCompositionVisual, _target: &Rc<ServerCompositionTarget>) {
106        self.unsubscribe(visual);
107    }
108
109    fn dispose(&self, visual: &ServerCompositionVisual) {
110        self.unsubscribe(visual);
111    }
112
113    fn find_props(&self, type_id: TypeId) -> Option<&dyn Any> {
114        self.props.find_props(type_id)
115    }
116
117    fn as_any(&self) -> &dyn Any {
118        self
119    }
120}