async_context.rs

  1use crate::{
  2    AnyView, AnyWindowHandle, AppCell, AppContext, BackgroundExecutor, BorrowAppContext, Context,
  3    DismissEvent, FocusableView, ForegroundExecutor, Global, Model, ModelContext, Render,
  4    Reservation, Result, Task, View, ViewContext, VisualContext, WindowContext, WindowHandle,
  5};
  6use anyhow::{anyhow, Context as _};
  7use derive_more::{Deref, DerefMut};
  8use std::{future::Future, rc::Weak};
  9
 10/// An async-friendly version of [AppContext] with a static lifetime so it can be held across `await` points in async code.
 11/// You're provided with an instance when calling [AppContext::spawn], and you can also create one with [AppContext::to_async].
 12/// Internally, this holds a weak reference to an `AppContext`, so its methods are fallible to protect against cases where the [AppContext] is dropped.
 13#[derive(Clone)]
 14pub struct AsyncAppContext {
 15    pub(crate) app: Weak<AppCell>,
 16    pub(crate) background_executor: BackgroundExecutor,
 17    pub(crate) foreground_executor: ForegroundExecutor,
 18}
 19
 20impl Context for AsyncAppContext {
 21    type Result<T> = Result<T>;
 22
 23    fn new_model<T: 'static>(
 24        &mut self,
 25        build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
 26    ) -> Self::Result<Model<T>>
 27    where
 28        T: 'static,
 29    {
 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_model(build_model))
 36    }
 37
 38    fn reserve_model<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_model())
 45    }
 46
 47    fn insert_model<T: 'static>(
 48        &mut self,
 49        reservation: Reservation<T>,
 50        build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
 51    ) -> Result<Model<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_model(reservation, build_model))
 58    }
 59
 60    fn update_model<T: 'static, R>(
 61        &mut self,
 62        handle: &Model<T>,
 63        update: impl FnOnce(&mut T, &mut ModelContext<'_, 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_model(handle, update))
 71    }
 72
 73    fn read_model<T, R>(
 74        &self,
 75        handle: &Model<T>,
 76        callback: impl FnOnce(&T, &AppContext) -> 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_model(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 WindowContext<'_>) -> 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(View<T>, &AppContext) -> 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
109impl AsyncAppContext {
110    /// Schedules all windows in the application to be redrawn.
111    pub fn refresh(&mut self) -> Result<()> {
112        let app = self
113            .app
114            .upgrade()
115            .ok_or_else(|| anyhow!("app was released"))?;
116        let mut lock = app.borrow_mut();
117        lock.refresh();
118        Ok(())
119    }
120
121    /// Get an executor which can be used to spawn futures in the background.
122    pub fn background_executor(&self) -> &BackgroundExecutor {
123        &self.background_executor
124    }
125
126    /// Get an executor which can be used to spawn futures in the foreground.
127    pub fn foreground_executor(&self) -> &ForegroundExecutor {
128        &self.foreground_executor
129    }
130
131    /// Invoke the given function in the context of the app, then flush any effects produced during its invocation.
132    pub fn update<R>(&self, f: impl FnOnce(&mut AppContext) -> R) -> Result<R> {
133        let app = self
134            .app
135            .upgrade()
136            .ok_or_else(|| anyhow!("app was released"))?;
137        let mut lock = app.borrow_mut();
138        Ok(f(&mut lock))
139    }
140
141    /// Open a window with the given options based on the root view returned by the given function.
142    pub fn open_window<V>(
143        &self,
144        options: crate::WindowOptions,
145        build_root_view: impl FnOnce(&mut WindowContext) -> View<V>,
146    ) -> Result<WindowHandle<V>>
147    where
148        V: 'static + Render,
149    {
150        let app = self
151            .app
152            .upgrade()
153            .ok_or_else(|| anyhow!("app was released"))?;
154        let mut lock = app.borrow_mut();
155        Ok(lock.open_window(options, build_root_view))
156    }
157
158    /// Schedule a future to be polled in the background.
159    pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut) -> Task<R>
160    where
161        Fut: Future<Output = R> + 'static,
162        R: 'static,
163    {
164        self.foreground_executor.spawn(f(self.clone()))
165    }
166
167    /// Determine whether global state of the specified type has been assigned.
168    /// Returns an error if the `AppContext` has been dropped.
169    pub fn has_global<G: Global>(&self) -> Result<bool> {
170        let app = self
171            .app
172            .upgrade()
173            .ok_or_else(|| anyhow!("app was released"))?;
174        let app = app.borrow_mut();
175        Ok(app.has_global::<G>())
176    }
177
178    /// Reads the global state of the specified type, passing it to the given callback.
179    ///
180    /// Panics if no global state of the specified type has been assigned.
181    /// Returns an error if the `AppContext` has been dropped.
182    pub fn read_global<G: Global, R>(&self, read: impl FnOnce(&G, &AppContext) -> R) -> Result<R> {
183        let app = self
184            .app
185            .upgrade()
186            .ok_or_else(|| anyhow!("app was released"))?;
187        let app = app.borrow_mut();
188        Ok(read(app.global(), &app))
189    }
190
191    /// Reads the global state of the specified type, passing it to the given callback.
192    ///
193    /// Similar to [`AsyncAppContext::read_global`], but returns an error instead of panicking
194    /// if no state of the specified type has been assigned.
195    ///
196    /// Returns an error if no state of the specified type has been assigned the `AppContext` has been dropped.
197    pub fn try_read_global<G: Global, R>(
198        &self,
199        read: impl FnOnce(&G, &AppContext) -> R,
200    ) -> Option<R> {
201        let app = self.app.upgrade()?;
202        let app = app.borrow_mut();
203        Some(read(app.try_global()?, &app))
204    }
205
206    /// A convenience method for [AppContext::update_global]
207    /// for updating the global state of the specified type.
208    pub fn update_global<G: Global, R>(
209        &mut self,
210        update: impl FnOnce(&mut G, &mut AppContext) -> R,
211    ) -> Result<R> {
212        let app = self
213            .app
214            .upgrade()
215            .ok_or_else(|| anyhow!("app was released"))?;
216        let mut app = app.borrow_mut();
217        Ok(app.update(|cx| cx.update_global(update)))
218    }
219}
220
221/// A cloneable, owned handle to the application context,
222/// composed with the window associated with the current task.
223#[derive(Clone, Deref, DerefMut)]
224pub struct AsyncWindowContext {
225    #[deref]
226    #[deref_mut]
227    app: AsyncAppContext,
228    window: AnyWindowHandle,
229}
230
231impl AsyncWindowContext {
232    pub(crate) fn new(app: AsyncAppContext, window: AnyWindowHandle) -> Self {
233        Self { app, window }
234    }
235
236    /// Get the handle of the window this context is associated with.
237    pub fn window_handle(&self) -> AnyWindowHandle {
238        self.window
239    }
240
241    /// A convenience method for [`AppContext::update_window`].
242    pub fn update<R>(&mut self, update: impl FnOnce(&mut WindowContext) -> R) -> Result<R> {
243        self.app.update_window(self.window, |_, cx| update(cx))
244    }
245
246    /// A convenience method for [`AppContext::update_window`].
247    pub fn update_root<R>(
248        &mut self,
249        update: impl FnOnce(AnyView, &mut WindowContext) -> R,
250    ) -> Result<R> {
251        self.app.update_window(self.window, update)
252    }
253
254    /// A convenience method for [`WindowContext::on_next_frame`].
255    pub fn on_next_frame(&mut self, f: impl FnOnce(&mut WindowContext) + 'static) {
256        self.window.update(self, |_, cx| cx.on_next_frame(f)).ok();
257    }
258
259    /// A convenience method for [`AppContext::global`].
260    pub fn read_global<G: Global, R>(
261        &mut self,
262        read: impl FnOnce(&G, &WindowContext) -> R,
263    ) -> Result<R> {
264        self.window.update(self, |_, cx| read(cx.global(), cx))
265    }
266
267    /// A convenience method for [`AppContext::update_global`].
268    /// for updating the global state of the specified type.
269    pub fn update_global<G, R>(
270        &mut self,
271        update: impl FnOnce(&mut G, &mut WindowContext) -> R,
272    ) -> Result<R>
273    where
274        G: Global,
275    {
276        self.window.update(self, |_, cx| cx.update_global(update))
277    }
278
279    /// Schedule a future to be executed on the main thread. This is used for collecting
280    /// the results of background tasks and updating the UI.
281    pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncWindowContext) -> Fut) -> Task<R>
282    where
283        Fut: Future<Output = R> + 'static,
284        R: 'static,
285    {
286        self.foreground_executor.spawn(f(self.clone()))
287    }
288}
289
290impl Context for AsyncWindowContext {
291    type Result<T> = Result<T>;
292
293    fn new_model<T>(
294        &mut self,
295        build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
296    ) -> Result<Model<T>>
297    where
298        T: 'static,
299    {
300        self.window.update(self, |_, cx| cx.new_model(build_model))
301    }
302
303    fn reserve_model<T: 'static>(&mut self) -> Result<Reservation<T>> {
304        self.window.update(self, |_, cx| cx.reserve_model())
305    }
306
307    fn insert_model<T: 'static>(
308        &mut self,
309        reservation: Reservation<T>,
310        build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
311    ) -> Self::Result<Model<T>> {
312        self.window
313            .update(self, |_, cx| cx.insert_model(reservation, build_model))
314    }
315
316    fn update_model<T: 'static, R>(
317        &mut self,
318        handle: &Model<T>,
319        update: impl FnOnce(&mut T, &mut ModelContext<'_, T>) -> R,
320    ) -> Result<R> {
321        self.window
322            .update(self, |_, cx| cx.update_model(handle, update))
323    }
324
325    fn read_model<T, R>(
326        &self,
327        handle: &Model<T>,
328        read: impl FnOnce(&T, &AppContext) -> R,
329    ) -> Self::Result<R>
330    where
331        T: 'static,
332    {
333        self.app.read_model(handle, read)
334    }
335
336    fn update_window<T, F>(&mut self, window: AnyWindowHandle, update: F) -> Result<T>
337    where
338        F: FnOnce(AnyView, &mut WindowContext<'_>) -> T,
339    {
340        self.app.update_window(window, update)
341    }
342
343    fn read_window<T, R>(
344        &self,
345        window: &WindowHandle<T>,
346        read: impl FnOnce(View<T>, &AppContext) -> R,
347    ) -> Result<R>
348    where
349        T: 'static,
350    {
351        self.app.read_window(window, read)
352    }
353}
354
355impl VisualContext for AsyncWindowContext {
356    fn new_view<V>(
357        &mut self,
358        build_view_state: impl FnOnce(&mut ViewContext<'_, V>) -> V,
359    ) -> Self::Result<View<V>>
360    where
361        V: 'static + Render,
362    {
363        self.window
364            .update(self, |_, cx| cx.new_view(build_view_state))
365    }
366
367    fn update_view<V: 'static, R>(
368        &mut self,
369        view: &View<V>,
370        update: impl FnOnce(&mut V, &mut ViewContext<'_, V>) -> R,
371    ) -> Self::Result<R> {
372        self.window
373            .update(self, |_, cx| cx.update_view(view, update))
374    }
375
376    fn replace_root_view<V>(
377        &mut self,
378        build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
379    ) -> Self::Result<View<V>>
380    where
381        V: 'static + Render,
382    {
383        self.window
384            .update(self, |_, cx| cx.replace_root_view(build_view))
385    }
386
387    fn focus_view<V>(&mut self, view: &View<V>) -> Self::Result<()>
388    where
389        V: FocusableView,
390    {
391        self.window.update(self, |_, cx| {
392            view.read(cx).focus_handle(cx).clone().focus(cx);
393        })
394    }
395
396    fn dismiss_view<V>(&mut self, view: &View<V>) -> Self::Result<()>
397    where
398        V: crate::ManagedView,
399    {
400        self.window
401            .update(self, |_, cx| view.update(cx, |_, cx| cx.emit(DismissEvent)))
402    }
403}