Skip to main content

gpui_core/app/
async_context.rs

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