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        let Some(bmp) = surface.bitmap() else { return };
93
94        //TODO: add a way to always render the whole bitmap instead of just assuming 96 DPI
95        let size = visual.size();
96        context.canvas().draw_bitmap(
97            &*bmp,
98            1.0,
99            Rect::from_size(bmp.pixel_size().to_size(1.0)),
100            Rect::from_size(Size::new(size.x, size.y)),
101        );
102    }
103
104    fn on_attached_to_root(&self, visual: &ServerCompositionVisual, _target: &Rc<ServerCompositionTarget>) {
105        self.subscribe(visual);
106    }
107
108    fn on_detached_from_root(&self, visual: &ServerCompositionVisual, _target: &Rc<ServerCompositionTarget>) {
109        self.unsubscribe(visual);
110    }
111
112    fn dispose(&self, visual: &ServerCompositionVisual) {
113        self.unsubscribe(visual);
114    }
115
116    fn find_props(&self, type_id: TypeId) -> Option<&dyn Any> {
117        self.props.find_props(type_id)
118    }
119
120    fn as_any(&self) -> &dyn Any {
121        self
122    }
123}