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