Skip to main content

vizia_core/context/
proxy.rs

1use std::any::Any;
2use std::fmt::Formatter;
3use std::sync::Mutex;
4
5use super::InternalEvent;
6
7use crate::events::ProxyEvent;
8use crate::prelude::*;
9
10/// A bundle of data representing a snapshot of the context when a thread was spawned.
11///
12/// It supports a small subset of context operations. You will get one of these passed to you when
13/// you create a new thread with the [`spawn`](crate::context::Context::spawn) method on [`Context`].
14pub struct ContextProxy {
15    /// The current entity when the proxy context was created.
16    pub current: Entity,
17    /// An event proxy used to send events back to the main thread.
18    pub event_proxy: Option<Box<dyn EventProxy>>,
19}
20
21/// Errors that might occur when emitting an event via a ContextProxy.
22#[derive(Debug)]
23pub enum ProxyEmitError {
24    /// The current runtime does not support proxying events.
25    Unsupported,
26    /// The event loop has been closed; the application is exiting.
27    EventLoopClosed,
28}
29
30impl std::fmt::Display for ProxyEmitError {
31    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
32        match self {
33            ProxyEmitError::Unsupported => {
34                f.write_str("The current runtime does not support proxying events")
35            }
36            ProxyEmitError::EventLoopClosed => {
37                f.write_str("Sending an event to an event loop which has been closed")
38            }
39        }
40    }
41}
42
43impl std::error::Error for ProxyEmitError {}
44
45impl ContextProxy {
46    pub fn emit<M: Any + Send>(&mut self, message: M) -> Result<(), ProxyEmitError> {
47        if let Some(proxy) = &self.event_proxy {
48            let event = ProxyEvent::new(message)
49                .target(self.current)
50                .origin(self.current)
51                .propagate(Propagation::Up);
52
53            proxy.send(event).map_err(|_| ProxyEmitError::EventLoopClosed)
54        } else {
55            Err(ProxyEmitError::Unsupported)
56        }
57    }
58
59    pub fn emit_to<M: Any + Send>(
60        &mut self,
61        target: Entity,
62        message: M,
63    ) -> Result<(), ProxyEmitError> {
64        if let Some(proxy) = &self.event_proxy {
65            let event = ProxyEvent::new(message)
66                .target(target)
67                .origin(self.current)
68                .propagate(Propagation::Direct);
69
70            proxy.send(event).map_err(|_| ProxyEmitError::EventLoopClosed)
71        } else {
72            Err(ProxyEmitError::Unsupported)
73        }
74    }
75
76    pub fn redraw(&mut self) -> Result<(), ProxyEmitError> {
77        self.emit(InternalEvent::Redraw)
78    }
79
80    pub fn load_image_encoded(
81        &mut self,
82        path: String,
83        data: &[u8],
84        policy: ImageRetentionPolicy,
85    ) -> Result<bool, ProxyEmitError> {
86        if let Some(image) = skia_safe::Image::from_encoded(skia_safe::Data::new_copy(data)) {
87            self.emit(InternalEvent::LoadImage { path, image: Mutex::new(Some(image)), policy })?;
88            return Ok(true);
89        }
90
91        Ok(false)
92    }
93
94    pub fn load_svg(
95        &mut self,
96        path: String,
97        data: &[u8],
98        policy: ImageRetentionPolicy,
99    ) -> Result<(), ProxyEmitError> {
100        self.emit(InternalEvent::LoadSvg { path, data: data.to_vec(), policy })
101    }
102
103    pub fn load_font(&mut self, path: String, data: &[u8]) -> Result<(), ProxyEmitError> {
104        self.emit(InternalEvent::LoadFont { path, data: data.to_vec() })
105    }
106
107    pub fn load_translation(
108        &mut self,
109        lang: LanguageIdentifier,
110        path: String,
111        ftl: String,
112    ) -> Result<(), ProxyEmitError> {
113        self.emit(InternalEvent::LoadTranslation { lang, path, ftl })
114    }
115
116    pub fn update_resource_status(
117        &mut self,
118        path: String,
119        status: crate::resource::LoadingStatus,
120    ) -> Result<(), ProxyEmitError> {
121        self.emit(InternalEvent::UpdateResourceStatus { path, status })
122    }
123
124    pub fn spawn<F>(&self, target: F)
125    where
126        F: 'static + Send + FnOnce(&mut ContextProxy),
127    {
128        let mut cxp = self.clone();
129        std::thread::spawn(move || target(&mut cxp));
130    }
131}
132
133impl Clone for ContextProxy {
134    fn clone(&self) -> Self {
135        Self {
136            current: self.current,
137            event_proxy: self.event_proxy.as_ref().map(|p| p.make_clone()),
138        }
139    }
140}
141
142pub trait EventProxy: Send {
143    #[allow(clippy::result_unit_err)]
144    fn send(&self, event: ProxyEvent) -> Result<(), ()>;
145    fn make_clone(&self) -> Box<dyn EventProxy>;
146}