async_context.rs

  1use crate::{
  2    AnyView, AnyWindowHandle, App, AppCell, AppContext, BackgroundExecutor, BorrowAppContext,
  3    Entity, Focusable, ForegroundExecutor, Global, PromptLevel, Render, Reservation, Result, Task,
  4    VisualContext, Window, WindowHandle,
  5};
  6use anyhow::{anyhow, Context as _};
  7use derive_more::{Deref, DerefMut};
  8use futures::channel::oneshot;
  9use std::{future::Future, rc::Weak};
 10
 11use super::Context;
 12
 13/// An async-friendly version of [App] with a static lifetime so it can be held across `await` points in async code.
 14/// You're provided with an instance when calling [App::spawn], and you can also create one with [App::to_async].
 15/// Internally, this holds a weak reference to an `App`, so its methods are fallible to protect against cases where the [App] is dropped.
 16#[derive(Clone)]
 17pub struct AsyncApp {
 18    pub(crate) app: Weak<AppCell>,
 19    pub(crate) background_executor: BackgroundExecutor,
 20    pub(crate) foreground_executor: ForegroundExecutor,
 21}
 22
 23impl AppContext for AsyncApp {
 24    type Result<T> = Result<T>;
 25
 26    fn new<T: 'static>(
 27        &mut self,
 28        build_entity: impl FnOnce(&mut Context<'_, T>) -> T,
 29    ) -> Self::Result<Entity<T>> {
 30        let app = self
 31            .app
 32            .upgrade()
 33            .ok_or_else(|| anyhow!("app was released"))?;
 34        let mut app = app.borrow_mut();
 35        Ok(app.new(build_entity))
 36    }
 37
 38    fn reserve_entity<T: 'static>(&mut self) -> Result<Reservation<T>> {
 39        let app = self
 40            .app
 41            .upgrade()
 42            .ok_or_else(|| anyhow!("app was released"))?;
 43        let mut app = app.borrow_mut();
 44        Ok(app.reserve_entity())
 45    }
 46
 47    fn insert_entity<T: 'static>(
 48        &mut self,
 49        reservation: Reservation<T>,
 50        build_entity: impl FnOnce(&mut Context<'_, T>) -> T,
 51    ) -> Result<Entity<T>> {
 52        let app = self
 53            .app
 54            .upgrade()
 55            .ok_or_else(|| anyhow!("app was released"))?;
 56        let mut app = app.borrow_mut();
 57        Ok(app.insert_entity(reservation, build_entity))
 58    }
 59
 60    fn update_entity<T: 'static, R>(
 61        &mut self,
 62        handle: &Entity<T>,
 63        update: impl FnOnce(&mut T, &mut Context<'_, T>) -> R,
 64    ) -> Self::Result<R> {
 65        let app = self
 66            .app
 67            .upgrade()
 68            .ok_or_else(|| anyhow!("app was released"))?;
 69        let mut app = app.borrow_mut();
 70        Ok(app.update_entity(handle, update))
 71    }
 72
 73    fn read_entity<T, R>(
 74        &self,
 75        handle: &Entity<T>,
 76        callback: impl FnOnce(&T, &App) -> R,
 77    ) -> Self::Result<R>
 78    where
 79        T: 'static,
 80    {
 81        let app = self.app.upgrade().context("app was released")?;
 82        let lock = app.borrow();
 83        Ok(lock.read_entity(handle, callback))
 84    }
 85
 86    fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Result<T>
 87    where
 88        F: FnOnce(AnyView, &mut Window, &mut App) -> T,
 89    {
 90        let app = self.app.upgrade().context("app was released")?;
 91        let mut lock = app.borrow_mut();
 92        lock.update_window(window, f)
 93    }
 94
 95    fn read_window<T, R>(
 96        &self,
 97        window: &WindowHandle<T>,
 98        read: impl FnOnce(Entity<T>, &App) -> R,
 99    ) -> Result<R>
100    where
101        T: 'static,
102    {
103        let app = self.app.upgrade().context("app was released")?;
104        let lock = app.borrow();
105        lock.read_window(window, read)
106    }
107
108    fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
109    where
110        R: Send + 'static,
111    {
112        self.background_executor.spawn(future)
113    }
114
115    fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Self::Result<R>
116    where
117        G: Global,
118    {
119        let app = self.app.upgrade().context("app was released")?;
120        let mut lock = app.borrow_mut();
121        Ok(lock.update(|this| this.read_global(callback)))
122    }
123}
124
125impl AsyncApp {
126    /// Schedules all windows in the application to be redrawn.
127    pub fn refresh(&self) -> Result<()> {
128        let app = self
129            .app
130            .upgrade()
131            .ok_or_else(|| anyhow!("app was released"))?;
132        let mut lock = app.borrow_mut();
133        lock.refresh_windows();
134        Ok(())
135    }
136
137    /// Get an executor which can be used to spawn futures in the background.
138    pub fn background_executor(&self) -> &BackgroundExecutor {
139        &self.background_executor
140    }
141
142    /// Get an executor which can be used to spawn futures in the foreground.
143    pub fn foreground_executor(&self) -> &ForegroundExecutor {
144        &self.foreground_executor
145    }
146
147    /// Invoke the given function in the context of the app, then flush any effects produced during its invocation.
148    pub fn update<R>(&self, f: impl FnOnce(&mut App) -> R) -> Result<R> {
149        let app = self
150            .app
151            .upgrade()
152            .ok_or_else(|| anyhow!("app was released"))?;
153        let mut lock = app.borrow_mut();
154        Ok(f(&mut lock))
155    }
156
157    /// Open a window with the given options based on the root view returned by the given function.
158    pub fn open_window<V>(
159        &self,
160        options: crate::WindowOptions,
161        build_root_view: impl FnOnce(&mut Window, &mut App) -> Entity<V>,
162    ) -> Result<WindowHandle<V>>
163    where
164        V: 'static + Render,
165    {
166        let app = self
167            .app
168            .upgrade()
169            .ok_or_else(|| anyhow!("app was released"))?;
170        let mut lock = app.borrow_mut();
171        lock.open_window(options, build_root_view)
172    }
173
174    /// Schedule a future to be polled in the background.
175    pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncApp) -> Fut) -> Task<R>
176    where
177        Fut: Future<Output = R> + 'static,
178        R: 'static,
179    {
180        self.foreground_executor.spawn(f(self.clone()))
181    }
182
183    /// Determine whether global state of the specified type has been assigned.
184    /// Returns an error if the `App` has been dropped.
185    pub fn has_global<G: Global>(&self) -> Result<bool> {
186        let app = self
187            .app
188            .upgrade()
189            .ok_or_else(|| anyhow!("app was released"))?;
190        let app = app.borrow_mut();
191        Ok(app.has_global::<G>())
192    }
193
194    /// Reads the global state of the specified type, passing it to the given callback.
195    ///
196    /// Panics if no global state of the specified type has been assigned.
197    /// Returns an error if the `App` has been dropped.
198    pub fn read_global<G: Global, R>(&self, read: impl FnOnce(&G, &App) -> R) -> Result<R> {
199        let app = self
200            .app
201            .upgrade()
202            .ok_or_else(|| anyhow!("app was released"))?;
203        let app = app.borrow_mut();
204        Ok(read(app.global(), &app))
205    }
206
207    /// Reads the global state of the specified type, passing it to the given callback.
208    ///
209    /// Similar to [`AsyncApp::read_global`], but returns an error instead of panicking
210    /// if no state of the specified type has been assigned.
211    ///
212    /// Returns an error if no state of the specified type has been assigned the `App` has been dropped.
213    pub fn try_read_global<G: Global, R>(&self, read: impl FnOnce(&G, &App) -> R) -> Option<R> {
214        let app = self.app.upgrade()?;
215        let app = app.borrow_mut();
216        Some(read(app.try_global()?, &app))
217    }
218
219    /// A convenience method for [App::update_global]
220    /// for updating the global state of the specified type.
221    pub fn update_global<G: Global, R>(
222        &self,
223        update: impl FnOnce(&mut G, &mut App) -> R,
224    ) -> Result<R> {
225        let app = self
226            .app
227            .upgrade()
228            .ok_or_else(|| anyhow!("app was released"))?;
229        let mut app = app.borrow_mut();
230        Ok(app.update(|cx| cx.update_global(update)))
231    }
232}
233
234/// A cloneable, owned handle to the application context,
235/// composed with the window associated with the current task.
236#[derive(Clone, Deref, DerefMut)]
237pub struct AsyncWindowContext {
238    #[deref]
239    #[deref_mut]
240    app: AsyncApp,
241    window: AnyWindowHandle,
242}
243
244impl AsyncWindowContext {
245    pub(crate) fn new_context(app: AsyncApp, window: AnyWindowHandle) -> Self {
246        Self { app, window }
247    }
248
249    /// Get the handle of the window this context is associated with.
250    pub fn window_handle(&self) -> AnyWindowHandle {
251        self.window
252    }
253
254    /// A convenience method for [`App::update_window`].
255    pub fn update<R>(&mut self, update: impl FnOnce(&mut Window, &mut App) -> R) -> Result<R> {
256        self.app
257            .update_window(self.window, |_, window, cx| update(window, cx))
258    }
259
260    /// A convenience method for [`App::update_window`].
261    pub fn update_root<R>(
262        &mut self,
263        update: impl FnOnce(AnyView, &mut Window, &mut App) -> R,
264    ) -> Result<R> {
265        self.app.update_window(self.window, update)
266    }
267
268    /// A convenience method for [`Window::on_next_frame`].
269    pub fn on_next_frame(&mut self, f: impl FnOnce(&mut Window, &mut App) + 'static) {
270        self.window
271            .update(self, |_, window, _| window.on_next_frame(f))
272            .ok();
273    }
274
275    /// A convenience method for [`App::global`].
276    pub fn read_global<G: Global, R>(
277        &mut self,
278        read: impl FnOnce(&G, &Window, &App) -> R,
279    ) -> Result<R> {
280        self.window
281            .update(self, |_, window, cx| read(cx.global(), window, cx))
282    }
283
284    /// A convenience method for [`App::update_global`].
285    /// for updating the global state of the specified type.
286    pub fn update_global<G, R>(
287        &mut self,
288        update: impl FnOnce(&mut G, &mut Window, &mut App) -> R,
289    ) -> Result<R>
290    where
291        G: Global,
292    {
293        self.window.update(self, |_, window, cx| {
294            cx.update_global(|global, cx| update(global, window, cx))
295        })
296    }
297
298    /// Schedule a future to be executed on the main thread. This is used for collecting
299    /// the results of background tasks and updating the UI.
300    pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncWindowContext) -> Fut) -> Task<R>
301    where
302        Fut: Future<Output = R> + 'static,
303        R: 'static,
304    {
305        self.foreground_executor.spawn(f(self.clone()))
306    }
307
308    /// Present a platform dialog.
309    /// The provided message will be presented, along with buttons for each answer.
310    /// When a button is clicked, the returned Receiver will receive the index of the clicked button.
311    pub fn prompt(
312        &mut self,
313        level: PromptLevel,
314        message: &str,
315        detail: Option<&str>,
316        answers: &[&str],
317    ) -> oneshot::Receiver<usize> {
318        self.window
319            .update(self, |_, window, cx| {
320                window.prompt(level, message, detail, answers, cx)
321            })
322            .unwrap_or_else(|_| oneshot::channel().1)
323    }
324}
325
326impl AppContext for AsyncWindowContext {
327    type Result<T> = Result<T>;
328
329    fn new<T>(&mut self, build_entity: impl FnOnce(&mut Context<'_, T>) -> T) -> Result<Entity<T>>
330    where
331        T: 'static,
332    {
333        self.window.update(self, |_, _, cx| cx.new(build_entity))
334    }
335
336    fn reserve_entity<T: 'static>(&mut self) -> Result<Reservation<T>> {
337        self.window.update(self, |_, _, cx| cx.reserve_entity())
338    }
339
340    fn insert_entity<T: 'static>(
341        &mut self,
342        reservation: Reservation<T>,
343        build_entity: impl FnOnce(&mut Context<'_, T>) -> T,
344    ) -> Self::Result<Entity<T>> {
345        self.window
346            .update(self, |_, _, cx| cx.insert_entity(reservation, build_entity))
347    }
348
349    fn update_entity<T: 'static, R>(
350        &mut self,
351        handle: &Entity<T>,
352        update: impl FnOnce(&mut T, &mut Context<'_, T>) -> R,
353    ) -> Result<R> {
354        self.window
355            .update(self, |_, _, cx| cx.update_entity(handle, update))
356    }
357
358    fn read_entity<T, R>(
359        &self,
360        handle: &Entity<T>,
361        read: impl FnOnce(&T, &App) -> R,
362    ) -> Self::Result<R>
363    where
364        T: 'static,
365    {
366        self.app.read_entity(handle, read)
367    }
368
369    fn update_window<T, F>(&mut self, window: AnyWindowHandle, update: F) -> Result<T>
370    where
371        F: FnOnce(AnyView, &mut Window, &mut App) -> T,
372    {
373        self.app.update_window(window, update)
374    }
375
376    fn read_window<T, R>(
377        &self,
378        window: &WindowHandle<T>,
379        read: impl FnOnce(Entity<T>, &App) -> R,
380    ) -> Result<R>
381    where
382        T: 'static,
383    {
384        self.app.read_window(window, read)
385    }
386
387    fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
388    where
389        R: Send + 'static,
390    {
391        self.app.background_executor.spawn(future)
392    }
393
394    fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Result<R>
395    where
396        G: Global,
397    {
398        self.app.read_global(callback)
399    }
400}
401
402impl VisualContext for AsyncWindowContext {
403    fn window_handle(&self) -> AnyWindowHandle {
404        self.window
405    }
406
407    fn new_window_entity<T: 'static>(
408        &mut self,
409        build_entity: impl FnOnce(&mut Window, &mut Context<T>) -> T,
410    ) -> Self::Result<Entity<T>> {
411        self.window
412            .update(self, |_, window, cx| cx.new(|cx| build_entity(window, cx)))
413    }
414
415    fn update_window_entity<T: 'static, R>(
416        &mut self,
417        view: &Entity<T>,
418        update: impl FnOnce(&mut T, &mut Window, &mut Context<T>) -> R,
419    ) -> Self::Result<R> {
420        self.window.update(self, |_, window, cx| {
421            view.update(cx, |entity, cx| update(entity, window, cx))
422        })
423    }
424
425    fn replace_root_view<V>(
426        &mut self,
427        build_view: impl FnOnce(&mut Window, &mut Context<V>) -> V,
428    ) -> Self::Result<Entity<V>>
429    where
430        V: 'static + Render,
431    {
432        self.window
433            .update(self, |_, window, cx| window.replace_root(cx, build_view))
434    }
435
436    fn focus<V>(&mut self, view: &Entity<V>) -> Self::Result<()>
437    where
438        V: Focusable,
439    {
440        self.window.update(self, |_, window, cx| {
441            view.read(cx).focus_handle(cx).clone().focus(window);
442        })
443    }
444}