Skip to main content

ferroui_base/rendering/composition/
redirect_visual.rs

1use super::generated::{RedirectVisualHooks, RedirectVisualProps};
2use super::server::ServerRedirectVisual;
3use super::visual::CompositionVisualKind;
4use super::{CompositionVisual, CompositionVisualSurface, Compositor, ICompositionObject};
5use std::ops::Deref;
6use std::rc::Rc;
7
8impl RedirectVisualHooks for CompositionVisual {}
9
10/// A visual that shows the subtree of another visual in its own place
11/// (WinUI `Microsoft.UI.Composition.RedirectVisual`): the source keeps
12/// rendering where it is, and the redirect visual draws the content and the
13/// children of the source again, in its own coordinates, under its own
14/// children.
15///
16/// The source is drawn as a [`CompositionVisualSurface`] draws it: its
17/// content and children with their transforms, clips and opacities, the
18/// visibility, opacity, transform and clip of the source itself ignored.
19/// A redirect visual follows the changes of the source subtree while the
20/// source is attached to a composition target. A redirect visual inside the
21/// subtree of its own source draws nothing (it would contain itself).
22///
23/// Like a sprite visual, a redirect visual does not clip to its bounds when
24/// it is created (`clip_to_bounds` is `false`).
25#[derive(Clone)]
26pub struct RedirectVisual(Rc<CompositionVisual>);
27
28impl Deref for RedirectVisual {
29    type Target = Rc<CompositionVisual>;
30
31    fn deref(&self) -> &Rc<CompositionVisual> {
32        &self.0
33    }
34}
35
36impl RedirectVisual {
37    pub(crate) fn new(compositor: &Rc<Compositor>) -> RedirectVisual {
38        let visual = CompositionVisual::create(
39            compositor,
40            CompositionVisualKind::Redirect(RedirectVisualProps::new()),
41            || Box::new(ServerRedirectVisual::new()),
42        );
43        // A WinUI visual has no implicit clip.
44        visual.set_clip_to_bounds(false);
45        let this = RedirectVisual(visual);
46        // The hidden visual surface that renders the source and follows its
47        // changes on the server.
48        let surface = CompositionVisualSurface::new(compositor);
49        this.props().set_source_surface(&*this.0, Some(surface as Rc<dyn ICompositionObject>));
50        this
51    }
52
53    pub(crate) fn with_source(compositor: &Rc<Compositor>, source: Option<Rc<CompositionVisual>>) -> RedirectVisual {
54        let visual = Self::new(compositor);
55        visual.set_source(source);
56        visual
57    }
58
59    /// The handle of `visual` as a redirect visual, if it is one.
60    pub fn from_visual(visual: &Rc<CompositionVisual>) -> Option<RedirectVisual> {
61        matches!(visual.kind, CompositionVisualKind::Redirect(_)).then(|| RedirectVisual(visual.clone()))
62    }
63
64    /// The visual as a composition visual.
65    pub fn visual(&self) -> &Rc<CompositionVisual> {
66        &self.0
67    }
68
69    fn props(&self) -> &RedirectVisualProps {
70        match &self.0.kind {
71            CompositionVisualKind::Redirect(props) => props,
72            _ => unreachable!("a redirect visual handle wraps a redirect visual"),
73        }
74    }
75
76    /// The hidden surface that renders the source (not a WinUI member).
77    pub(crate) fn source_surface(&self) -> Option<Rc<CompositionVisualSurface>> {
78        self.props()
79            .source_surface()
80            .and_then(|surface| surface.into_any_rc().downcast::<CompositionVisualSurface>().ok())
81    }
82
83    /// The visual whose subtree the redirect visual shows.
84    pub fn source(&self) -> Option<Rc<CompositionVisual>> {
85        self.props().source().and_then(|visual| visual.into_any_rc().downcast::<CompositionVisual>().ok())
86    }
87
88    pub fn set_source(&self, value: Option<Rc<CompositionVisual>>) {
89        if let Some(surface) = self.source_surface() {
90            surface.set_source_visual(value.clone());
91        }
92        self.props().set_source(&*self.0, value.map(|visual| visual as Rc<dyn ICompositionObject>))
93    }
94}