417 lines · 13.0 KB · Rust
RawDownload
use crate::{    AnyView, AnyWindowHandle, AppCell, AppContext, BackgroundExecutor, BorrowAppContext, Context,    DismissEvent, FocusableView, ForegroundExecutor, Global, Model, ModelContext, PromptLevel,    Render, Reservation, Result, Task, View, ViewContext, VisualContext, WindowContext,    WindowHandle,};use anyhow::{anyhow, Context as _};use derive_more::{Deref, DerefMut};use futures::channel::oneshot;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<AppCell>,    pub(crate) background_executor: BackgroundExecutor,    pub(crate) foreground_executor: ForegroundExecutor,} impl Context for AsyncAppContext {    type Result<T> = Result<T>;     fn new_model<T: 'static>(        &mut self,        build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,    ) -> Self::Result<Model<T>> {        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 reserve_model<T: 'static>(&mut self) -> Result<Reservation<T>> {        let app = self            .app            .upgrade()            .ok_or_else(|| anyhow!("app was released"))?;        let mut app = app.borrow_mut();        Ok(app.reserve_model())    }     fn insert_model<T: 'static>(        &mut self,        reservation: Reservation<T>,        build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,    ) -> Result<Model<T>> {        let app = self            .app            .upgrade()            .ok_or_else(|| anyhow!("app was released"))?;        let mut app = app.borrow_mut();        Ok(app.insert_model(reservation, build_model))    }     fn update_model<T: 'static, R>(        &mut self,        handle: &Model<T>,        update: impl FnOnce(&mut T, &mut ModelContext<'_, T>) -> R,    ) -> Self::Result<R> {        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<T, R>(        &self,        handle: &Model<T>,        callback: impl FnOnce(&T, &AppContext) -> R,    ) -> Self::Result<R>    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<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Result<T>    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<T, R>(        &self,        window: &WindowHandle<T>,        read: impl FnOnce(View<T>, &AppContext) -> R,    ) -> Result<R>    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(&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<R>(&self, f: impl FnOnce(&mut AppContext) -> R) -> Result<R> {        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<V>(        &self,        options: crate::WindowOptions,        build_root_view: impl FnOnce(&mut WindowContext) -> View<V>,    ) -> Result<WindowHandle<V>>    where        V: 'static + Render,    {        let app = self            .app            .upgrade()            .ok_or_else(|| anyhow!("app was released"))?;        let mut lock = app.borrow_mut();        lock.open_window(options, build_root_view)    }     /// Schedule a future to be polled in the background.    pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut) -> Task<R>    where        Fut: Future<Output = R> + '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<G: Global>(&self) -> Result<bool> {        let app = self            .app            .upgrade()            .ok_or_else(|| anyhow!("app was released"))?;        let app = app.borrow_mut();        Ok(app.has_global::<G>())    }     /// 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<G: Global, R>(&self, read: impl FnOnce(&G, &AppContext) -> R) -> Result<R> {        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<G: Global, R>(        &self,        read: impl FnOnce(&G, &AppContext) -> R,    ) -> Option<R> {        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<G: Global, R>(        &self,        update: impl FnOnce(&mut G, &mut AppContext) -> R,    ) -> Result<R> {        let app = self            .app            .upgrade()            .ok_or_else(|| anyhow!("app was released"))?;        let mut app = app.borrow_mut();        Ok(app.update(|cx| cx.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<R>(&mut self, update: impl FnOnce(&mut WindowContext) -> R) -> Result<R> {        self.app.update_window(self.window, |_, cx| update(cx))    }     /// A convenience method for [`AppContext::update_window`].    pub fn update_root<R>(        &mut self,        update: impl FnOnce(AnyView, &mut WindowContext) -> R,    ) -> Result<R> {        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<G: Global, R>(        &mut self,        read: impl FnOnce(&G, &WindowContext) -> R,    ) -> Result<R> {        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<G, R>(        &mut self,        update: impl FnOnce(&mut G, &mut WindowContext) -> R,    ) -> Result<R>    where        G: Global,    {        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<Fut, R>(&self, f: impl FnOnce(AsyncWindowContext) -> Fut) -> Task<R>    where        Fut: Future<Output = R> + 'static,        R: 'static,    {        self.foreground_executor.spawn(f(self.clone()))    }     /// Present a platform dialog.    /// The provided message will be presented, along with buttons for each answer.    /// When a button is clicked, the returned Receiver will receive the index of the clicked button.    pub fn prompt(        &mut self,        level: PromptLevel,        message: &str,        detail: Option<&str>,        answers: &[&str],    ) -> oneshot::Receiver<usize> {        self.window            .update(self, |_, cx| cx.prompt(level, message, detail, answers))            .unwrap_or_else(|_| oneshot::channel().1)    }} impl Context for AsyncWindowContext {    type Result<T> = Result<T>;     fn new_model<T>(        &mut self,        build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,    ) -> Result<Model<T>>    where        T: 'static,    {        self.window.update(self, |_, cx| cx.new_model(build_model))    }     fn reserve_model<T: 'static>(&mut self) -> Result<Reservation<T>> {        self.window.update(self, |_, cx| cx.reserve_model())    }     fn insert_model<T: 'static>(        &mut self,        reservation: Reservation<T>,        build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,    ) -> Self::Result<Model<T>> {        self.window            .update(self, |_, cx| cx.insert_model(reservation, build_model))    }     fn update_model<T: 'static, R>(        &mut self,        handle: &Model<T>,        update: impl FnOnce(&mut T, &mut ModelContext<'_, T>) -> R,    ) -> Result<R> {        self.window            .update(self, |_, cx| cx.update_model(handle, update))    }     fn read_model<T, R>(        &self,        handle: &Model<T>,        read: impl FnOnce(&T, &AppContext) -> R,    ) -> Self::Result<R>    where        T: 'static,    {        self.app.read_model(handle, read)    }     fn update_window<T, F>(&mut self, window: AnyWindowHandle, update: F) -> Result<T>    where        F: FnOnce(AnyView, &mut WindowContext<'_>) -> T,    {        self.app.update_window(window, update)    }     fn read_window<T, R>(        &self,        window: &WindowHandle<T>,        read: impl FnOnce(View<T>, &AppContext) -> R,    ) -> Result<R>    where        T: 'static,    {        self.app.read_window(window, read)    }} impl VisualContext for AsyncWindowContext {    fn new_view<V>(        &mut self,        build_view_state: impl FnOnce(&mut ViewContext<'_, V>) -> V,    ) -> Self::Result<View<V>>    where        V: 'static + Render,    {        self.window            .update(self, |_, cx| cx.new_view(build_view_state))    }     fn update_view<V: 'static, R>(        &mut self,        view: &View<V>,        update: impl FnOnce(&mut V, &mut ViewContext<'_, V>) -> R,    ) -> Self::Result<R> {        self.window            .update(self, |_, cx| cx.update_view(view, update))    }     fn replace_root_view<V>(        &mut self,        build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,    ) -> Self::Result<View<V>>    where        V: 'static + Render,    {        self.window            .update(self, |_, cx| cx.replace_root_view(build_view))    }     fn focus_view<V>(&mut self, view: &View<V>) -> Self::Result<()>    where        V: FocusableView,    {        self.window.update(self, |_, cx| {            view.read(cx).focus_handle(cx).clone().focus(cx);        })    }     fn dismiss_view<V>(&mut self, view: &View<V>) -> Self::Result<()>    where        V: crate::ManagedView,    {        self.window            .update(self, |_, cx| view.update(cx, |_, cx| cx.emit(DismissEvent)))    }}