use crate::{ AnyView, AnyWindowHandle, AppCell, AppContext, BackgroundExecutor, Context, DismissEvent, FocusableView, ForegroundExecutor, Model, ModelContext, Render, Result, Task, View, ViewContext, VisualContext, WindowContext, WindowHandle, }; use anyhow::{anyhow, Context as _}; use derive_more::{Deref, DerefMut}; use std::{future::Future, rc::Weak}; /// An async-friendly version of [AppContext] with a static lifetime so it can be held across `await` points in async code. /// You're provided with an instance when calling [AppContext::spawn], and you can also create one with [AppContext::to_async]. /// Internally, this holds a weak reference to an `AppContext`, so its methods are fallible to protect against cases where the [AppContext] is dropped. #[derive(Clone)] pub struct AsyncAppContext { pub(crate) app: Weak, pub(crate) background_executor: BackgroundExecutor, pub(crate) foreground_executor: ForegroundExecutor, } impl Context for AsyncAppContext { type Result = Result; fn new_model( &mut self, build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T, ) -> Self::Result> where T: 'static, { let app = self .app .upgrade() .ok_or_else(|| anyhow!("app was released"))?; let mut app = app.borrow_mut(); Ok(app.new_model(build_model)) } fn update_model( &mut self, handle: &Model, update: impl FnOnce(&mut T, &mut ModelContext<'_, T>) -> R, ) -> Self::Result { let app = self .app .upgrade() .ok_or_else(|| anyhow!("app was released"))?; let mut app = app.borrow_mut(); Ok(app.update_model(handle, update)) } fn read_model( &self, handle: &Model, callback: impl FnOnce(&T, &AppContext) -> R, ) -> Self::Result where T: 'static, { let app = self.app.upgrade().context("app was released")?; let lock = app.borrow(); Ok(lock.read_model(handle, callback)) } fn update_window(&mut self, window: AnyWindowHandle, f: F) -> Result where F: FnOnce(AnyView, &mut WindowContext<'_>) -> T, { let app = self.app.upgrade().context("app was released")?; let mut lock = app.borrow_mut(); lock.update_window(window, f) } fn read_window( &self, window: &WindowHandle, read: impl FnOnce(View, &AppContext) -> R, ) -> Result where T: 'static, { let app = self.app.upgrade().context("app was released")?; let lock = app.borrow(); lock.read_window(window, read) } } impl AsyncAppContext { /// Schedules all windows in the application to be redrawn. pub fn refresh(&mut self) -> Result<()> { let app = self .app .upgrade() .ok_or_else(|| anyhow!("app was released"))?; let mut lock = app.borrow_mut(); lock.refresh(); Ok(()) } /// Get an executor which can be used to spawn futures in the background. pub fn background_executor(&self) -> &BackgroundExecutor { &self.background_executor } /// Get an executor which can be used to spawn futures in the foreground. pub fn foreground_executor(&self) -> &ForegroundExecutor { &self.foreground_executor } /// Invoke the given function in the context of the app, then flush any effects produced during its invocation. pub fn update(&self, f: impl FnOnce(&mut AppContext) -> R) -> Result { let app = self .app .upgrade() .ok_or_else(|| anyhow!("app was released"))?; let mut lock = app.borrow_mut(); Ok(f(&mut lock)) } /// Open a window with the given options based on the root view returned by the given function. pub fn open_window( &self, options: crate::WindowOptions, build_root_view: impl FnOnce(&mut WindowContext) -> View, ) -> Result> where V: 'static + Render, { let app = self .app .upgrade() .ok_or_else(|| anyhow!("app was released"))?; let mut lock = app.borrow_mut(); Ok(lock.open_window(options, build_root_view)) } /// Schedule a future to be polled in the background. pub fn spawn(&self, f: impl FnOnce(AsyncAppContext) -> Fut) -> Task where Fut: Future + 'static, R: 'static, { self.foreground_executor.spawn(f(self.clone())) } /// Determine whether global state of the specified type has been assigned. /// Returns an error if the `AppContext` has been dropped. pub fn has_global(&self) -> Result { let app = self .app .upgrade() .ok_or_else(|| anyhow!("app was released"))?; let app = app.borrow_mut(); Ok(app.has_global::()) } /// Reads the global state of the specified type, passing it to the given callback. /// /// Panics if no global state of the specified type has been assigned. /// Returns an error if the `AppContext` has been dropped. pub fn read_global(&self, read: impl FnOnce(&G, &AppContext) -> R) -> Result { let app = self .app .upgrade() .ok_or_else(|| anyhow!("app was released"))?; let app = app.borrow_mut(); Ok(read(app.global(), &app)) } /// Reads the global state of the specified type, passing it to the given callback. /// /// Similar to [`AsyncAppContext::read_global`], but returns an error instead of panicking /// if no state of the specified type has been assigned. /// /// Returns an error if no state of the specified type has been assigned the `AppContext` has been dropped. pub fn try_read_global( &self, read: impl FnOnce(&G, &AppContext) -> R, ) -> Option { let app = self.app.upgrade()?; let app = app.borrow_mut(); Some(read(app.try_global()?, &app)) } /// A convenience method for [AppContext::update_global] /// for updating the global state of the specified type. pub fn update_global( &mut self, update: impl FnOnce(&mut G, &mut AppContext) -> R, ) -> Result { let app = self .app .upgrade() .ok_or_else(|| anyhow!("app was released"))?; let mut app = app.borrow_mut(); Ok(app.update_global(update)) } } /// A cloneable, owned handle to the application context, /// composed with the window associated with the current task. #[derive(Clone, Deref, DerefMut)] pub struct AsyncWindowContext { #[deref] #[deref_mut] app: AsyncAppContext, window: AnyWindowHandle, } impl AsyncWindowContext { pub(crate) fn new(app: AsyncAppContext, window: AnyWindowHandle) -> Self { Self { app, window } } /// Get the handle of the window this context is associated with. pub fn window_handle(&self) -> AnyWindowHandle { self.window } /// A convenience method for [`AppContext::update_window`]. pub fn update(&mut self, update: impl FnOnce(&mut WindowContext) -> R) -> Result { self.app.update_window(self.window, |_, cx| update(cx)) } /// A convenience method for [`AppContext::update_window`]. pub fn update_root( &mut self, update: impl FnOnce(AnyView, &mut WindowContext) -> R, ) -> Result { self.app.update_window(self.window, update) } /// A convenience method for [`WindowContext::on_next_frame`]. pub fn on_next_frame(&mut self, f: impl FnOnce(&mut WindowContext) + 'static) { self.window.update(self, |_, cx| cx.on_next_frame(f)).ok(); } /// A convenience method for [`AppContext::global`]. pub fn read_global( &mut self, read: impl FnOnce(&G, &WindowContext) -> R, ) -> Result { self.window.update(self, |_, cx| read(cx.global(), cx)) } /// A convenience method for [`AppContext::update_global`]. /// for updating the global state of the specified type. pub fn update_global( &mut self, update: impl FnOnce(&mut G, &mut WindowContext) -> R, ) -> Result where G: 'static, { self.window.update(self, |_, cx| cx.update_global(update)) } /// Schedule a future to be executed on the main thread. This is used for collecting /// the results of background tasks and updating the UI. pub fn spawn(&self, f: impl FnOnce(AsyncWindowContext) -> Fut) -> Task where Fut: Future + 'static, R: 'static, { self.foreground_executor.spawn(f(self.clone())) } } impl Context for AsyncWindowContext { type Result = Result; fn new_model( &mut self, build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T, ) -> Result> where T: 'static, { self.window.update(self, |_, cx| cx.new_model(build_model)) } fn update_model( &mut self, handle: &Model, update: impl FnOnce(&mut T, &mut ModelContext<'_, T>) -> R, ) -> Result { self.window .update(self, |_, cx| cx.update_model(handle, update)) } fn update_window(&mut self, window: AnyWindowHandle, update: F) -> Result where F: FnOnce(AnyView, &mut WindowContext<'_>) -> T, { self.app.update_window(window, update) } fn read_model( &self, handle: &Model, read: impl FnOnce(&T, &AppContext) -> R, ) -> Self::Result where T: 'static, { self.app.read_model(handle, read) } fn read_window( &self, window: &WindowHandle, read: impl FnOnce(View, &AppContext) -> R, ) -> Result where T: 'static, { self.app.read_window(window, read) } } impl VisualContext for AsyncWindowContext { fn new_view( &mut self, build_view_state: impl FnOnce(&mut ViewContext<'_, V>) -> V, ) -> Self::Result> where V: 'static + Render, { self.window .update(self, |_, cx| cx.new_view(build_view_state)) } fn update_view( &mut self, view: &View, update: impl FnOnce(&mut V, &mut ViewContext<'_, V>) -> R, ) -> Self::Result { self.window .update(self, |_, cx| cx.update_view(view, update)) } fn replace_root_view( &mut self, build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V, ) -> Self::Result> where V: 'static + Render, { self.window .update(self, |_, cx| cx.replace_root_view(build_view)) } fn focus_view(&mut self, view: &View) -> Self::Result<()> where V: FocusableView, { self.window.update(self, |_, cx| { view.read(cx).focus_handle(cx).clone().focus(cx); }) } fn dismiss_view(&mut self, view: &View) -> Self::Result<()> where V: crate::ManagedView, { self.window .update(self, |_, cx| view.update(cx, |_, cx| cx.emit(DismissEvent))) } }