diff --git a/crates/gpui2/src/app.rs b/crates/gpui2/src/app.rs index d39083c1aa..a4d574abcf 100644 --- a/crates/gpui2/src/app.rs +++ b/crates/gpui2/src/app.rs @@ -17,8 +17,8 @@ use crate::{ AppMetadata, AssetSource, ClipboardItem, Context, DispatchPhase, DisplayId, Executor, FocusEvent, FocusHandle, FocusId, KeyBinding, Keymap, LayoutId, MainThread, MainThreadOnly, Pixels, Platform, Point, SharedString, SubscriberSet, Subscription, SvgRenderer, Task, - TextStyle, TextStyleRefinement, TextSystem, View, Window, WindowContext, WindowHandle, - WindowId, + TextStyle, TextStyleRefinement, TextSystem, View, ViewContext, Window, WindowContext, + WindowHandle, WindowId, }; use anyhow::{anyhow, Result}; use collections::{HashMap, HashSet, VecDeque}; @@ -303,6 +303,20 @@ impl AppContext { }) } + pub fn update_window_root( + &mut self, + handle: &WindowHandle, + update: impl FnOnce(&mut V, &mut ViewContext<'_, '_, V>) -> R, + ) -> Result + where + V: 'static, + { + self.update_window(handle.any_handle, |cx| { + let root_view = cx.window.root_view.as_ref().unwrap().downcast().unwrap(); + root_view.update(cx, update) + }) + } + pub(crate) fn push_effect(&mut self, effect: Effect) { match &effect { Effect::Notify { emitter } => { @@ -841,6 +855,20 @@ impl MainThread { }) } + pub fn update_window_root( + &mut self, + handle: &WindowHandle, + update: impl FnOnce(&mut V, &mut MainThread>) -> R, + ) -> Result + where + V: 'static, + { + self.update_window(handle.any_handle, |cx| { + let root_view = cx.window.root_view.as_ref().unwrap().downcast().unwrap(); + root_view.update(cx, update) + }) + } + /// Opens a new window with the given option and the root view returned by the given function. /// The function is invoked with a `WindowContext`, which can be used to interact with window-specific /// functionality. diff --git a/crates/gpui2/src/app/async_context.rs b/crates/gpui2/src/app/async_context.rs index 6615fd535e..5998917e52 100644 --- a/crates/gpui2/src/app/async_context.rs +++ b/crates/gpui2/src/app/async_context.rs @@ -1,6 +1,6 @@ use crate::{ - AnyWindowHandle, AppContext, Context, Executor, Handle, MainThread, ModelContext, Result, Task, - WindowContext, + AnyWindowHandle, AppContext, Component, Context, Executor, Handle, MainThread, ModelContext, + Result, Task, View, ViewContext, VisualContext, WindowContext, WindowHandle, }; use anyhow::Context as _; use derive_more::{Deref, DerefMut}; @@ -78,6 +78,19 @@ impl AsyncAppContext { 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, @@ -245,6 +258,32 @@ impl Context for AsyncWindowContext { } } +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::*; diff --git a/crates/gpui2/src/view.rs b/crates/gpui2/src/view.rs index cf7441249f..e3e89b2add 100644 --- a/crates/gpui2/src/view.rs +++ b/crates/gpui2/src/view.rs @@ -1,11 +1,12 @@ use crate::{ AnyBox, AnyElement, AvailableSpace, BorrowWindow, Bounds, Component, Element, ElementId, - EntityId, Handle, LayoutId, Pixels, Size, ViewContext, VisualContext, WeakHandle, + EntityId, Flatten, Handle, LayoutId, Pixels, Size, ViewContext, VisualContext, WeakHandle, WindowContext, }; use anyhow::{Context, Result}; use parking_lot::Mutex; use std::{ + any::Any, marker::PhantomData, sync::{Arc, Weak}, }; @@ -128,13 +129,17 @@ impl WeakView { Some(View { state, render }) } - pub fn update( + pub fn update( &self, - cx: &mut WindowContext, - f: impl FnOnce(&mut V, &mut ViewContext) -> R, - ) -> Result { + cx: &mut C, + f: impl FnOnce(&mut V, &mut C::ViewContext<'_, '_, V>) -> R, + ) -> Result + where + C: VisualContext, + Result>: Flatten, + { let view = self.upgrade().context("error upgrading view")?; - Ok(view.update(cx, f)) + Ok(view.update(cx, f)).flatten() } } @@ -201,15 +206,16 @@ trait ViewObject: Send + Sync { fn initialize(&self, cx: &mut WindowContext) -> AnyBox; fn layout(&self, element: &mut AnyBox, cx: &mut WindowContext) -> LayoutId; fn paint(&self, bounds: Bounds, element: &mut AnyBox, cx: &mut WindowContext); + fn as_any(&self) -> &dyn Any; } impl ViewObject for View { fn entity_id(&self) -> EntityId { - self.state.entity_id() + self.state.entity_id } fn initialize(&self, cx: &mut WindowContext) -> AnyBox { - cx.with_element_id(self.entity_id(), |_global_id, cx| { + cx.with_element_id(self.state.entity_id, |_global_id, cx| { self.update(cx, |state, cx| { let mut any_element = Box::new((self.render.lock())(state, cx)); any_element.initialize(state, cx); @@ -219,7 +225,7 @@ impl ViewObject for View { } fn layout(&self, element: &mut AnyBox, cx: &mut WindowContext) -> LayoutId { - cx.with_element_id(self.entity_id(), |_global_id, cx| { + cx.with_element_id(self.state.entity_id, |_global_id, cx| { self.update(cx, |state, cx| { let element = element.downcast_mut::>().unwrap(); element.layout(state, cx) @@ -228,19 +234,27 @@ impl ViewObject for View { } fn paint(&self, _: Bounds, element: &mut AnyBox, cx: &mut WindowContext) { - cx.with_element_id(self.entity_id(), |_global_id, cx| { + cx.with_element_id(self.state.entity_id, |_global_id, cx| { self.update(cx, |state, cx| { let element = element.downcast_mut::>().unwrap(); element.paint(state, cx); }); }); } + + fn as_any(&self) -> &dyn Any { + self + } } #[derive(Clone)] pub struct AnyView(Arc); impl AnyView { + pub fn downcast(&self) -> Option> { + self.0.as_any().downcast_ref().cloned() + } + pub(crate) fn draw(&self, available_space: Size, cx: &mut WindowContext) { let mut rendered_element = self.0.initialize(cx); let layout_id = self.0.layout(&mut rendered_element, cx); diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index a197b0b615..e89c713d93 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -1237,16 +1237,6 @@ impl<'a, 'w> WindowContext<'a, 'w> { } } -impl<'a, 'w> MainThread> { - fn platform_window(&self) -> &dyn PlatformWindow { - self.window.platform_window.borrow_on_main_thread().as_ref() - } - - pub fn activate_window(&self) { - self.platform_window().activate(); - } -} - impl Context for WindowContext<'_, '_> { type EntityContext<'a, T> = ModelContext<'a, T>; type Result = T; @@ -1864,6 +1854,16 @@ where } } +impl<'a, 'w, V: 'static> MainThread> { + fn platform_window(&self) -> &dyn PlatformWindow { + self.window.platform_window.borrow_on_main_thread().as_ref() + } + + pub fn activate_window(&self) { + self.platform_window().activate(); + } +} + impl<'a, 'w, V> Context for ViewContext<'a, 'w, V> { type EntityContext<'b, U> = ModelContext<'b, U>; type Result = U; @@ -1934,38 +1934,40 @@ impl WindowId { } } -#[derive(PartialEq, Eq)] +#[derive(PartialEq, Eq, Deref, DerefMut)] pub struct WindowHandle { - id: WindowId, + #[deref] + #[deref_mut] + pub(crate) any_handle: AnyWindowHandle, state_type: PhantomData, } -impl Copy for WindowHandle {} +impl Copy for WindowHandle {} -impl Clone for WindowHandle { +impl Clone for WindowHandle { fn clone(&self) -> Self { WindowHandle { - id: self.id, + any_handle: self.any_handle, state_type: PhantomData, } } } -impl WindowHandle { +impl WindowHandle { pub fn new(id: WindowId) -> Self { WindowHandle { - id, + any_handle: AnyWindowHandle { + id, + state_type: TypeId::of::(), + }, state_type: PhantomData, } } } -impl Into for WindowHandle { +impl Into for WindowHandle { fn into(self) -> AnyWindowHandle { - AnyWindowHandle { - id: self.id, - state_type: TypeId::of::(), - } + self.any_handle } } @@ -1979,6 +1981,17 @@ impl AnyWindowHandle { pub fn window_id(&self) -> WindowId { self.id } + + pub fn downcast(&self) -> Option> { + if TypeId::of::() == self.state_type { + Some(WindowHandle { + any_handle: *self, + state_type: PhantomData, + }) + } else { + None + } + } } #[cfg(any(test, feature = "test-support"))] diff --git a/crates/workspace2/src/workspace2.rs b/crates/workspace2/src/workspace2.rs index 36dd26eb3c..06f6d65e2e 100644 --- a/crates/workspace2/src/workspace2.rs +++ b/crates/workspace2/src/workspace2.rs @@ -4103,25 +4103,28 @@ pub struct Workspace { pub async fn activate_workspace_for_project( cx: &mut AsyncAppContext, predicate: impl Fn(&Project, &AppContext) -> bool + Send + 'static, -) -> Option> { +) -> Option> { cx.run_on_main(move |cx| { for window in cx.windows() { - let handle = cx - .update_window(window, |cx| { - if let Some(workspace_handle) = cx.root_view()?.downcast::() { - let project = workspace_handle.read(cx).project.clone(); - if project.update(cx, |project, cx| predicate(project, cx)) { - cx.activate_window(); - return Some(workspace_handle.clone()); - } + let Some(workspace) = window.downcast::() else { + continue; + }; + + let predicate = cx + .update_window_root(&workspace, |workspace, cx| { + let project = workspace.project.read(cx); + if predicate(project, cx) { + cx.activate_window(); + true + } else { + false } - None }) .log_err() - .flatten(); + .unwrap_or(false); - if let Some(handle) = handle { - return Some(handle.downgrade()); + if predicate { + return Some(workspace); } }