Skip to main content

ferroui_base/rendering/composition/server/
server_composition_view_box.rs

1//! The server-side counterpart of the WinUI `CompositionViewBox`, and the
2//! mapping of a view box onto the area of a visual.
3
4use super::{
5    impl_server_render_resource, surface_brush_content_rect, IAnimatedServerObject, IServerObject,
6    IServerRenderResource, ServerCompositor, ServerRenderResource,
7};
8use crate::rendering::composition::generated::{ServerCompositionViewBoxHooks, ServerCompositionViewBoxProps};
9use crate::rendering::composition::transport::BatchStreamReader;
10use crate::rendering::composition::CompositionStretch;
11use crate::{Matrix, Rect, Size, Vector};
12use std::any::{Any, TypeId};
13use std::rc::{Rc, Weak};
14use std::time::Duration;
15
16/// How a view box maps shape coordinates onto the area of a visual.
17#[derive(Clone, Copy, Debug, PartialEq)]
18pub struct ViewBoxMapping {
19    /// The transform from shape coordinates to the coordinates of the
20    /// visual.
21    pub transform: Matrix,
22    /// The mapped view box, in the coordinates of the visual, limited to the
23    /// area: what stays visible.
24    pub clip: Rect,
25}
26
27/// Maps the view box `(offset, size)` onto `area`: the view box is sized by
28/// `stretch` and placed by the alignment ratios, as a surface brush lays
29/// out its surface (see [`surface_brush_content_rect`]). `None` when the
30/// view box or the area is empty.
31pub fn view_box_mapping(
32    offset: Vector,
33    size: Vector,
34    area: Rect,
35    stretch: CompositionStretch,
36    horizontal_ratio: f64,
37    vertical_ratio: f64,
38) -> Option<ViewBoxMapping> {
39    let content = surface_brush_content_rect(Size::new(size.x, size.y), area, stretch, horizontal_ratio, vertical_ratio)?;
40    let transform = Matrix::create_translation(-offset.x, -offset.y)
41        * Matrix::create_scale(content.width / size.x, content.height / size.y)
42        * Matrix::create_translation(content.x, content.y);
43    Some(ViewBoxMapping { transform, clip: content.intersect(area) })
44}
45
46/// The server-side counterpart of a
47/// [`CompositionViewBox`](crate::rendering::composition::CompositionViewBox).
48pub struct ServerCompositionViewBox {
49    base: ServerRenderResource,
50    props: ServerCompositionViewBoxProps,
51}
52
53impl ServerCompositionViewBox {
54    /// Creates the view box, active: the UI-thread view box activates its
55    /// server object when it is created.
56    pub fn new(compositor: &Rc<ServerCompositor>) -> Rc<ServerCompositionViewBox> {
57        let view_box = Rc::new_cyclic(|this: &Weak<ServerCompositionViewBox>| ServerCompositionViewBox {
58            base: ServerRenderResource::new(
59                compositor,
60                this.clone() as Weak<dyn IAnimatedServerObject>,
61                this.clone() as Weak<dyn IServerRenderResource>,
62            ),
63            props: ServerCompositionViewBoxProps::new(),
64        });
65        view_box.base.object().activate();
66        view_box
67    }
68
69    /// The properties of the view box.
70    pub fn props(&self) -> &ServerCompositionViewBoxProps {
71        &self.props
72    }
73
74    pub fn is_disposed(&self) -> bool {
75        self.base.is_disposed()
76    }
77
78    /// How the view box maps onto a visual of `size`; `None` when the view
79    /// box or the visual is empty (the shapes are then drawn without a view
80    /// box).
81    pub fn mapping(&self, size: Vector) -> Option<ViewBoxMapping> {
82        view_box_mapping(
83            self.props.offset(),
84            self.props.size(),
85            Rect::new(0.0, 0.0, size.x, size.y),
86            self.props.stretch(),
87            self.props.horizontal_alignment_ratio() as f64,
88            self.props.vertical_alignment_ratio() as f64,
89        )
90    }
91
92    /// The mapping of the view box behind `object` (a server-side view box)
93    /// onto a visual of `size`.
94    pub fn mapping_of(object: &Rc<dyn IServerObject>, size: Vector) -> Option<ViewBoxMapping> {
95        object.as_any().downcast_ref::<ServerCompositionViewBox>()?.mapping(size)
96    }
97}
98
99impl_server_render_resource!(
100    ServerCompositionViewBox,
101    base,
102    composition_property: ServerCompositionViewBoxProps::get_composition_property
103);
104
105impl ServerCompositionViewBoxHooks for ServerCompositionViewBox {}
106
107impl IServerObject for ServerCompositionViewBox {
108    fn deserialize_changes_core(&self, reader: &mut BatchStreamReader<'_>, committed_at: Duration) {
109        self.props.deserialize_changes_core(self, reader, committed_at);
110    }
111
112    fn values_invalidated(&self) {
113        self.base.core().invalidated(self);
114    }
115
116    fn dispose(&self) {
117        self.base.core().dispose();
118    }
119
120    fn as_render_resource(self: Rc<Self>) -> Option<Rc<dyn IServerRenderResource>> {
121        Some(self)
122    }
123
124    fn as_animated(self: Rc<Self>) -> Option<Rc<dyn IAnimatedServerObject>> {
125        Some(self)
126    }
127
128    fn get_props(&self, type_id: TypeId) -> Option<&dyn Any> {
129        self.props.find_props(type_id)
130    }
131
132    fn as_any(&self) -> &dyn Any {
133        self
134    }
135
136    fn into_any_rc(self: Rc<Self>) -> Rc<dyn Any> {
137        self
138    }
139}