Skip to main content

ferroui_base/rendering/composition/
composition_custom_visual.rs

1use super::generated::CompositionContainerVisualProps;
2use super::server::{ServerCompositionCustomVisual, ServerCompositionVisual};
3use super::visual::CompositionVisualKind;
4use super::{CompositionVisual, Compositor, ICompositionCustomVisualHandler};
5use std::any::Any;
6use std::cell::RefCell;
7use std::ops::Deref;
8use std::rc::Rc;
9
10/// What a custom visual adds to the container visual: the messages for its
11/// handler that wait for the next commit.
12pub(crate) struct CustomVisualData {
13    props: CompositionContainerVisualProps,
14    messages: RefCell<Option<Vec<std::sync::Arc<dyn Any + Send + Sync>>>>,
15}
16
17impl CustomVisualData {
18    pub(crate) fn props(&self) -> &CompositionContainerVisualProps {
19        &self.props
20    }
21}
22
23/// A container visual that draws through a handler on the render thread.
24#[derive(Clone)]
25pub struct CompositionCustomVisual(Rc<CompositionVisual>);
26
27impl Deref for CompositionCustomVisual {
28    type Target = Rc<CompositionVisual>;
29
30    fn deref(&self) -> &Rc<CompositionVisual> {
31        &self.0
32    }
33}
34
35impl CompositionCustomVisual {
36    pub(crate) fn new(
37        compositor: &Rc<Compositor>,
38        handler: impl FnOnce() -> Rc<dyn ICompositionCustomVisualHandler> + Send + 'static,
39    ) -> CompositionCustomVisual {
40        let data = CustomVisualData { props: CompositionContainerVisualProps::new(), messages: RefCell::new(None) };
41        CompositionCustomVisual(CompositionVisual::create_with(
42            compositor,
43            CompositionVisualKind::Custom(data),
44            move || Box::new(ServerCompositionCustomVisual::new(handler())),
45            ServerCompositionCustomVisual::attach,
46        ))
47    }
48
49    /// The handle of `visual` as a custom visual, if it is one.
50    pub fn from_visual(visual: &Rc<CompositionVisual>) -> Option<CompositionCustomVisual> {
51        matches!(visual.kind, CompositionVisualKind::Custom(_)).then(|| CompositionCustomVisual(visual.clone()))
52    }
53
54    fn data(&self) -> &CustomVisualData {
55        match &self.0.kind {
56            CompositionVisualKind::Custom(data) => data,
57            _ => unreachable!("a custom visual handle wraps a custom visual"),
58        }
59    }
60
61    /// Sends a message to the handler, which receives it on the render
62    /// thread with the next commit.
63    pub fn send_handler_message(&self, message: std::sync::Arc<dyn Any + Send + Sync>) {
64        let first = self.data().messages.borrow().is_none();
65        if first {
66            *self.data().messages.borrow_mut() = Some(Vec::new());
67            let visual = Rc::downgrade(&self.0);
68            self.compositor().request_composition_update(move || {
69                if let Some(visual) = visual.upgrade() {
70                    CompositionCustomVisual(visual).on_composition_update();
71                }
72            });
73        }
74        if let Some(messages) = self.data().messages.borrow_mut().as_mut() {
75            messages.push(message);
76        }
77    }
78
79    fn on_composition_update(&self) {
80        let Some(messages) = self.data().messages.borrow_mut().take() else { return };
81
82        let server = self.server();
83        self.compositor().post_server_job(
84            move |compositor| {
85                let visual = compositor.get::<ServerCompositionVisual>(server);
86                if let Some(content) = visual.as_ref().and_then(|v| v.content_as::<ServerCompositionCustomVisual>()) {
87                    content.dispatch_messages(messages);
88                }
89            },
90            false,
91        );
92    }
93}