Skip to main content

gpui/app/
async_context.rs

1use crate::{
2    AnyView, AnyWindowHandle, App, AppCell, AppContext, BackgroundExecutor, BorrowAppContext,
3    Entity, EntityId, EventEmitter, Focusable, ForegroundExecutor, Global, GpuiBorrow,
4    PromptButton, PromptLevel, Render, Reservation, Result, Subscription, Task, VisualContext,
5    Window, WindowHandle,
6};
7use anyhow::{Context as _, bail};
8use derive_more::{Deref, DerefMut};
9use futures::channel::oneshot;
10use futures::future::FutureExt;
11use std::{future::Future, rc::Weak};
12
13use super::{Context, WeakEntity};
14
15/// An async-friendly version of [App] with a static lifetime so it can be held across `await` points in async code.
16/// You're provided with an instance when calling [App::spawn], and you can also create one with [App::to_async].
17///
18/// Internally, this holds a weak reference to an `App`. Methods will panic if the app has been dropped,
19/// but this should not happen in practice when using foreground tasks spawned via `cx.spawn()`,
20/// as the executor checks if the app is alive before running each task.
21#[derive(Clone)]
22pub struct AsyncApp {
23    pub(crate) app: Weak<AppCell>,
24    pub(crate) background_executor: BackgroundExecutor,
25    pub(crate) foreground_executor: ForegroundExecutor,
26}
27
28impl AsyncApp {
29    fn app(&self) -> std::rc::Rc<AppCell> {
30        self.app
31            .upgrade()
32            .expect("app was released before async operation completed")
33    }
34}
35
36impl AppContext for AsyncApp {
37    fn new<T: 'static>(&mut self, build_entity: impl FnOnce(&mut Context<T>) -> T) -> Entity<T> {
38        let app = self.app();
39        let mut app = app.borrow_mut();
40        app.new(build_entity)
41    }
42
43    fn reserve_entity<T: 'static>(&mut self) -> Reservation<T> {
44        let app = self.app();
45        let mut app = app.borrow_mut();
46        app.reserve_entity()
47    }
48
49    fn insert_entity<T: 'static>(
50        &mut self,
51        reservation: Reservation<T>,
52        build_entity: impl FnOnce(&mut Context<T>) -> T,
53    ) -> Entity<T> {
54        let app = self.app();
55        let mut app = app.borrow_mut();
56        app.insert_entity(reservation, build_entity)
57    }
58
59    #[inline(always)]
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    ) -> R {
65        let app = self.app();
66        let mut app = app.borrow_mut();
67        app.update_entity(handle, update)
68    }
69
70    fn as_mut<'a, T>(&'a mut self, _handle: &Entity<T>) -> GpuiBorrow<'a, T>
71    where
72        T: 'static,
73    {
74        panic!("Cannot as_mut with an async context. Try calling update() first")
75    }
76
77    #[inline(always)]
78    fn read_entity<T, R>(&self, handle: &Entity<T>, callback: impl FnOnce(&T, &App) -> R) -> R
79    where
80        T: 'static,
81    {
82        let app = self.app();
83        let lock = app.borrow();
84        lock.read_entity(handle, callback)
85    }
86
87    #[inline(always)]
88    fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Result<T>
89    where
90        F: FnOnce(AnyView, &mut Window, &mut App) -> T,
91    {
92        let app = self.app.upgrade().context("app was released")?;
93        let mut lock = app.try_borrow_mut()?;
94        if lock.quitting {
95            bail!("app is quitting");
96        }
97        lock.update_window(window, f)
98    }
99
100    #[inline(always)]
101    fn with_window<R>(
102        &mut self,
103        entity_id: EntityId,
104        f: impl FnOnce(&mut Window, &mut App) -> R,
105    ) -> Option<R> {
106        let app = self.app.upgrade()?;
107        let mut lock = app.try_borrow_mut().ok()?;
108        if lock.quitting {
109            return None;
110        }
111        lock.with_window(entity_id, f)
112    }
113
114    fn read_window<T, R>(
115        &self,
116        window: &WindowHandle<T>,
117        read: impl FnOnce(Entity<T>, &App) -> R,
118    ) -> Result<R>
119    where
120        T: 'static,
121    {
122        let app = self.app.upgrade().context("app was released")?;
123        let lock = app.borrow();
124        if lock.quitting {
125            bail!("app is quitting");
126        }
127        lock.read_window(window, read)
128    }
129
130    #[track_caller]
131    fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
132    where
133        R: Send + 'static,
134    {
135        self.background_executor.spawn(future)
136    }
137
138    fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> R
139    where
140        G: Global,
141    {
142        let app = self.app();
143        let mut lock = app.borrow_mut();
144        lock.update(|this| this.read_global(callback))
145    }
146}
147
148impl AsyncApp {
149    /// Schedules all windows in the application to be redrawn.
150    pub fn refresh(&self) {
151        let app = self.app();
152        let mut lock = app.borrow_mut();
153        // A direct call would leave the refresh effect queued, which cannot wake
154        // a platform render loop that has already parked.
155        lock.update(|cx| cx.refresh_windows());
156    }
157
158    /// Get an executor which can be used to spawn futures in the background.
159    pub fn background_executor(&self) -> &BackgroundExecutor {
160        &self.background_executor
161    }
162
163    /// Whether this app runs on the deterministic test scheduler. See
164    /// [`BackgroundExecutor::is_test`].
165    pub fn is_test(&self) -> bool {
166        self.background_executor.is_test()
167    }
168
169    /// Get an executor which can be used to spawn futures in the foreground.
170    pub fn foreground_executor(&self) -> &ForegroundExecutor {
171        &self.foreground_executor
172    }
173
174    /// Invoke the given function in the context of the app, then flush any effects produced during its invocation.
175    #[inline(always)]
176    pub fn update<R>(&self, f: impl FnOnce(&mut App) -> R) -> R {
177        let app = self.app();
178        let mut lock = app.borrow_mut();
179        lock.update(f)
180    }
181
182    /// Arrange for the given callback to be invoked whenever the given entity emits an event of a given type.
183    /// The callback is provided a handle to the emitting entity and a reference to the emitted event.
184    pub fn subscribe<T, Event>(
185        &mut self,
186        entity: &Entity<T>,
187        on_event: impl FnMut(Entity<T>, &Event, &mut App) + 'static,
188    ) -> Subscription
189    where
190        T: 'static + EventEmitter<Event>,
191        Event: 'static,
192    {
193        let app = self.app();
194        let mut lock = app.borrow_mut();
195        lock.subscribe(entity, on_event)
196    }
197
198    /// Open a window with the given options based on the root view returned by the given function.
199    pub fn open_window<V>(
200        &self,
201        options: crate::WindowOptions,
202        build_root_view: impl FnOnce(&mut Window, &mut App) -> Entity<V>,
203    ) -> Result<WindowHandle<V>>
204    where
205        V: 'static + Render,
206    {
207        let app = self.app();
208        let mut lock = app.borrow_mut();
209        if lock.quitting {
210            bail!("app is quitting");
211        }
212        lock.open_window(options, build_root_view)
213    }
214
215    /// Schedule a future to be polled in the foreground.
216    #[track_caller]
217    #[inline(always)]
218    pub fn spawn<AsyncFn, R>(&self, f: AsyncFn) -> Task<R>
219    where
220        AsyncFn: AsyncFnOnce(&mut AsyncApp) -> R + 'static,
221        R: 'static,
222    {
223        let mut cx = self.clone();
224        self.foreground_executor
225            .spawn(async move { f(&mut cx).await }.boxed_local())
226    }
227
228    /// Determine whether global state of the specified type has been assigned.
229    pub fn has_global<G: Global>(&self) -> bool {
230        let app = self.app();
231        let app = app.borrow_mut();
232        app.has_global::<G>()
233    }
234
235    /// Reads the global state of the specified type, passing it to the given callback.
236    ///
237    /// Panics if no global state of the specified type has been assigned.
238    #[inline(always)]
239    pub fn read_global<G: Global, R>(&self, read: impl FnOnce(&G, &App) -> R) -> R {
240        let app = self.app();
241        let app = app.borrow_mut();
242        read(app.global(), &app)
243    }
244
245    /// Reads the global state of the specified type, passing it to the given callback.
246    ///
247    /// Similar to [`AsyncApp::read_global`], but returns an error instead of panicking
248    pub fn try_read_global<G: Global, R>(&self, read: impl FnOnce(&G, &App) -> R) -> Option<R> {
249        let app = self.app();
250        let app = app.borrow_mut();
251        if app.quitting {
252            return None;
253        }
254        Some(read(app.try_global()?, &app))
255    }
256
257    /// Reads the global state of the specified type, passing it to the given callback.
258    /// A default value is assigned if a global of this type has not yet been assigned.
259    pub fn read_default_global<G: Global + Default, R>(
260        &self,
261        read: impl FnOnce(&G, &App) -> R,
262    ) -> R {
263        let app = self.app();
264        let mut app = app.borrow_mut();
265        app.update(|cx| {
266            cx.default_global::<G>();
267        });
268        read(app.global(), &app)
269    }
270
271    /// A convenience method for [`App::update_global`](BorrowAppContext::update_global)
272    /// for updating the global state of the specified type.
273    pub fn update_global<G: Global, R>(&self, update: impl FnOnce(&mut G, &mut App) -> R) -> R {
274        let app = self.app();
275        let mut app = app.borrow_mut();
276        app.update(|cx| cx.update_global(update))
277    }
278
279    /// Run something using this entity and cx, when the returned struct is dropped
280    pub fn on_drop<T: 'static, Callback: FnOnce(&mut T, &mut Context<T>) + 'static>(
281        &self,
282        entity: &WeakEntity<T>,
283        f: Callback,
284    ) -> gpui_util::Deferred<impl FnOnce() + use<T, Callback>> {
285        let entity = entity.clone();
286        let mut cx = self.clone();
287        gpui_util::defer(move || {
288            entity.update(&mut cx, f).ok();
289        })
290    }
291}
292
293/// A cloneable, owned handle to the application context,
294/// composed with the window associated with the current task.
295#[derive(Clone, Deref, DerefMut)]
296pub struct AsyncWindowContext {
297    #[deref]
298    #[deref_mut]
299    app: AsyncApp,
300    window: AnyWindowHandle,
301}
302
303impl AsyncWindowContext {
304    pub(crate) fn new_context(app: AsyncApp, window: AnyWindowHandle) -> Self {
305        Self { app, window }
306    }
307
308    /// Get the handle of the window this context is associated with.
309    pub fn window_handle(&self) -> AnyWindowHandle {
310        self.window
311    }
312
313    /// A convenience method for [`App::update_window`].
314    #[inline(always)]
315    pub fn update<R>(&mut self, update: impl FnOnce(&mut Window, &mut App) -> R) -> Result<R> {
316        self.app
317            .update_window(self.window, |_, window, cx| update(window, cx))
318    }
319
320    /// A convenience method for [`App::update_window`].
321    #[inline(always)]
322    pub fn update_root<R>(
323        &mut self,
324        update: impl FnOnce(AnyView, &mut Window, &mut App) -> R,
325    ) -> Result<R> {
326        self.app.update_window(self.window, update)
327    }
328
329    /// A convenience method for [`Window::on_next_frame`].
330    pub fn on_next_frame(&mut self, f: impl FnOnce(&mut Window, &mut App) + 'static) {
331        self.app
332            .update_window(self.window, |_, window, _| window.on_next_frame(f))
333            .ok();
334    }
335
336    /// A convenience method for [`App::global`].
337    pub fn read_global<G: Global, R>(
338        &mut self,
339        read: impl FnOnce(&G, &Window, &App) -> R,
340    ) -> Result<R> {
341        self.app
342            .update_window(self.window, |_, window, cx| read(cx.global(), window, cx))
343    }
344
345    /// A convenience method for [`App::update_global`](BorrowAppContext::update_global).
346    /// for updating the global state of the specified type.
347    pub fn update_global<G, R>(
348        &mut self,
349        update: impl FnOnce(&mut G, &mut Window, &mut App) -> R,
350    ) -> Result<R>
351    where
352        G: Global,
353    {
354        self.app.update_window(self.window, |_, window, cx| {
355            cx.update_global(|global, cx| update(global, window, cx))
356        })
357    }
358
359    /// Schedule a future to be executed on the main thread. This is used for collecting
360    /// the results of background tasks and updating the UI.
361    #[track_caller]
362    #[inline(always)]
363    pub fn spawn<AsyncFn, R>(&self, f: AsyncFn) -> Task<R>
364    where
365        AsyncFn: AsyncFnOnce(&mut AsyncWindowContext) -> R + 'static,
366        R: 'static,
367    {
368        let mut cx = self.clone();
369        self.foreground_executor
370            .spawn(async move { f(&mut cx).await }.boxed_local())
371    }
372
373    /// Present a platform dialog.
374    /// The provided message will be presented, along with buttons for each answer.
375    /// When a button is clicked, the returned Receiver will receive the index of the clicked button.
376    pub fn prompt<T>(
377        &mut self,
378        level: PromptLevel,
379        message: &str,
380        detail: Option<&str>,
381        answers: &[T],
382    ) -> oneshot::Receiver<usize>
383    where
384        T: Clone + Into<PromptButton>,
385    {
386        self.app
387            .update_window(self.window, |_, window, cx| {
388                window.prompt(level, message, detail, answers, cx)
389            })
390            .unwrap_or_else(|_| oneshot::channel().1)
391    }
392}
393
394impl AppContext for AsyncWindowContext {
395    fn new<T>(&mut self, build_entity: impl FnOnce(&mut Context<T>) -> T) -> Entity<T>
396    where
397        T: 'static,
398    {
399        let mut build_entity = Some(build_entity);
400        match self.app.update_window(self.window, |_, _, cx| {
401            cx.new(
402                build_entity
403                    .take()
404                    .expect("build_entity is taken exactly once"),
405            )
406        }) {
407            Ok(entity) => entity,
408            Err(_) => self.app.new(
409                build_entity
410                    .take()
411                    .expect("update_window returned Err without invoking the closure"),
412            ),
413        }
414    }
415
416    fn reserve_entity<T: 'static>(&mut self) -> Reservation<T> {
417        self.app.reserve_entity()
418    }
419
420    fn insert_entity<T: 'static>(
421        &mut self,
422        reservation: Reservation<T>,
423        build_entity: impl FnOnce(&mut Context<T>) -> T,
424    ) -> Entity<T> {
425        let mut args = Some((reservation, build_entity));
426        match self.app.update_window(self.window, |_, _, cx| {
427            let (reservation, build_entity) = args.take().expect("args are taken exactly once");
428            cx.insert_entity(reservation, build_entity)
429        }) {
430            Ok(entity) => entity,
431            Err(_) => {
432                let (reservation, build_entity) = args
433                    .take()
434                    .expect("update_window returned Err without invoking the closure");
435                self.app.insert_entity(reservation, build_entity)
436            }
437        }
438    }
439
440    #[inline(always)]
441    fn update_entity<T: 'static, R>(
442        &mut self,
443        handle: &Entity<T>,
444        update: impl FnOnce(&mut T, &mut Context<T>) -> R,
445    ) -> R {
446        self.app.update_entity(handle, update)
447    }
448
449    fn as_mut<'a, T>(&'a mut self, _: &Entity<T>) -> GpuiBorrow<'a, T>
450    where
451        T: 'static,
452    {
453        panic!("Cannot use as_mut() from an async context, call `update`")
454    }
455
456    #[inline(always)]
457    fn read_entity<T, R>(&self, handle: &Entity<T>, read: impl FnOnce(&T, &App) -> R) -> R
458    where
459        T: 'static,
460    {
461        self.app.read_entity(handle, read)
462    }
463
464    #[inline(always)]
465    fn update_window<T, F>(&mut self, window: AnyWindowHandle, update: F) -> Result<T>
466    where
467        F: FnOnce(AnyView, &mut Window, &mut App) -> T,
468    {
469        self.app.update_window(window, update)
470    }
471
472    #[inline(always)]
473    fn with_window<R>(
474        &mut self,
475        entity_id: EntityId,
476        f: impl FnOnce(&mut Window, &mut App) -> R,
477    ) -> Option<R> {
478        self.app.with_window(entity_id, f)
479    }
480
481    fn read_window<T, R>(
482        &self,
483        window: &WindowHandle<T>,
484        read: impl FnOnce(Entity<T>, &App) -> R,
485    ) -> Result<R>
486    where
487        T: 'static,
488    {
489        self.app.read_window(window, read)
490    }
491
492    #[track_caller]
493    fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
494    where
495        R: Send + 'static,
496    {
497        self.app.background_executor.spawn(future)
498    }
499
500    fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> R
501    where
502        G: Global,
503    {
504        self.app.read_global(callback)
505    }
506}
507
508impl VisualContext for AsyncWindowContext {
509    type Result<T> = Result<T>;
510
511    fn window_handle(&self) -> AnyWindowHandle {
512        self.window
513    }
514
515    fn new_window_entity<T: 'static>(
516        &mut self,
517        build_entity: impl FnOnce(&mut Window, &mut Context<T>) -> T,
518    ) -> Result<Entity<T>> {
519        self.app.update_window(self.window, |_, window, cx| {
520            cx.new(|cx| build_entity(window, cx))
521        })
522    }
523
524    fn update_window_entity<T: 'static, R>(
525        &mut self,
526        view: &Entity<T>,
527        update: impl FnOnce(&mut T, &mut Window, &mut Context<T>) -> R,
528    ) -> Result<R> {
529        let view = view.clone();
530        self.app
531            .with_window(view.entity_id(), |window, app| {
532                view.update(app, |entity, cx| update(entity, window, cx))
533            })
534            .context("entity has no current window")
535    }
536
537    fn replace_root_view<V>(
538        &mut self,
539        build_view: impl FnOnce(&mut Window, &mut Context<V>) -> V,
540    ) -> Result<Entity<V>>
541    where
542        V: 'static + Render,
543    {
544        self.app.update_window(self.window, |_, window, cx| {
545            window.replace_root(cx, build_view)
546        })
547    }
548
549    fn focus<V>(&mut self, view: &Entity<V>) -> Result<()>
550    where
551        V: Focusable,
552    {
553        self.app.update_window(self.window, |_, window, cx| {
554            view.read(cx).focus_handle(cx).focus(window, cx);
555        })
556    }
557}