diff --git a/crates/gpui2/src/app.rs b/crates/gpui2/src/app.rs index 66c10f901df2e13a09101d06184ab96c7495d6d7..7c62661f337ffc2baf571388c32546ef949906fa 100644 --- a/crates/gpui2/src/app.rs +++ b/crates/gpui2/src/app.rs @@ -333,8 +333,7 @@ impl AppContext { .collect::>(); for dirty_window_handle in dirty_window_ids { - self.update_window(dirty_window_handle, |_, cx| cx.draw()) - .unwrap(); + dirty_window_handle.update(self, |_, cx| cx.draw()).unwrap(); } } @@ -363,25 +362,26 @@ impl AppContext { /// a window blur handler to restore focus to some logical element. fn release_dropped_focus_handles(&mut self) { for window_handle in self.windows() { - self.update_window(window_handle, |_, cx| { - let mut blur_window = false; - let focus = cx.window.focus; - cx.window.focus_handles.write().retain(|handle_id, count| { - if count.load(SeqCst) == 0 { - if focus == Some(handle_id) { - blur_window = true; + window_handle + .update(self, |_, cx| { + let mut blur_window = false; + let focus = cx.window.focus; + cx.window.focus_handles.write().retain(|handle_id, count| { + if count.load(SeqCst) == 0 { + if focus == Some(handle_id) { + blur_window = true; + } + false + } else { + true } - false - } else { - true - } - }); + }); - if blur_window { - cx.blur(); - } - }) - .unwrap(); + if blur_window { + cx.blur(); + } + }) + .unwrap(); } } @@ -403,29 +403,30 @@ impl AppContext { window_handle: AnyWindowHandle, focused: Option, ) { - self.update_window(window_handle, |_, cx| { - if cx.window.focus == focused { - let mut listeners = mem::take(&mut cx.window.focus_listeners); - let focused = - focused.map(|id| FocusHandle::for_id(id, &cx.window.focus_handles).unwrap()); - let blurred = cx - .window - .last_blur - .take() - .unwrap() - .and_then(|id| FocusHandle::for_id(id, &cx.window.focus_handles)); - if focused.is_some() || blurred.is_some() { - let event = FocusEvent { focused, blurred }; - for listener in &listeners { - listener(&event, cx); + window_handle + .update(self, |_, cx| { + if cx.window.focus == focused { + let mut listeners = mem::take(&mut cx.window.focus_listeners); + let focused = focused + .map(|id| FocusHandle::for_id(id, &cx.window.focus_handles).unwrap()); + let blurred = cx + .window + .last_blur + .take() + .unwrap() + .and_then(|id| FocusHandle::for_id(id, &cx.window.focus_handles)); + if focused.is_some() || blurred.is_some() { + let event = FocusEvent { focused, blurred }; + for listener in &listeners { + listener(&event, cx); + } } - } - listeners.extend(cx.window.focus_listeners.drain(..)); - cx.window.focus_listeners = listeners; - } - }) - .ok(); + listeners.extend(cx.window.focus_listeners.drain(..)); + cx.window.focus_listeners = listeners; + } + }) + .ok(); } fn apply_refresh_effect(&mut self) { diff --git a/crates/gpui2/src/gpui2.rs b/crates/gpui2/src/gpui2.rs index 58e1a7fc54034ae9ba3741240b583a259858e881..d016e174fe56d016ce36b1acc66df2400db44a0e 100644 --- a/crates/gpui2/src/gpui2.rs +++ b/crates/gpui2/src/gpui2.rs @@ -77,7 +77,7 @@ use taffy::TaffyLayoutEngine; type AnyBox = Box; pub trait Context { - type WindowContext<'a>: VisualContext; + type WindowContext<'a>: UpdateView; type ModelContext<'a, T>; type Result; @@ -125,6 +125,16 @@ pub trait VisualContext: Context { V: 'static + Send + Render; } +pub trait UpdateView { + type ViewContext<'a, V: 'static>; + + fn update_view( + &mut self, + view: &View, + update: impl FnOnce(&mut V, &mut Self::ViewContext<'_, V>) -> R, + ) -> R; +} + pub trait Entity: Sealed { type Weak: 'static + Send; @@ -268,6 +278,26 @@ impl VisualContext for MainThread { } } +impl UpdateView for MainThread { + type ViewContext<'a, V: 'static> = MainThread>; + + fn update_view( + &mut self, + view: &View, + update: impl FnOnce(&mut V, &mut Self::ViewContext<'_, V>) -> R, + ) -> R { + self.0.update_view(view, |view_state, cx| { + let cx = unsafe { + mem::transmute::< + &mut C::ViewContext<'_, V>, + &mut MainThread>, + >(cx) + }; + update(view_state, cx) + }) + } +} + pub trait BorrowAppContext { fn with_text_style(&mut self, style: TextStyleRefinement, f: F) -> R where diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index 36452bbf034e0517ccb72666b6cb164ab4f2966a..cf83e4e3af2691eb1dded0521540764e50c235b3 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -7,7 +7,7 @@ use crate::{ MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Quad, Render, RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, Style, Subscription, TaffyLayoutEngine, - Task, Underline, UnderlineStyle, View, VisualContext, WeakView, WindowOptions, + Task, Underline, UnderlineStyle, UpdateView, View, VisualContext, WeakView, WindowOptions, SUBPIXEL_VARIANTS, }; use anyhow::{anyhow, Result}; @@ -206,26 +206,28 @@ impl Window { platform_window.on_resize(Box::new({ let mut cx = cx.to_async(); move |content_size, scale_factor| { - cx.update_window(handle, |_, cx| { - cx.window.scale_factor = scale_factor; - cx.window.scene_builder = SceneBuilder::new(); - cx.window.content_size = content_size; - cx.window.display_id = cx - .window - .platform_window - .borrow_on_main_thread() - .display() - .id(); - cx.window.dirty = true; - }) - .log_err(); + handle + .update(&mut cx, |_, cx| { + cx.window.scale_factor = scale_factor; + cx.window.scene_builder = SceneBuilder::new(); + cx.window.content_size = content_size; + cx.window.display_id = cx + .window + .platform_window + .borrow_on_main_thread() + .display() + .id(); + cx.window.dirty = true; + }) + .log_err(); } })); platform_window.on_input({ let mut cx = cx.to_async(); Box::new(move |event| { - cx.update_window(handle, |_, cx| cx.dispatch_event(event)) + handle + .update(&mut cx, |_, cx| cx.dispatch_event(event)) .log_err() .unwrap_or(true) }) @@ -370,9 +372,9 @@ impl<'a> WindowContext<'a> { /// Schedules the given function to be run at the end of the current effect cycle, allowing entities /// that are currently on the stack to be returned to the app. pub fn defer(&mut self, f: impl FnOnce(&mut WindowContext) + 'static + Send) { - let window = self.window.handle; + let handle = self.window.handle; self.app.defer(move |cx| { - cx.update_window(window, |_, cx| f(cx)).ok(); + handle.update(cx, |_, cx| f(cx)).ok(); }); } @@ -391,16 +393,17 @@ impl<'a> WindowContext<'a> { self.app.event_listeners.insert( entity_id, Box::new(move |event, cx| { - cx.update_window(window_handle, |_, cx| { - if let Some(handle) = E::upgrade_from(&entity) { - let event = event.downcast_ref().expect("invalid event type"); - on_event(handle, event, cx); - true - } else { - false - } - }) - .unwrap_or(false) + window_handle + .update(cx, |_, cx| { + if let Some(handle) = E::upgrade_from(&entity) { + let event = event.downcast_ref().expect("invalid event type"); + on_event(handle, event, cx); + true + } else { + false + } + }) + .unwrap_or(false) }), ) } @@ -422,7 +425,7 @@ impl<'a> WindowContext<'a> { } else { let handle = self.window.handle; self.app - .run_on_main(move |cx| cx.update_window(handle, |_, cx| f(cx))) + .run_on_main(move |cx| handle.update(cx, |_, cx| f(cx))) } } @@ -1182,7 +1185,7 @@ impl<'a> WindowContext<'a> { let window_handle = self.window.handle; self.global_observers.insert( TypeId::of::(), - Box::new(move |cx| cx.update_window(window_handle, |_, cx| f(cx)).is_ok()), + Box::new(move |cx| window_handle.update(cx, |_, cx| f(cx)).is_ok()), ) } @@ -1304,7 +1307,7 @@ impl Context for WindowContext<'_> { let root_view = self.window.root_view.clone().unwrap(); Ok(update(root_view, self)) } else { - self.app.update_window(window, update) + window.update(self.app, update) } } } @@ -1361,6 +1364,18 @@ impl VisualContext for WindowContext<'_> { } } +impl UpdateView for WindowContext<'_> { + type ViewContext<'a, V: 'static> = ViewContext<'a, V>; + + fn update_view( + &mut self, + view: &View, + update: impl FnOnce(&mut V, &mut Self::ViewContext<'_, V>) -> R, + ) -> R { + VisualContext::update_view(self, view, update) + } +} + impl<'a> std::ops::Deref for WindowContext<'a> { type Target = AppContext; @@ -1649,15 +1664,16 @@ impl<'a, V: 'static> ViewContext<'a, V> { self.app.observers.insert( entity_id, Box::new(move |cx| { - cx.update_window(window_handle, |_, cx| { - if let Some(handle) = E::upgrade_from(&entity) { - view.update(cx, |this, cx| on_notify(this, handle, cx)) - .is_ok() - } else { - false - } - }) - .unwrap_or(false) + window_handle + .update(cx, |_, cx| { + if let Some(handle) = E::upgrade_from(&entity) { + view.update(cx, |this, cx| on_notify(this, handle, cx)) + .is_ok() + } else { + false + } + }) + .unwrap_or(false) }), ) } @@ -1678,16 +1694,17 @@ impl<'a, V: 'static> ViewContext<'a, V> { self.app.event_listeners.insert( entity_id, Box::new(move |event, cx| { - cx.update_window(window_handle, |_, cx| { - if let Some(handle) = E::upgrade_from(&handle) { - let event = event.downcast_ref().expect("invalid event type"); - view.update(cx, |this, cx| on_event(this, handle, event, cx)) - .is_ok() - } else { - false - } - }) - .unwrap_or(false) + window_handle + .update(cx, |_, cx| { + if let Some(handle) = E::upgrade_from(&handle) { + let event = event.downcast_ref().expect("invalid event type"); + view.update(cx, |this, cx| on_event(this, handle, event, cx)) + .is_ok() + } else { + false + } + }) + .unwrap_or(false) }), ) } @@ -1702,7 +1719,7 @@ impl<'a, V: 'static> ViewContext<'a, V> { Box::new(move |this, cx| { let this = this.downcast_mut().expect("invalid entity type"); // todo!("are we okay with silently swallowing the error?") - let _ = cx.update_window(window_handle, |_, cx| on_release(this, cx)); + let _ = window_handle.update(cx, |_, cx| on_release(this, cx)); }), ) } @@ -1724,7 +1741,7 @@ impl<'a, V: 'static> ViewContext<'a, V> { entity_id, Box::new(move |entity, cx| { let entity = entity.downcast_mut().expect("invalid entity type"); - let _ = cx.update_window(window_handle, |_, cx| { + let _ = window_handle.update(cx, |_, cx| { view.update(cx, |this, cx| on_release(this, entity, cx)) }); }), @@ -1887,10 +1904,9 @@ impl<'a, V: 'static> ViewContext<'a, V> { self.global_observers.insert( TypeId::of::(), Box::new(move |cx| { - cx.update_window(window_handle, |_, cx| { - view.update(cx, |view, cx| f(view, cx)).is_ok() - }) - .unwrap_or(false) + window_handle + .update(cx, |_, cx| view.update(cx, |view, cx| f(view, cx)).is_ok()) + .unwrap_or(false) }), ) } @@ -1978,7 +1994,7 @@ impl VisualContext for ViewContext<'_, V> { view: &View, update: impl FnOnce(&mut V2, &mut Self::ViewContext<'_, V2>) -> R, ) -> Self::Result { - self.window_cx.update_view(view, update) + VisualContext::update_view(&mut self.window_cx, view, update) } fn replace_root_view( @@ -2034,23 +2050,20 @@ impl WindowHandle { } } - pub fn update_root( + pub fn update( &self, cx: &mut C, - update: impl FnOnce( - &mut V, - &mut as VisualContext>::ViewContext<'_, V>, - ) -> R, + update: impl FnOnce(&mut V, &mut as UpdateView>::ViewContext<'_, V>) -> R, ) -> Result where - C: for<'a> Context = W>, - W: VisualContext = R>, + C: Context, { cx.update_window(self.any_handle, |root_view, cx| { let view = root_view .downcast::() .map_err(|_| anyhow!("the type of the window's root view has changed"))?; - Ok(view.update(cx, update)) + + Ok(cx.update_view(&view, update)) })? } } @@ -2107,6 +2120,17 @@ impl AnyWindowHandle { None } } + + pub fn update( + &self, + cx: &mut C, + update: impl FnOnce(AnyView, &mut C::WindowContext<'_>) -> R, + ) -> Result + where + C: Context, + { + cx.update_window(*self, update) + } } #[cfg(any(test, feature = "test-support"))] diff --git a/crates/workspace2/src/workspace2.rs b/crates/workspace2/src/workspace2.rs index 2db525a26de86448939ce0af6c21437abe81afac..68e5b058e9b1a97bb3d5efadc96be77665190ce9 100644 --- a/crates/workspace2/src/workspace2.rs +++ b/crates/workspace2/src/workspace2.rs @@ -428,10 +428,10 @@ pub struct AppState { pub build_window_options: fn(Option, Option, &mut MainThread) -> WindowOptions, pub initialize_workspace: fn( - WeakView, + WindowHandle, bool, Arc, - AsyncWindowContext, + AsyncAppContext, ) -> Task>, pub node_runtime: Arc, } @@ -874,12 +874,14 @@ impl Workspace { let options = cx.update(|cx| (app_state.build_window_options)(bounds, display, cx))?; cx.open_window(options, |cx| { - Workspace::new(workspace_id, project_handle.clone(), app_state.clone(), cx) + cx.build_view(|cx| { + Workspace::new(workspace_id, project_handle.clone(), app_state.clone(), cx) + }) })? }; (app_state.initialize_workspace)( - workspace.downgrade(), + window, serialized_workspace.is_some(), app_state.clone(), cx.clone(), @@ -887,10 +889,7 @@ impl Workspace { .await .log_err(); - window.update_root(&mut cx, |_, cx| { - // todo!() - // cx.activate_window() - }); + window.update(&mut cx, |_, cx| cx.activate_window()); let workspace = workspace.downgrade(); notify_if_database_failed(&workspace, &mut cx); @@ -3976,7 +3975,7 @@ impl WorkspaceStore { let mut response = proto::FollowResponse::default(); for workspace in &this.workspaces { workspace - .update_root(cx, |workspace, cx| { + .update(cx, |workspace, cx| { let handler_response = workspace.handle_follow(follower.project_id, cx); if response.views.is_empty() { response.views = handler_response.views;