async_context.rs

  1use crate::{
  2    AnyView, AnyWindowHandle, AppContext, Context, Executor, MainThread, Model, ModelContext,
  3    Render, 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, mem, 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 WindowContext<'a> = WindowContext<'a>;
 18    type ModelContext<'a, T> = ModelContext<'a, T>;
 19    type Result<T> = Result<T>;
 20
 21    fn build_model<T: 'static>(
 22        &mut self,
 23        build_model: impl FnOnce(&mut Self::ModelContext<'_, T>) -> T,
 24    ) -> Self::Result<Model<T>>
 25    where
 26        T: 'static + Send,
 27    {
 28        let app = self.app.upgrade().context("app was released")?;
 29        let mut lock = app.lock(); // Need this to compile
 30        Ok(lock.build_model(build_model))
 31    }
 32
 33    fn update_model<T: 'static, R>(
 34        &mut self,
 35        handle: &Model<T>,
 36        update: impl FnOnce(&mut T, &mut Self::ModelContext<'_, T>) -> R,
 37    ) -> Self::Result<R> {
 38        let app = self.app.upgrade().context("app was released")?;
 39        let mut lock = app.lock(); // Need this to compile
 40        Ok(lock.update_model(handle, update))
 41    }
 42
 43    fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Result<T>
 44    where
 45        F: FnOnce(AnyView, &mut Self::WindowContext<'_>) -> T,
 46    {
 47        let app = self.app.upgrade().context("app was released")?;
 48        let mut lock = app.lock(); // Need this to compile
 49        lock.update_window(window, f)
 50    }
 51}
 52
 53impl AsyncAppContext {
 54    pub fn refresh(&mut self) -> Result<()> {
 55        let app = self.app.upgrade().context("app was released")?;
 56        let mut lock = app.lock(); // Need this to compile
 57        lock.refresh();
 58        Ok(())
 59    }
 60
 61    pub fn executor(&self) -> &Executor {
 62        &self.executor
 63    }
 64
 65    pub fn update<R>(&self, f: impl FnOnce(&mut AppContext) -> R) -> Result<R> {
 66        let app = self.app.upgrade().context("app was released")?;
 67        let mut lock = app.lock();
 68        Ok(f(&mut *lock))
 69    }
 70
 71    pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut + Send + 'static) -> Task<R>
 72    where
 73        Fut: Future<Output = R> + Send + 'static,
 74        R: Send + 'static,
 75    {
 76        let this = self.clone();
 77        self.executor.spawn(async move { f(this).await })
 78    }
 79
 80    pub fn spawn_on_main<Fut, R>(
 81        &self,
 82        f: impl FnOnce(MainThread<AsyncAppContext>) -> Fut + Send + 'static,
 83    ) -> Task<R>
 84    where
 85        Fut: Future<Output = R> + 'static,
 86        R: Send + 'static,
 87    {
 88        let this = self.clone();
 89        self.executor.spawn_on_main(|| f(MainThread(this)))
 90    }
 91
 92    pub fn run_on_main<R>(
 93        &self,
 94        f: impl FnOnce(&mut MainThread<AppContext>) -> R + Send + 'static,
 95    ) -> Result<Task<R>>
 96    where
 97        R: Send + 'static,
 98    {
 99        let app = self.app.upgrade().context("app was released")?;
100        let mut app_context = app.lock();
101        Ok(app_context.run_on_main(f))
102    }
103
104    pub fn has_global<G: 'static>(&self) -> Result<bool> {
105        let app = self.app.upgrade().context("app was released")?;
106        let lock = app.lock(); // Need this to compile
107        Ok(lock.has_global::<G>())
108    }
109
110    pub fn read_global<G: 'static, R>(&self, read: impl FnOnce(&G, &AppContext) -> R) -> Result<R> {
111        let app = self.app.upgrade().context("app was released")?;
112        let lock = app.lock(); // Need this to compile
113        Ok(read(lock.global(), &lock))
114    }
115
116    pub fn try_read_global<G: 'static, R>(
117        &self,
118        read: impl FnOnce(&G, &AppContext) -> R,
119    ) -> Option<R> {
120        let app = self.app.upgrade()?;
121        let lock = app.lock(); // Need this to compile
122        Some(read(lock.try_global()?, &lock))
123    }
124
125    pub fn update_global<G: 'static, R>(
126        &mut self,
127        update: impl FnOnce(&mut G, &mut AppContext) -> R,
128    ) -> Result<R> {
129        let app = self.app.upgrade().context("app was released")?;
130        let mut lock = app.lock(); // Need this to compile
131        Ok(lock.update_global(update))
132    }
133}
134
135impl MainThread<AsyncAppContext> {
136    pub fn update<R>(&self, f: impl FnOnce(&mut MainThread<AppContext>) -> R) -> Result<R> {
137        let app = self.app.upgrade().context("app was released")?;
138        let cx = &mut *app.lock();
139        let cx = unsafe { mem::transmute::<&mut AppContext, &mut MainThread<AppContext>>(cx) };
140        Ok(f(cx))
141    }
142
143    /// Opens a new window with the given option and the root view returned by the given function.
144    /// The function is invoked with a `WindowContext`, which can be used to interact with window-specific
145    /// functionality.
146    pub fn open_window<V: Render>(
147        &mut self,
148        options: crate::WindowOptions,
149        build_root_view: impl FnOnce(&mut MainThread<WindowContext>) -> View<V> + Send + 'static,
150    ) -> Result<WindowHandle<V>> {
151        let app = self.app.upgrade().context("app was released")?;
152        let cx = &mut *app.lock();
153        let cx = unsafe { mem::transmute::<&mut AppContext, &mut MainThread<AppContext>>(cx) };
154        Ok(cx.open_window(options, build_root_view))
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>(
172        &mut self,
173        update: impl FnOnce(AnyView, &mut WindowContext) -> R,
174    ) -> Result<R> {
175        self.app.update_window(self.window, update)
176    }
177
178    pub fn on_next_frame(&mut self, f: impl FnOnce(&mut WindowContext) + Send + 'static) {
179        self.app
180            .update_window(self.window, |_root, cx| cx.on_next_frame(f))
181            .ok();
182    }
183
184    pub fn read_global<G: 'static, R>(
185        &mut self,
186        read: impl FnOnce(&G, &WindowContext) -> R,
187    ) -> Result<R> {
188        self.app
189            .update_window(self.window, |_, cx| read(cx.global(), cx))
190    }
191
192    pub fn update_global<G, R>(
193        &mut self,
194        update: impl FnOnce(&mut G, &mut WindowContext) -> R,
195    ) -> Result<R>
196    where
197        G: 'static,
198    {
199        self.app
200            .update_window(self.window, |_, cx| cx.update_global(update))
201    }
202
203    pub fn spawn<Fut, R>(
204        &self,
205        f: impl FnOnce(AsyncWindowContext) -> Fut + Send + 'static,
206    ) -> Task<R>
207    where
208        Fut: Future<Output = R> + Send + 'static,
209        R: Send + 'static,
210    {
211        let this = self.clone();
212        self.executor.spawn(async move { f(this).await })
213    }
214
215    pub fn spawn_on_main<Fut, R>(
216        &self,
217        f: impl FnOnce(AsyncWindowContext) -> Fut + Send + 'static,
218    ) -> Task<R>
219    where
220        Fut: Future<Output = R> + 'static,
221        R: Send + 'static,
222    {
223        let this = self.clone();
224        self.executor.spawn_on_main(|| f(this))
225    }
226
227    pub fn run_on_main<R>(
228        &mut self,
229        f: impl FnOnce(&mut MainThread<WindowContext>) -> R + Send + 'static,
230    ) -> Task<Result<R>>
231    where
232        R: Send + 'static,
233    {
234        self.update(|_, cx| cx.run_on_main(f))
235            .unwrap_or_else(|error| Task::ready(Err(error)))
236    }
237}
238
239impl Context for AsyncWindowContext {
240    type WindowContext<'a> = WindowContext<'a>;
241    type ModelContext<'a, T> = ModelContext<'a, T>;
242
243    type Result<T> = Result<T>;
244
245    fn build_model<T>(
246        &mut self,
247        build_model: impl FnOnce(&mut Self::ModelContext<'_, T>) -> T,
248    ) -> Result<Model<T>>
249    where
250        T: 'static + Send,
251    {
252        self.app
253            .update_window(self.window, |_, cx| cx.build_model(build_model))
254    }
255
256    fn update_model<T: 'static, R>(
257        &mut self,
258        handle: &Model<T>,
259        update: impl FnOnce(&mut T, &mut Self::ModelContext<'_, T>) -> R,
260    ) -> Result<R> {
261        self.app
262            .update_window(self.window, |_, cx| cx.update_model(handle, update))
263    }
264
265    fn update_window<T, F>(&mut self, window: AnyWindowHandle, update: F) -> Result<T>
266    where
267        F: FnOnce(AnyView, &mut Self::WindowContext<'_>) -> T,
268    {
269        self.app.update_window(window, update)
270    }
271}
272
273impl VisualContext for AsyncWindowContext {
274    type ViewContext<'a, V: 'static> = ViewContext<'a, V>;
275
276    fn build_view<V>(
277        &mut self,
278        build_view_state: impl FnOnce(&mut Self::ViewContext<'_, V>) -> V,
279    ) -> Self::Result<View<V>>
280    where
281        V: 'static + Send,
282    {
283        self.app
284            .update_window(self.window, |_, cx| cx.build_view(build_view_state))
285    }
286
287    fn update_view<V: 'static, R>(
288        &mut self,
289        view: &View<V>,
290        update: impl FnOnce(&mut V, &mut Self::ViewContext<'_, V>) -> R,
291    ) -> Self::Result<R> {
292        self.app
293            .update_window(self.window, |_, cx| cx.update_view(view, update))
294    }
295
296    fn replace_root_view<V>(
297        &mut self,
298        build_view: impl FnOnce(&mut Self::ViewContext<'_, V>) -> V,
299    ) -> Self::Result<View<V>>
300    where
301        V: 'static + Send + Render,
302    {
303        self.app
304            .update_window(self.window, |_, cx| cx.replace_root_view(build_view))
305    }
306}
307
308#[cfg(test)]
309mod tests {
310    use super::*;
311
312    #[test]
313    fn test_async_app_context_send_sync() {
314        fn assert_send_sync<T: Send + Sync>() {}
315        assert_send_sync::<AsyncAppContext>();
316    }
317}