Add Context::read_window, WindowHandle::root, and change ViewContext.view() to return a reference

Mikayla created

Change summary

crates/gpui2/src/app.rs               | 16 ++++++++++
crates/gpui2/src/app/async_context.rs | 18 +++++++++++
crates/gpui2/src/app/model_context.rs |  8 +++++
crates/gpui2/src/app/test_context.rs  | 14 +++++++++
crates/gpui2/src/gpui2.rs             |  6 +++
crates/gpui2/src/input.rs             |  2 
crates/gpui2/src/window.rs            | 45 +++++++++++++++++++++++++---
7 files changed, 103 insertions(+), 6 deletions(-)

Detailed changes

crates/gpui2/src/app.rs 🔗

@@ -1007,6 +1007,22 @@ impl Context for AppContext {
         let entity = self.entities.read(handle);
         read(entity, self)
     }
+
+    fn read_window<R>(
+        &self,
+        window: &AnyWindowHandle,
+        read: impl FnOnce(AnyView, &AppContext) -> R,
+    ) -> Result<R> {
+        let window = self
+            .windows
+            .get(window.id)
+            .ok_or_else(|| anyhow!("window not found"))?
+            .as_ref()
+            .unwrap();
+
+        let root_view = window.root_view.clone().unwrap();
+        Ok(read(root_view, self))
+    }
 }
 
 /// These effects are processed at the end of each application update cycle.

crates/gpui2/src/app/async_context.rs 🔗

@@ -66,6 +66,16 @@ impl Context for AsyncAppContext {
         let mut lock = app.borrow_mut();
         lock.update_window(window, f)
     }
+
+    fn read_window<R>(
+        &self,
+        window: &AnyWindowHandle,
+        read: impl FnOnce(AnyView, &AppContext) -> R,
+    ) -> Result<R> {
+        let app = self.app.upgrade().context("app was released")?;
+        let lock = app.borrow();
+        lock.read_window(window, read)
+    }
 }
 
 impl AsyncAppContext {
@@ -250,6 +260,14 @@ impl Context for AsyncWindowContext {
     {
         self.app.read_model(handle, read)
     }
+
+    fn read_window<R>(
+        &self,
+        window: &AnyWindowHandle,
+        read: impl FnOnce(AnyView, &AppContext) -> R,
+    ) -> Result<R> {
+        self.app.read_window(window, read)
+    }
 }
 
 impl VisualContext for AsyncWindowContext {

crates/gpui2/src/app/model_context.rs 🔗

@@ -239,6 +239,14 @@ impl<'a, T> Context for ModelContext<'a, T> {
     {
         self.app.read_model(handle, read)
     }
+
+    fn read_window<R>(
+        &self,
+        window: &AnyWindowHandle,
+        read: impl FnOnce(AnyView, &AppContext) -> R,
+    ) -> Result<R> {
+        self.app.read_window(window, read)
+    }
 }
 
 impl<T> Borrow<AppContext> for ModelContext<'_, T> {

crates/gpui2/src/app/test_context.rs 🔗

@@ -58,6 +58,15 @@ impl Context for TestAppContext {
         let app = self.app.borrow();
         app.read_model(handle, read)
     }
+
+    fn read_window<R>(
+        &self,
+        window: &AnyWindowHandle,
+        read: impl FnOnce(AnyView, &AppContext) -> R,
+    ) -> Result<R> {
+        let app = self.app.borrow();
+        app.read_window(window, read)
+    }
 }
 
 impl TestAppContext {
@@ -146,6 +155,11 @@ impl TestAppContext {
         Some(read(lock.try_global()?, &lock))
     }
 
+    pub fn set_global<G: 'static, R>(&mut self, global: G) {
+        let mut lock = self.app.borrow_mut();
+        lock.set_global(global);
+    }
+
     pub fn update_global<G: 'static, R>(
         &mut self,
         update: impl FnOnce(&mut G, &mut AppContext) -> R,

crates/gpui2/src/gpui2.rs 🔗

@@ -104,6 +104,12 @@ pub trait Context {
     fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Result<T>
     where
         F: FnOnce(AnyView, &mut WindowContext<'_>) -> T;
+
+    fn read_window<R>(
+        &self,
+        window: &AnyWindowHandle,
+        read: impl FnOnce(AnyView, &AppContext) -> R,
+    ) -> Result<R>;
 }
 
 pub trait VisualContext: Context {

crates/gpui2/src/input.rs 🔗

@@ -45,7 +45,7 @@ impl<V: 'static> ElementInputHandler<V> {
     /// containing view.
     pub fn new(element_bounds: Bounds<Pixels>, cx: &mut ViewContext<V>) -> Self {
         ElementInputHandler {
-            view: cx.view(),
+            view: cx.view().clone(),
             element_bounds,
             cx: cx.to_async(),
         }

crates/gpui2/src/window.rs 🔗

@@ -1428,6 +1428,19 @@ impl Context for WindowContext<'_> {
         let entity = self.entities.read(handle);
         read(&*entity, &*self.app)
     }
+
+    fn read_window<R>(
+        &self,
+        window: &AnyWindowHandle,
+        read: impl FnOnce(AnyView, &AppContext) -> R,
+    ) -> Result<R> {
+        if window == &self.window.handle {
+            let root_view = self.window.root_view.clone().unwrap();
+            Ok(read(root_view, self))
+        } else {
+            window.read(self.app, read)
+        }
+    }
 }
 
 impl VisualContext for WindowContext<'_> {
@@ -1747,9 +1760,8 @@ impl<'a, V: 'static> ViewContext<'a, V> {
         }
     }
 
-    // todo!("change this to return a reference");
-    pub fn view(&self) -> View<V> {
-        self.view.clone()
+    pub fn view(&self) -> &View<V> {
+        self.view
     }
 
     pub fn model(&self) -> Model<V> {
@@ -1772,7 +1784,7 @@ impl<'a, V: 'static> ViewContext<'a, V> {
     where
         V: 'static,
     {
-        let view = self.view();
+        let view = self.view().clone();
         self.window_cx.on_next_frame(move |cx| view.update(cx, f));
     }
 
@@ -2170,7 +2182,7 @@ impl<'a, V: 'static> ViewContext<'a, V> {
         &mut self,
         handler: impl Fn(&mut V, &Event, DispatchPhase, &mut ViewContext<V>) + 'static,
     ) {
-        let handle = self.view();
+        let handle = self.view().clone();
         self.window_cx.on_mouse_event(move |event, phase, cx| {
             handle.update(cx, |view, cx| {
                 handler(view, event, phase, cx);
@@ -2244,6 +2256,14 @@ impl<V> Context for ViewContext<'_, V> {
     {
         self.window_cx.read_model(handle, read)
     }
+
+    fn read_window<R>(
+        &self,
+        window: &AnyWindowHandle,
+        read: impl FnOnce(AnyView, &AppContext) -> R,
+    ) -> Result<R> {
+        self.window_cx.read_window(window, read)
+    }
 }
 
 impl<V: 'static> VisualContext for ViewContext<'_, V> {
@@ -2315,6 +2335,14 @@ impl<V: 'static + Render> WindowHandle<V> {
         }
     }
 
+    pub fn root<C: Context>(&self, cx: &C) -> Result<View<V>> {
+        cx.read_window(&self.any_handle, |root_view, _| {
+            root_view
+                .downcast::<V>()
+                .map_err(|_| anyhow!("the type of the window's root view has changed"))
+        })?
+    }
+
     pub fn update<C, R>(
         self,
         cx: &mut C,
@@ -2395,6 +2423,13 @@ impl AnyWindowHandle {
     {
         cx.update_window(self, update)
     }
+
+    pub fn read<C, R>(self, cx: &C, read: impl FnOnce(AnyView, &AppContext) -> R) -> Result<R>
+    where
+        C: Context,
+    {
+        cx.read_window(&self, read)
+    }
 }
 
 #[cfg(any(test, feature = "test-support"))]