Skip to main content

ferroui_base/rendering/composition/
composition_visual_surface.rs

1use super::composition_object_impls::composition_object_impls;
2use super::generated::{
3    CompositionVisualSurfaceHooks, CompositionVisualSurfaceProps, ServerCompositionVisualSurfaceProps,
4};
5use super::server::{ServerCompositionVisualSurface, ServerObjectId};
6use super::transport::IRegisterForSerialization;
7use super::{CompositionObject, CompositionVisual, Compositor, ICompositionObject, ICompositionSurface};
8use crate::Vector;
9use std::cell::Cell;
10use std::rc::{Rc, Weak};
11
12/// A live image of the subtree of a visual, shown by a surface brush
13/// (WinUI `Microsoft.UI.Composition.CompositionVisualSurface`).
14///
15/// The surface shows the area `(source_offset, source_size)` of
16/// `source_visual`, in the coordinates of the source; a zero `source_size`
17/// is the size of the source visual. The subtree is rendered as a bitmap
18/// cache renders it: the visibility, opacity, transform and clip of the
19/// source visual itself are ignored, so a hidden source still shows (the
20/// connected animation hides its elements and shows them through visual
21/// surfaces). Changes of the subtree are followed while the source is
22/// attached to a composition target.
23pub struct CompositionVisualSurface {
24    this: Weak<CompositionVisualSurface>,
25    object: CompositionObject,
26    props: CompositionVisualSurfaceProps,
27    /// Whether the next batch asks the server to freeze the surface.
28    freeze_requested: Cell<bool>,
29}
30
31impl CompositionVisualSurface {
32    pub(crate) fn new(compositor: &Rc<Compositor>) -> Rc<CompositionVisualSurface> {
33        let server = compositor.create_server_object(|compositor, _| ServerCompositionVisualSurface::new(compositor));
34        let surface = Rc::new_cyclic(|this: &Weak<CompositionVisualSurface>| CompositionVisualSurface {
35            this: this.clone(),
36            object: CompositionObject::new(compositor, Some(server)),
37            props: CompositionVisualSurfaceProps::new(),
38            freeze_requested: Cell::new(false),
39        });
40        surface.props.initialize_defaults(&*surface);
41        surface
42    }
43
44    pub fn compositor(&self) -> &Rc<Compositor> {
45        self.object.compositor()
46    }
47
48    /// The embedded `CompositionObject`.
49    pub fn object(&self) -> &CompositionObject {
50        &self.object
51    }
52
53    /// The id of the server-side surface.
54    pub fn server(&self) -> ServerObjectId {
55        self.object.required_server()
56    }
57
58    pub fn is_disposed(&self) -> bool {
59        self.object.is_disposed()
60    }
61
62    pub fn dispose(&self) {
63        self.object.dispose();
64    }
65
66    /// The generated properties of the surface.
67    pub fn props(&self) -> &CompositionVisualSurfaceProps {
68        &self.props
69    }
70
71    /// The visual whose subtree the surface shows.
72    pub fn source_visual(&self) -> Option<Rc<CompositionVisual>> {
73        self.props.source_visual().and_then(|visual| visual.into_any_rc().downcast::<CompositionVisual>().ok())
74    }
75
76    pub fn set_source_visual(&self, value: Option<Rc<CompositionVisual>>) {
77        self.props.set_source_visual(self, value.map(|visual| visual as Rc<dyn ICompositionObject>))
78    }
79
80    /// The top left corner of the area shown, in the coordinates of the
81    /// source visual (WinUI `Vector2`; animatable as `SourceOffset`).
82    pub fn source_offset(&self) -> Vector {
83        self.props.source_offset()
84    }
85
86    pub fn set_source_offset(&self, value: Vector) {
87        self.props.set_source_offset(self, value)
88    }
89
90    /// The size of the area shown; zero for the size of the source visual
91    /// (WinUI `Vector2`; animatable as `SourceSize`).
92    pub fn source_size(&self) -> Vector {
93        self.props.source_size()
94    }
95
96    pub fn set_source_size(&self, value: Vector) {
97        self.props.set_source_size(self, value)
98    }
99
100    /// Freezes the surface: the frame that applies the next batch renders
101    /// the source into an offscreen layer once, and the surface shows that
102    /// image from then on, whatever happens to the source.
103    ///
104    /// Not a public member in WinUI, where the connected animation freezes
105    /// its snapshot surface internally. Best effort: the image is rendered
106    /// with the bounds of the source subtree as of the last rendered frame.
107    pub fn freeze(&self) {
108        self.freeze_requested.set(true);
109        self.register_for_serialization();
110    }
111}
112
113/// `InitializeDefaultsExtra` activates the server object: done when the
114/// server object is created.
115impl CompositionVisualSurfaceHooks for CompositionVisualSurface {}
116
117impl ICompositionSurface for CompositionVisualSurface {}
118
119composition_object_impls!(
120    CompositionVisualSurface, "CompositionVisualSurface",
121    // The request to freeze precedes the properties; the server reads it in
122    // `deserialize_changes_extra`.
123    serialize: |this, writer| {
124        writer.write(this.freeze_requested.replace(false));
125        this.props.serialize_changes_core(this, writer)
126    },
127    start_animation: |this, name, animation, final_value| this.props.start_animation(this, name, animation, final_value),
128    composition_property: |_this, name| ServerCompositionVisualSurfaceProps::get_composition_property(name),
129);