async_context.rs

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