use crate::{ AnyWindowHandle, AppContext, Component, Context, Executor, Handle, MainThread, ModelContext, Result, Task, View, ViewContext, VisualContext, WindowContext, WindowHandle, }; use anyhow::Context as _; use derive_more::{Deref, DerefMut}; use parking_lot::Mutex; use std::{future::Future, sync::Weak}; #[derive(Clone)] pub struct AsyncAppContext { pub(crate) app: Weak>, pub(crate) executor: Executor, } impl Context for AsyncAppContext { type EntityContext<'a, T> = ModelContext<'a, T>; type Result = Result; fn entity( &mut self, build_entity: impl FnOnce(&mut Self::EntityContext<'_, T>) -> T, ) -> Self::Result> where T: 'static + Send, { let app = self.app.upgrade().context("app was released")?; let mut lock = app.lock(); // Need this to compile Ok(lock.entity(build_entity)) } fn update_entity( &mut self, handle: &Handle, update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, T>) -> R, ) -> Self::Result { let app = self.app.upgrade().context("app was released")?; let mut lock = app.lock(); // Need this to compile Ok(lock.update_entity(handle, update)) } } impl AsyncAppContext { pub fn refresh(&mut self) -> Result<()> { let app = self.app.upgrade().context("app was released")?; let mut lock = app.lock(); // Need this to compile lock.refresh(); Ok(()) } pub fn executor(&self) -> &Executor { &self.executor } pub fn update(&self, f: impl FnOnce(&mut AppContext) -> R) -> Result { let app = self.app.upgrade().context("app was released")?; let mut lock = app.lock(); Ok(f(&mut *lock)) } pub fn read_window( &self, handle: AnyWindowHandle, update: impl FnOnce(&WindowContext) -> R, ) -> Result { let app = self.app.upgrade().context("app was released")?; let mut app_context = app.lock(); app_context.read_window(handle, update) } pub fn update_window( &self, handle: AnyWindowHandle, update: impl FnOnce(&mut WindowContext) -> R, ) -> Result { let app = self.app.upgrade().context("app was released")?; let mut app_context = app.lock(); app_context.update_window(handle, update) } pub fn update_window_root( &mut self, handle: &WindowHandle, update: impl FnOnce(&mut V, &mut ViewContext<'_, '_, V>) -> R, ) -> Result where V: 'static, { let app = self.app.upgrade().context("app was released")?; let mut app_context = app.lock(); app_context.update_window_root(handle, update) } pub fn spawn(&self, f: impl FnOnce(AsyncAppContext) -> Fut + Send + 'static) -> Task where Fut: Future + Send + 'static, R: Send + 'static, { let this = self.clone(); self.executor.spawn(async move { f(this).await }) } pub fn spawn_on_main( &self, f: impl FnOnce(AsyncAppContext) -> Fut + Send + 'static, ) -> Task where Fut: Future + 'static, R: Send + 'static, { let this = self.clone(); self.executor.spawn_on_main(|| f(this)) } pub fn run_on_main( &self, f: impl FnOnce(&mut MainThread) -> R + Send + 'static, ) -> Result> where R: Send + 'static, { let app = self.app.upgrade().context("app was released")?; let mut app_context = app.lock(); Ok(app_context.run_on_main(f)) } pub fn has_global(&self) -> Result { let app = self.app.upgrade().context("app was released")?; let lock = app.lock(); // Need this to compile Ok(lock.has_global::()) } pub fn read_global(&self, read: impl FnOnce(&G, &AppContext) -> R) -> Result { let app = self.app.upgrade().context("app was released")?; let lock = app.lock(); // Need this to compile Ok(read(lock.global(), &lock)) } pub fn try_read_global( &self, read: impl FnOnce(&G, &AppContext) -> R, ) -> Option { let app = self.app.upgrade()?; let lock = app.lock(); // Need this to compile Some(read(lock.try_global()?, &lock)) } pub fn update_global( &mut self, update: impl FnOnce(&mut G, &mut AppContext) -> R, ) -> Result { let app = self.app.upgrade().context("app was released")?; let mut lock = app.lock(); // Need this to compile Ok(lock.update_global(update)) } } #[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 } } pub fn update(&self, update: impl FnOnce(&mut WindowContext) -> R) -> Result { self.app.update_window(self.window, update) } pub fn on_next_frame(&mut self, f: impl FnOnce(&mut WindowContext) + Send + 'static) { self.app .update_window(self.window, |cx| cx.on_next_frame(f)) .ok(); } pub fn read_global( &self, read: impl FnOnce(&G, &WindowContext) -> R, ) -> Result { self.app .read_window(self.window, |cx| read(cx.global(), cx)) } pub fn update_global( &mut self, update: impl FnOnce(&mut G, &mut WindowContext) -> R, ) -> Result where G: 'static, { self.app .update_window(self.window, |cx| cx.update_global(update)) } pub fn spawn( &self, f: impl FnOnce(AsyncWindowContext) -> Fut + Send + 'static, ) -> Task where Fut: Future + Send + 'static, R: Send + 'static, { let this = self.clone(); self.executor.spawn(async move { f(this).await }) } pub fn spawn_on_main( &self, f: impl FnOnce(AsyncWindowContext) -> Fut + Send + 'static, ) -> Task where Fut: Future + 'static, R: Send + 'static, { let this = self.clone(); self.executor.spawn_on_main(|| f(this)) } pub fn run_on_main( &self, f: impl FnOnce(&mut MainThread) -> R + Send + 'static, ) -> Task> where R: Send + 'static, { self.update(|cx| cx.run_on_main(f)) .unwrap_or_else(|error| Task::ready(Err(error))) } } impl Context for AsyncWindowContext { type EntityContext<'a, T> = ModelContext<'a, T>; type Result = Result; fn entity( &mut self, build_entity: impl FnOnce(&mut Self::EntityContext<'_, T>) -> T, ) -> Result> where T: 'static + Send, { self.app .update_window(self.window, |cx| cx.entity(build_entity)) } fn update_entity( &mut self, handle: &Handle, update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, T>) -> R, ) -> Result { self.app .update_window(self.window, |cx| cx.update_entity(handle, update)) } } impl VisualContext for AsyncWindowContext { type ViewContext<'a, 'w, V> = ViewContext<'a, 'w, V>; fn build_view( &mut self, build_entity: impl FnOnce(&mut Self::ViewContext<'_, '_, V>) -> V, render: impl Fn(&mut V, &mut ViewContext<'_, '_, V>) -> E + Send + 'static, ) -> Self::Result> where E: Component, V: 'static + Send, { self.app .update_window(self.window, |cx| cx.build_view(build_entity, render)) } fn update_view( &mut self, view: &View, update: impl FnOnce(&mut V, &mut Self::ViewContext<'_, '_, V>) -> R, ) -> Self::Result { self.app .update_window(self.window, |cx| cx.update_view(view, update)) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_async_app_context_send_sync() { fn assert_send_sync() {} assert_send_sync::(); } }