async_context.rs

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