Skip to main content

ferroui_base/rendering/composition/
composition_custom_visual_handler.rs

1use super::server::ServerCompositionVisual;
2use crate::media::ImmediateDrawingContext;
3use crate::platform::LtrbRect;
4use crate::{Matrix, Point, Rect, Vector};
5use std::any::Any;
6use std::cell::{Cell, RefCell};
7use std::rc::{Rc, Weak};
8use std::time::Duration;
9
10/// The state upstream's abstract `CompositionCustomVisualHandler` keeps,
11/// and its protected members: what a handler class embeds and returns from
12/// [`ICompositionCustomVisualHandler::handler_base`].
13///
14/// The handler is created on the UI thread and used on the server only: it
15/// is attached to the server-side custom visual when that visual is created.
16#[derive(Default)]
17pub struct CompositionCustomVisualHandler {
18    host: RefCell<Option<Weak<ServerCompositionVisual>>>,
19    in_render: Cell<bool>,
20    current_transformed_clip: Cell<Rect>,
21    current_transform: Cell<Matrix>,
22}
23
24/// The overridable members of a custom visual handler. A handler class
25/// embeds a [`CompositionCustomVisualHandler`] and implements this trait.
26pub trait ICompositionCustomVisualHandler: 'static {
27    /// The embedded handler state.
28    fn handler_base(&self) -> &CompositionCustomVisualHandler;
29
30    /// A message sent with `CompositionCustomVisual::send_handler_message`.
31    fn on_message(&self, _message: std::sync::Arc<dyn Any + Send + Sync>) {}
32
33    fn on_animation_frame_update(&self) {}
34
35    fn on_render(&self, drawing_context: &mut ImmediateDrawingContext<'_>);
36
37    /// The bounds of what the handler draws; the effective size of the
38    /// visual by default.
39    fn get_render_bounds(&self) -> Rect {
40        let size = self.handler_base().effective_size();
41        Rect::new(0.0, 0.0, size.x, size.y)
42    }
43}
44
45impl CompositionCustomVisualHandler {
46    pub fn new() -> Self {
47        Self::default()
48    }
49
50    /// Renders through `handler`: the render APIs are available while the
51    /// handler draws.
52    pub(crate) fn render(
53        handler: &dyn ICompositionCustomVisualHandler,
54        drawing_context: &mut ImmediateDrawingContext<'_>,
55        current_transformed_clip: Rect,
56    ) {
57        let base = handler.handler_base();
58        base.in_render.set(true);
59        base.current_transformed_clip.set(current_transformed_clip);
60        base.current_transform.set(drawing_context.current_transform());
61        struct Reset<'a>(&'a Cell<bool>);
62        impl Drop for Reset<'_> {
63            fn drop(&mut self) {
64                self.0.set(false);
65            }
66        }
67        let _reset = Reset(&base.in_render);
68        handler.on_render(drawing_context);
69    }
70
71    fn host(&self) -> Rc<ServerCompositionVisual> {
72        match self.host.borrow().as_ref().and_then(Weak::upgrade) {
73            Some(host) => host,
74            None => panic!("Object is not yet attached to the compositor"),
75        }
76    }
77
78    /// `VerifyAccess`: the handler must be attached, and its compositor
79    /// must be rendering (`_host.Compositor.VerifyAccess()`).
80    fn verify_access(&self) -> (Rc<ServerCompositionVisual>, Rc<super::server::ServerCompositor>) {
81        let host = self.host();
82        let compositor = super::server::ServerCompositor::verify_access_of(host.compositor().as_ref());
83        (host, compositor)
84    }
85
86    fn verify_in_render(&self) {
87        self.verify_access();
88        if !self.in_render.get() {
89            panic!("This API is only available from OnRender");
90        }
91    }
92
93    /// The size of the visual.
94    pub fn effective_size(&self) -> Vector {
95        self.verify_access().0.size()
96    }
97
98    /// The time of the server compositor.
99    pub fn composition_now(&self) -> Duration {
100        self.verify_access().1.server_now()
101    }
102
103    pub(crate) fn attach(&self, visual: Weak<ServerCompositionVisual>) {
104        *self.host.borrow_mut() = Some(visual);
105    }
106
107    /// Asks for the visual to be redrawn.
108    pub fn invalidate(&self) {
109        self.verify_access().0.invalidate_content();
110    }
111
112    /// Asks for an area of the visual to be redrawn.
113    pub fn invalidate_rect(&self, rc: Rect) {
114        self.verify_access().0.add_extra_dirty_rect(LtrbRect::from_rect(rc));
115    }
116
117    /// Asks for [`ICompositionCustomVisualHandler::on_animation_frame_update`]
118    /// to be called on the next frame.
119    pub fn register_for_next_animation_frame_update(&self) {
120        let (host, _) = self.verify_access();
121        super::server::ServerCompositionCustomVisual::handler_register_for_next_animation_frame_update(&host);
122    }
123
124    /// Whether a point of the visual is inside the current clip.
125    pub fn render_clip_contains(&self, pt: Point) -> bool {
126        self.verify_in_render();
127        let pt = pt.transform(self.current_transform.get());
128        self.current_transformed_clip.get().contains(pt)
129    }
130
131    /// Whether a rectangle of the visual intersects the current clip.
132    pub fn render_clip_intersectes(&self, rc: Rect) -> bool {
133        self.verify_in_render();
134        let rc = rc.transform_to_aabb(self.current_transform.get());
135        self.current_transformed_clip.get().intersects(rc)
136    }
137}