async_context.rs

  1use crate::{
  2    AnyView, AnyWindowHandle, App, AppCell, AppContext, BackgroundExecutor, BorrowAppContext,
  3    Entity, EventEmitter, Focusable, ForegroundExecutor, Global, PromptLevel, Render, Reservation,
  4    Result, Subscription, Task, VisualContext, Window, WindowHandle,
  5};
  6use anyhow::{Context as _, anyhow};
  7use derive_more::{Deref, DerefMut};
  8use futures::channel::oneshot;
  9use std::{future::Future, rc::Weak};
 10
 11use super::{Context, WeakEntity};
 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.try_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(lock.update(f))
155    }
156
157    /// Arrange for the given callback to be invoked whenever the given entity emits an event of a given type.
158    /// The callback is provided a handle to the emitting entity and a reference to the emitted event.
159    pub fn subscribe<T, Event>(
160        &mut self,
161        entity: &Entity<T>,
162        mut on_event: impl FnMut(Entity<T>, &Event, &mut App) + 'static,
163    ) -> Result<Subscription>
164    where
165        T: 'static + EventEmitter<Event>,
166        Event: 'static,
167    {
168        let app = self
169            .app
170            .upgrade()
171            .ok_or_else(|| anyhow!("app was released"))?;
172        let mut lock = app.borrow_mut();
173        let subscription = lock.subscribe(entity, on_event);
174        Ok(subscription)
175    }
176
177    /// Open a window with the given options based on the root view returned by the given function.
178    pub fn open_window<V>(
179        &self,
180        options: crate::WindowOptions,
181        build_root_view: impl FnOnce(&mut Window, &mut App) -> Entity<V>,
182    ) -> Result<WindowHandle<V>>
183    where
184        V: 'static + Render,
185    {
186        let app = self
187            .app
188            .upgrade()
189            .ok_or_else(|| anyhow!("app was released"))?;
190        let mut lock = app.borrow_mut();
191        lock.open_window(options, build_root_view)
192    }
193
194    /// Schedule a future to be polled in the background.
195    #[track_caller]
196    pub fn spawn<AsyncFn, R>(&self, f: AsyncFn) -> Task<R>
197    where
198        AsyncFn: AsyncFnOnce(&mut AsyncApp) -> R + 'static,
199        R: 'static,
200    {
201        let mut cx = self.clone();
202        self.foreground_executor
203            .spawn(async move { f(&mut cx).await })
204    }
205
206    /// Determine whether global state of the specified type has been assigned.
207    /// Returns an error if the `App` has been dropped.
208    pub fn has_global<G: Global>(&self) -> Result<bool> {
209        let app = self
210            .app
211            .upgrade()
212            .ok_or_else(|| anyhow!("app was released"))?;
213        let app = app.borrow_mut();
214        Ok(app.has_global::<G>())
215    }
216
217    /// Reads the global state of the specified type, passing it to the given callback.
218    ///
219    /// Panics if no global state of the specified type has been assigned.
220    /// Returns an error if the `App` has been dropped.
221    pub fn read_global<G: Global, R>(&self, read: impl FnOnce(&G, &App) -> R) -> Result<R> {
222        let app = self
223            .app
224            .upgrade()
225            .ok_or_else(|| anyhow!("app was released"))?;
226        let app = app.borrow_mut();
227        Ok(read(app.global(), &app))
228    }
229
230    /// Reads the global state of the specified type, passing it to the given callback.
231    ///
232    /// Similar to [`AsyncApp::read_global`], but returns an error instead of panicking
233    /// if no state of the specified type has been assigned.
234    ///
235    /// Returns an error if no state of the specified type has been assigned the `App` has been dropped.
236    pub fn try_read_global<G: Global, R>(&self, read: impl FnOnce(&G, &App) -> R) -> Option<R> {
237        let app = self.app.upgrade()?;
238        let app = app.borrow_mut();
239        Some(read(app.try_global()?, &app))
240    }
241
242    /// A convenience method for [App::update_global]
243    /// for updating the global state of the specified type.
244    pub fn update_global<G: Global, R>(
245        &self,
246        update: impl FnOnce(&mut G, &mut App) -> R,
247    ) -> Result<R> {
248        let app = self
249            .app
250            .upgrade()
251            .ok_or_else(|| anyhow!("app was released"))?;
252        let mut app = app.borrow_mut();
253        Ok(app.update(|cx| cx.update_global(update)))
254    }
255
256    /// Run something using this entity and cx, when the returned struct is dropped
257    pub fn on_drop<T: 'static, Callback: FnOnce(&mut T, &mut Context<T>) + 'static>(
258        &self,
259        entity: &WeakEntity<T>,
260        f: Callback,
261    ) -> util::Deferred<impl FnOnce() + use<T, Callback>> {
262        let entity = entity.clone();
263        let mut cx = self.clone();
264        util::defer(move || {
265            entity.update(&mut cx, f).ok();
266        })
267    }
268}
269
270/// A cloneable, owned handle to the application context,
271/// composed with the window associated with the current task.
272#[derive(Clone, Deref, DerefMut)]
273pub struct AsyncWindowContext {
274    #[deref]
275    #[deref_mut]
276    app: AsyncApp,
277    window: AnyWindowHandle,
278}
279
280impl AsyncWindowContext {
281    pub(crate) fn new_context(app: AsyncApp, window: AnyWindowHandle) -> Self {
282        Self { app, window }
283    }
284
285    /// Get the handle of the window this context is associated with.
286    pub fn window_handle(&self) -> AnyWindowHandle {
287        self.window
288    }
289
290    /// A convenience method for [`App::update_window`].
291    pub fn update<R>(&mut self, update: impl FnOnce(&mut Window, &mut App) -> R) -> Result<R> {
292        self.app
293            .update_window(self.window, |_, window, cx| update(window, cx))
294    }
295
296    /// A convenience method for [`App::update_window`].
297    pub fn update_root<R>(
298        &mut self,
299        update: impl FnOnce(AnyView, &mut Window, &mut App) -> R,
300    ) -> Result<R> {
301        self.app.update_window(self.window, update)
302    }
303
304    /// A convenience method for [`Window::on_next_frame`].
305    pub fn on_next_frame(&mut self, f: impl FnOnce(&mut Window, &mut App) + 'static) {
306        self.window
307            .update(self, |_, window, _| window.on_next_frame(f))
308            .ok();
309    }
310
311    /// A convenience method for [`App::global`].
312    pub fn read_global<G: Global, R>(
313        &mut self,
314        read: impl FnOnce(&G, &Window, &App) -> R,
315    ) -> Result<R> {
316        self.window
317            .update(self, |_, window, cx| read(cx.global(), window, cx))
318    }
319
320    /// A convenience method for [`App::update_global`].
321    /// for updating the global state of the specified type.
322    pub fn update_global<G, R>(
323        &mut self,
324        update: impl FnOnce(&mut G, &mut Window, &mut App) -> R,
325    ) -> Result<R>
326    where
327        G: Global,
328    {
329        self.window.update(self, |_, window, cx| {
330            cx.update_global(|global, cx| update(global, window, cx))
331        })
332    }
333
334    /// Schedule a future to be executed on the main thread. This is used for collecting
335    /// the results of background tasks and updating the UI.
336    #[track_caller]
337    pub fn spawn<AsyncFn, R>(&self, f: AsyncFn) -> Task<R>
338    where
339        AsyncFn: AsyncFnOnce(&mut AsyncWindowContext) -> R + 'static,
340        R: 'static,
341    {
342        let mut cx = self.clone();
343        self.foreground_executor
344            .spawn(async move { f(&mut cx).await })
345    }
346
347    /// Present a platform dialog.
348    /// The provided message will be presented, along with buttons for each answer.
349    /// When a button is clicked, the returned Receiver will receive the index of the clicked button.
350    pub fn prompt(
351        &mut self,
352        level: PromptLevel,
353        message: &str,
354        detail: Option<&str>,
355        answers: &[&str],
356    ) -> oneshot::Receiver<usize> {
357        self.window
358            .update(self, |_, window, cx| {
359                window.prompt(level, message, detail, answers, cx)
360            })
361            .unwrap_or_else(|_| oneshot::channel().1)
362    }
363}
364
365impl AppContext for AsyncWindowContext {
366    type Result<T> = Result<T>;
367
368    fn new<T>(&mut self, build_entity: impl FnOnce(&mut Context<T>) -> T) -> Result<Entity<T>>
369    where
370        T: 'static,
371    {
372        self.window.update(self, |_, _, cx| cx.new(build_entity))
373    }
374
375    fn reserve_entity<T: 'static>(&mut self) -> Result<Reservation<T>> {
376        self.window.update(self, |_, _, cx| cx.reserve_entity())
377    }
378
379    fn insert_entity<T: 'static>(
380        &mut self,
381        reservation: Reservation<T>,
382        build_entity: impl FnOnce(&mut Context<T>) -> T,
383    ) -> Self::Result<Entity<T>> {
384        self.window
385            .update(self, |_, _, cx| cx.insert_entity(reservation, build_entity))
386    }
387
388    fn update_entity<T: 'static, R>(
389        &mut self,
390        handle: &Entity<T>,
391        update: impl FnOnce(&mut T, &mut Context<T>) -> R,
392    ) -> Result<R> {
393        self.window
394            .update(self, |_, _, cx| cx.update_entity(handle, update))
395    }
396
397    fn read_entity<T, R>(
398        &self,
399        handle: &Entity<T>,
400        read: impl FnOnce(&T, &App) -> R,
401    ) -> Self::Result<R>
402    where
403        T: 'static,
404    {
405        self.app.read_entity(handle, read)
406    }
407
408    fn update_window<T, F>(&mut self, window: AnyWindowHandle, update: F) -> Result<T>
409    where
410        F: FnOnce(AnyView, &mut Window, &mut App) -> T,
411    {
412        self.app.update_window(window, update)
413    }
414
415    fn read_window<T, R>(
416        &self,
417        window: &WindowHandle<T>,
418        read: impl FnOnce(Entity<T>, &App) -> R,
419    ) -> Result<R>
420    where
421        T: 'static,
422    {
423        self.app.read_window(window, read)
424    }
425
426    fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
427    where
428        R: Send + 'static,
429    {
430        self.app.background_executor.spawn(future)
431    }
432
433    fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> Result<R>
434    where
435        G: Global,
436    {
437        self.app.read_global(callback)
438    }
439}
440
441impl VisualContext for AsyncWindowContext {
442    fn window_handle(&self) -> AnyWindowHandle {
443        self.window
444    }
445
446    fn new_window_entity<T: 'static>(
447        &mut self,
448        build_entity: impl FnOnce(&mut Window, &mut Context<T>) -> T,
449    ) -> Self::Result<Entity<T>> {
450        self.window
451            .update(self, |_, window, cx| cx.new(|cx| build_entity(window, cx)))
452    }
453
454    fn update_window_entity<T: 'static, R>(
455        &mut self,
456        view: &Entity<T>,
457        update: impl FnOnce(&mut T, &mut Window, &mut Context<T>) -> R,
458    ) -> Self::Result<R> {
459        self.window.update(self, |_, window, cx| {
460            view.update(cx, |entity, cx| update(entity, window, cx))
461        })
462    }
463
464    fn replace_root_view<V>(
465        &mut self,
466        build_view: impl FnOnce(&mut Window, &mut Context<V>) -> V,
467    ) -> Self::Result<Entity<V>>
468    where
469        V: 'static + Render,
470    {
471        self.window
472            .update(self, |_, window, cx| window.replace_root(cx, build_view))
473    }
474
475    fn focus<V>(&mut self, view: &Entity<V>) -> Self::Result<()>
476    where
477        V: Focusable,
478    {
479        self.window.update(self, |_, window, cx| {
480            view.read(cx).focus_handle(cx).clone().focus(window);
481        })
482    }
483}