async_context.rs

  1use crate::{
  2    AnyWindowHandle, AppContext, Component, Context, Executor, Handle, MainThread, ModelContext,
  3    Result, Task, View, ViewContext, VisualContext, WindowContext, WindowHandle,
  4};
  5use anyhow::Context as _;
  6use derive_more::{Deref, DerefMut};
  7use parking_lot::Mutex;
  8use std::{future::Future, sync::Weak};
  9
 10#[derive(Clone)]
 11pub struct AsyncAppContext {
 12    pub(crate) app: Weak<Mutex<AppContext>>,
 13    pub(crate) executor: Executor,
 14}
 15
 16impl Context for AsyncAppContext {
 17    type EntityContext<'a, T> = ModelContext<'a, T>;
 18    type Result<T> = Result<T>;
 19
 20    fn entity<T: 'static>(
 21        &mut self,
 22        build_entity: impl FnOnce(&mut Self::EntityContext<'_, T>) -> T,
 23    ) -> Self::Result<Handle<T>>
 24    where
 25        T: 'static + Send,
 26    {
 27        let app = self.app.upgrade().context("app was released")?;
 28        let mut lock = app.lock(); // Need this to compile
 29        Ok(lock.entity(build_entity))
 30    }
 31
 32    fn update_entity<T: 'static, R>(
 33        &mut self,
 34        handle: &Handle<T>,
 35        update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, T>) -> R,
 36    ) -> Self::Result<R> {
 37        let app = self.app.upgrade().context("app was released")?;
 38        let mut lock = app.lock(); // Need this to compile
 39        Ok(lock.update_entity(handle, update))
 40    }
 41}
 42
 43impl AsyncAppContext {
 44    pub fn refresh(&mut self) -> Result<()> {
 45        let app = self.app.upgrade().context("app was released")?;
 46        let mut lock = app.lock(); // Need this to compile
 47        lock.refresh();
 48        Ok(())
 49    }
 50
 51    pub fn executor(&self) -> &Executor {
 52        &self.executor
 53    }
 54
 55    pub fn update<R>(&self, f: impl FnOnce(&mut AppContext) -> R) -> Result<R> {
 56        let app = self.app.upgrade().context("app was released")?;
 57        let mut lock = app.lock();
 58        Ok(f(&mut *lock))
 59    }
 60
 61    pub fn read_window<R>(
 62        &self,
 63        handle: AnyWindowHandle,
 64        update: impl FnOnce(&WindowContext) -> R,
 65    ) -> Result<R> {
 66        let app = self.app.upgrade().context("app was released")?;
 67        let mut app_context = app.lock();
 68        app_context.read_window(handle, update)
 69    }
 70
 71    pub fn update_window<R>(
 72        &self,
 73        handle: AnyWindowHandle,
 74        update: impl FnOnce(&mut WindowContext) -> R,
 75    ) -> Result<R> {
 76        let app = self.app.upgrade().context("app was released")?;
 77        let mut app_context = app.lock();
 78        app_context.update_window(handle, update)
 79    }
 80
 81    pub fn update_window_root<V, R>(
 82        &mut self,
 83        handle: &WindowHandle<V>,
 84        update: impl FnOnce(&mut V, &mut ViewContext<'_, '_, V>) -> R,
 85    ) -> Result<R>
 86    where
 87        V: 'static,
 88    {
 89        let app = self.app.upgrade().context("app was released")?;
 90        let mut app_context = app.lock();
 91        app_context.update_window_root(handle, update)
 92    }
 93
 94    pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut + Send + 'static) -> Task<R>
 95    where
 96        Fut: Future<Output = R> + Send + 'static,
 97        R: Send + 'static,
 98    {
 99        let this = self.clone();
100        self.executor.spawn(async move { f(this).await })
101    }
102
103    pub fn spawn_on_main<Fut, R>(
104        &self,
105        f: impl FnOnce(AsyncAppContext) -> Fut + Send + 'static,
106    ) -> Task<R>
107    where
108        Fut: Future<Output = R> + 'static,
109        R: Send + 'static,
110    {
111        let this = self.clone();
112        self.executor.spawn_on_main(|| f(this))
113    }
114
115    pub fn run_on_main<R>(
116        &self,
117        f: impl FnOnce(&mut MainThread<AppContext>) -> R + Send + 'static,
118    ) -> Result<Task<R>>
119    where
120        R: Send + 'static,
121    {
122        let app = self.app.upgrade().context("app was released")?;
123        let mut app_context = app.lock();
124        Ok(app_context.run_on_main(f))
125    }
126
127    pub fn has_global<G: 'static>(&self) -> Result<bool> {
128        let app = self.app.upgrade().context("app was released")?;
129        let lock = app.lock(); // Need this to compile
130        Ok(lock.has_global::<G>())
131    }
132
133    pub fn read_global<G: 'static, R>(&self, read: impl FnOnce(&G, &AppContext) -> R) -> Result<R> {
134        let app = self.app.upgrade().context("app was released")?;
135        let lock = app.lock(); // Need this to compile
136        Ok(read(lock.global(), &lock))
137    }
138
139    pub fn try_read_global<G: 'static, R>(
140        &self,
141        read: impl FnOnce(&G, &AppContext) -> R,
142    ) -> Option<R> {
143        let app = self.app.upgrade()?;
144        let lock = app.lock(); // Need this to compile
145        Some(read(lock.try_global()?, &lock))
146    }
147
148    pub fn update_global<G: 'static, R>(
149        &mut self,
150        update: impl FnOnce(&mut G, &mut AppContext) -> R,
151    ) -> Result<R> {
152        let app = self.app.upgrade().context("app was released")?;
153        let mut lock = app.lock(); // Need this to compile
154        Ok(lock.update_global(update))
155    }
156}
157
158#[derive(Clone, Deref, DerefMut)]
159pub struct AsyncWindowContext {
160    #[deref]
161    #[deref_mut]
162    app: AsyncAppContext,
163    window: AnyWindowHandle,
164}
165
166impl AsyncWindowContext {
167    pub(crate) fn new(app: AsyncAppContext, window: AnyWindowHandle) -> Self {
168        Self { app, window }
169    }
170
171    pub fn update<R>(&self, update: impl FnOnce(&mut WindowContext) -> R) -> Result<R> {
172        self.app.update_window(self.window, update)
173    }
174
175    pub fn on_next_frame(&mut self, f: impl FnOnce(&mut WindowContext) + Send + 'static) {
176        self.app
177            .update_window(self.window, |cx| cx.on_next_frame(f))
178            .ok();
179    }
180
181    pub fn read_global<G: 'static, R>(
182        &self,
183        read: impl FnOnce(&G, &WindowContext) -> R,
184    ) -> Result<R> {
185        self.app
186            .read_window(self.window, |cx| read(cx.global(), cx))
187    }
188
189    pub fn update_global<G, R>(
190        &mut self,
191        update: impl FnOnce(&mut G, &mut WindowContext) -> R,
192    ) -> Result<R>
193    where
194        G: 'static,
195    {
196        self.app
197            .update_window(self.window, |cx| cx.update_global(update))
198    }
199
200    pub fn spawn<Fut, R>(
201        &self,
202        f: impl FnOnce(AsyncWindowContext) -> Fut + Send + 'static,
203    ) -> Task<R>
204    where
205        Fut: Future<Output = R> + Send + 'static,
206        R: Send + 'static,
207    {
208        let this = self.clone();
209        self.executor.spawn(async move { f(this).await })
210    }
211
212    pub fn spawn_on_main<Fut, R>(
213        &self,
214        f: impl FnOnce(AsyncWindowContext) -> Fut + Send + 'static,
215    ) -> Task<R>
216    where
217        Fut: Future<Output = R> + 'static,
218        R: Send + 'static,
219    {
220        let this = self.clone();
221        self.executor.spawn_on_main(|| f(this))
222    }
223
224    pub fn run_on_main<R>(
225        &self,
226        f: impl FnOnce(&mut MainThread<WindowContext>) -> R + Send + 'static,
227    ) -> Task<Result<R>>
228    where
229        R: Send + 'static,
230    {
231        self.update(|cx| cx.run_on_main(f))
232            .unwrap_or_else(|error| Task::ready(Err(error)))
233    }
234}
235
236impl Context for AsyncWindowContext {
237    type EntityContext<'a, T> = ModelContext<'a, T>;
238    type Result<T> = Result<T>;
239
240    fn entity<T>(
241        &mut self,
242        build_entity: impl FnOnce(&mut Self::EntityContext<'_, T>) -> T,
243    ) -> Result<Handle<T>>
244    where
245        T: 'static + Send,
246    {
247        self.app
248            .update_window(self.window, |cx| cx.entity(build_entity))
249    }
250
251    fn update_entity<T: 'static, R>(
252        &mut self,
253        handle: &Handle<T>,
254        update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, T>) -> R,
255    ) -> Result<R> {
256        self.app
257            .update_window(self.window, |cx| cx.update_entity(handle, update))
258    }
259}
260
261impl VisualContext for AsyncWindowContext {
262    type ViewContext<'a, 'w, V> = ViewContext<'a, 'w, V>;
263
264    fn build_view<E, V>(
265        &mut self,
266        build_entity: impl FnOnce(&mut Self::ViewContext<'_, '_, V>) -> V,
267        render: impl Fn(&mut V, &mut ViewContext<'_, '_, V>) -> E + Send + 'static,
268    ) -> Self::Result<View<V>>
269    where
270        E: Component<V>,
271        V: 'static + Send,
272    {
273        self.app
274            .update_window(self.window, |cx| cx.build_view(build_entity, render))
275    }
276
277    fn update_view<V: 'static, R>(
278        &mut self,
279        view: &View<V>,
280        update: impl FnOnce(&mut V, &mut Self::ViewContext<'_, '_, V>) -> R,
281    ) -> Self::Result<R> {
282        self.app
283            .update_window(self.window, |cx| cx.update_view(view, update))
284    }
285}
286
287#[cfg(test)]
288mod tests {
289    use super::*;
290
291    #[test]
292    fn test_async_app_context_send_sync() {
293        fn assert_send_sync<T: Send + Sync>() {}
294        assert_send_sync::<AsyncAppContext>();
295    }
296}