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    /// Get an executor which can be used to spawn futures in the foreground.
164    pub fn foreground_executor(&self) -> &ForegroundExecutor {
165        &self.foreground_executor
166    }
167
168    /// Invoke the given function in the context of the app, then flush any effects produced during its invocation.
169    #[inline(always)]
170    pub fn update<R>(&self, f: impl FnOnce(&mut App) -> R) -> R {
171        let app = self.app();
172        let mut lock = app.borrow_mut();
173        lock.update(f)
174    }
175
176    /// Arrange for the given callback to be invoked whenever the given entity emits an event of a given type.
177    /// The callback is provided a handle to the emitting entity and a reference to the emitted event.
178    pub fn subscribe<T, Event>(
179        &mut self,
180        entity: &Entity<T>,
181        on_event: impl FnMut(Entity<T>, &Event, &mut App) + 'static,
182    ) -> Subscription
183    where
184        T: 'static + EventEmitter<Event>,
185        Event: 'static,
186    {
187        let app = self.app();
188        let mut lock = app.borrow_mut();
189        lock.subscribe(entity, on_event)
190    }
191
192    /// Open a window with the given options based on the root view returned by the given function.
193    pub fn open_window<V>(
194        &self,
195        options: crate::WindowOptions,
196        build_root_view: impl FnOnce(&mut Window, &mut App) -> Entity<V>,
197    ) -> Result<WindowHandle<V>>
198    where
199        V: 'static + Render,
200    {
201        let app = self.app();
202        let mut lock = app.borrow_mut();
203        if lock.quitting {
204            bail!("app is quitting");
205        }
206        lock.open_window(options, build_root_view)
207    }
208
209    /// Schedule a future to be polled in the foreground.
210    #[track_caller]
211    #[inline(always)]
212    pub fn spawn<AsyncFn, R>(&self, f: AsyncFn) -> Task<R>
213    where
214        AsyncFn: AsyncFnOnce(&mut AsyncApp) -> R + 'static,
215        R: 'static,
216    {
217        let mut cx = self.clone();
218        self.foreground_executor
219            .spawn(async move { f(&mut cx).await }.boxed_local())
220    }
221
222    /// Determine whether global state of the specified type has been assigned.
223    pub fn has_global<G: Global>(&self) -> bool {
224        let app = self.app();
225        let app = app.borrow_mut();
226        app.has_global::<G>()
227    }
228
229    /// Reads the global state of the specified type, passing it to the given callback.
230    ///
231    /// Panics if no global state of the specified type has been assigned.
232    #[inline(always)]
233    pub fn read_global<G: Global, R>(&self, read: impl FnOnce(&G, &App) -> R) -> R {
234        let app = self.app();
235        let app = app.borrow_mut();
236        read(app.global(), &app)
237    }
238
239    /// Reads the global state of the specified type, passing it to the given callback.
240    ///
241    /// Similar to [`AsyncApp::read_global`], but returns an error instead of panicking
242    pub fn try_read_global<G: Global, R>(&self, read: impl FnOnce(&G, &App) -> R) -> Option<R> {
243        let app = self.app();
244        let app = app.borrow_mut();
245        if app.quitting {
246            return None;
247        }
248        Some(read(app.try_global()?, &app))
249    }
250
251    /// Reads the global state of the specified type, passing it to the given callback.
252    /// A default value is assigned if a global of this type has not yet been assigned.
253    pub fn read_default_global<G: Global + Default, R>(
254        &self,
255        read: impl FnOnce(&G, &App) -> R,
256    ) -> R {
257        let app = self.app();
258        let mut app = app.borrow_mut();
259        app.update(|cx| {
260            cx.default_global::<G>();
261        });
262        read(app.global(), &app)
263    }
264
265    /// A convenience method for [`App::update_global`](BorrowAppContext::update_global)
266    /// for updating the global state of the specified type.
267    pub fn update_global<G: Global, R>(&self, update: impl FnOnce(&mut G, &mut App) -> R) -> R {
268        let app = self.app();
269        let mut app = app.borrow_mut();
270        app.update(|cx| cx.update_global(update))
271    }
272
273    /// Run something using this entity and cx, when the returned struct is dropped
274    pub fn on_drop<T: 'static, Callback: FnOnce(&mut T, &mut Context<T>) + 'static>(
275        &self,
276        entity: &WeakEntity<T>,
277        f: Callback,
278    ) -> gpui_util::Deferred<impl FnOnce() + use<T, Callback>> {
279        let entity = entity.clone();
280        let mut cx = self.clone();
281        gpui_util::defer(move || {
282            entity.update(&mut cx, f).ok();
283        })
284    }
285}
286
287/// A cloneable, owned handle to the application context,
288/// composed with the window associated with the current task.
289#[derive(Clone, Deref, DerefMut)]
290pub struct AsyncWindowContext {
291    #[deref]
292    #[deref_mut]
293    app: AsyncApp,
294    window: AnyWindowHandle,
295}
296
297impl AsyncWindowContext {
298    pub(crate) fn new_context(app: AsyncApp, window: AnyWindowHandle) -> Self {
299        Self { app, window }
300    }
301
302    /// Get the handle of the window this context is associated with.
303    pub fn window_handle(&self) -> AnyWindowHandle {
304        self.window
305    }
306
307    /// A convenience method for [`App::update_window`].
308    #[inline(always)]
309    pub fn update<R>(&mut self, update: impl FnOnce(&mut Window, &mut App) -> R) -> Result<R> {
310        self.app
311            .update_window(self.window, |_, window, cx| update(window, cx))
312    }
313
314    /// A convenience method for [`App::update_window`].
315    #[inline(always)]
316    pub fn update_root<R>(
317        &mut self,
318        update: impl FnOnce(AnyView, &mut Window, &mut App) -> R,
319    ) -> Result<R> {
320        self.app.update_window(self.window, update)
321    }
322
323    /// A convenience method for [`Window::on_next_frame`].
324    pub fn on_next_frame(&mut self, f: impl FnOnce(&mut Window, &mut App) + 'static) {
325        self.app
326            .update_window(self.window, |_, window, _| window.on_next_frame(f))
327            .ok();
328    }
329
330    /// A convenience method for [`App::global`].
331    pub fn read_global<G: Global, R>(
332        &mut self,
333        read: impl FnOnce(&G, &Window, &App) -> R,
334    ) -> Result<R> {
335        self.app
336            .update_window(self.window, |_, window, cx| read(cx.global(), window, cx))
337    }
338
339    /// A convenience method for [`App::update_global`](BorrowAppContext::update_global).
340    /// for updating the global state of the specified type.
341    pub fn update_global<G, R>(
342        &mut self,
343        update: impl FnOnce(&mut G, &mut Window, &mut App) -> R,
344    ) -> Result<R>
345    where
346        G: Global,
347    {
348        self.app.update_window(self.window, |_, window, cx| {
349            cx.update_global(|global, cx| update(global, window, cx))
350        })
351    }
352
353    /// Schedule a future to be executed on the main thread. This is used for collecting
354    /// the results of background tasks and updating the UI.
355    #[track_caller]
356    #[inline(always)]
357    pub fn spawn<AsyncFn, R>(&self, f: AsyncFn) -> Task<R>
358    where
359        AsyncFn: AsyncFnOnce(&mut AsyncWindowContext) -> R + 'static,
360        R: 'static,
361    {
362        let mut cx = self.clone();
363        self.foreground_executor
364            .spawn(async move { f(&mut cx).await }.boxed_local())
365    }
366
367    /// Present a platform dialog.
368    /// The provided message will be presented, along with buttons for each answer.
369    /// When a button is clicked, the returned Receiver will receive the index of the clicked button.
370    pub fn prompt<T>(
371        &mut self,
372        level: PromptLevel,
373        message: &str,
374        detail: Option<&str>,
375        answers: &[T],
376    ) -> oneshot::Receiver<usize>
377    where
378        T: Clone + Into<PromptButton>,
379    {
380        self.app
381            .update_window(self.window, |_, window, cx| {
382                window.prompt(level, message, detail, answers, cx)
383            })
384            .unwrap_or_else(|_| oneshot::channel().1)
385    }
386}
387
388impl AppContext for AsyncWindowContext {
389    fn new<T>(&mut self, build_entity: impl FnOnce(&mut Context<T>) -> T) -> Entity<T>
390    where
391        T: 'static,
392    {
393        let mut build_entity = Some(build_entity);
394        match self.app.update_window(self.window, |_, _, cx| {
395            cx.new(
396                build_entity
397                    .take()
398                    .expect("build_entity is taken exactly once"),
399            )
400        }) {
401            Ok(entity) => entity,
402            Err(_) => self.app.new(
403                build_entity
404                    .take()
405                    .expect("update_window returned Err without invoking the closure"),
406            ),
407        }
408    }
409
410    fn reserve_entity<T: 'static>(&mut self) -> Reservation<T> {
411        self.app.reserve_entity()
412    }
413
414    fn insert_entity<T: 'static>(
415        &mut self,
416        reservation: Reservation<T>,
417        build_entity: impl FnOnce(&mut Context<T>) -> T,
418    ) -> Entity<T> {
419        let mut args = Some((reservation, build_entity));
420        match self.app.update_window(self.window, |_, _, cx| {
421            let (reservation, build_entity) = args.take().expect("args are taken exactly once");
422            cx.insert_entity(reservation, build_entity)
423        }) {
424            Ok(entity) => entity,
425            Err(_) => {
426                let (reservation, build_entity) = args
427                    .take()
428                    .expect("update_window returned Err without invoking the closure");
429                self.app.insert_entity(reservation, build_entity)
430            }
431        }
432    }
433
434    #[inline(always)]
435    fn update_entity<T: 'static, R>(
436        &mut self,
437        handle: &Entity<T>,
438        update: impl FnOnce(&mut T, &mut Context<T>) -> R,
439    ) -> R {
440        self.app.update_entity(handle, update)
441    }
442
443    fn as_mut<'a, T>(&'a mut self, _: &Entity<T>) -> GpuiBorrow<'a, T>
444    where
445        T: 'static,
446    {
447        panic!("Cannot use as_mut() from an async context, call `update`")
448    }
449
450    #[inline(always)]
451    fn read_entity<T, R>(&self, handle: &Entity<T>, read: impl FnOnce(&T, &App) -> R) -> R
452    where
453        T: 'static,
454    {
455        self.app.read_entity(handle, read)
456    }
457
458    #[inline(always)]
459    fn update_window<T, F>(&mut self, window: AnyWindowHandle, update: F) -> Result<T>
460    where
461        F: FnOnce(AnyView, &mut Window, &mut App) -> T,
462    {
463        self.app.update_window(window, update)
464    }
465
466    #[inline(always)]
467    fn with_window<R>(
468        &mut self,
469        entity_id: EntityId,
470        f: impl FnOnce(&mut Window, &mut App) -> R,
471    ) -> Option<R> {
472        self.app.with_window(entity_id, f)
473    }
474
475    fn read_window<T, R>(
476        &self,
477        window: &WindowHandle<T>,
478        read: impl FnOnce(Entity<T>, &App) -> R,
479    ) -> Result<R>
480    where
481        T: 'static,
482    {
483        self.app.read_window(window, read)
484    }
485
486    #[track_caller]
487    fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
488    where
489        R: Send + 'static,
490    {
491        self.app.background_executor.spawn(future)
492    }
493
494    fn read_global<G, R>(&self, callback: impl FnOnce(&G, &App) -> R) -> R
495    where
496        G: Global,
497    {
498        self.app.read_global(callback)
499    }
500}
501
502impl VisualContext for AsyncWindowContext {
503    type Result<T> = Result<T>;
504
505    fn window_handle(&self) -> AnyWindowHandle {
506        self.window
507    }
508
509    fn new_window_entity<T: 'static>(
510        &mut self,
511        build_entity: impl FnOnce(&mut Window, &mut Context<T>) -> T,
512    ) -> Result<Entity<T>> {
513        self.app.update_window(self.window, |_, window, cx| {
514            cx.new(|cx| build_entity(window, cx))
515        })
516    }
517
518    fn update_window_entity<T: 'static, R>(
519        &mut self,
520        view: &Entity<T>,
521        update: impl FnOnce(&mut T, &mut Window, &mut Context<T>) -> R,
522    ) -> Result<R> {
523        let view = view.clone();
524        self.app
525            .with_window(view.entity_id(), |window, app| {
526                view.update(app, |entity, cx| update(entity, window, cx))
527            })
528            .context("entity has no current window")
529    }
530
531    fn replace_root_view<V>(
532        &mut self,
533        build_view: impl FnOnce(&mut Window, &mut Context<V>) -> V,
534    ) -> Result<Entity<V>>
535    where
536        V: 'static + Render,
537    {
538        self.app.update_window(self.window, |_, window, cx| {
539            window.replace_root(cx, build_view)
540        })
541    }
542
543    fn focus<V>(&mut self, view: &Entity<V>) -> Result<()>
544    where
545        V: Focusable,
546    {
547        self.app.update_window(self.window, |_, window, cx| {
548            view.read(cx).focus_handle(cx).focus(window, cx);
549        })
550    }
551}