Fix cx.windows() to return borrowed windows (#8086)

Conrad Irwin created

Fixes #8068

Release Notes:

- Fixed an error message when joining a project twice
([#8068](https://github.com/zed-industries/zed/issues/8068)).

Change summary

crates/gpui/src/app.rs    |  9 +++++++--
crates/gpui/src/window.rs | 10 +++++-----
crates/zed/src/zed.rs     |  4 ++--
3 files changed, 14 insertions(+), 9 deletions(-)

Detailed changes

crates/gpui/src/app.rs 🔗

@@ -226,6 +226,7 @@ pub struct AppContext {
     pub(crate) entities: EntityMap,
     pub(crate) new_view_observers: SubscriberSet<TypeId, NewViewListener>,
     pub(crate) windows: SlotMap<WindowId, Option<Window>>,
+    pub(crate) window_handles: FxHashMap<WindowId, AnyWindowHandle>,
     pub(crate) keymap: Rc<RefCell<Keymap>>,
     pub(crate) global_action_listeners:
         FxHashMap<TypeId, Vec<Rc<dyn Fn(&dyn Any, DispatchPhase, &mut Self)>>>,
@@ -285,6 +286,7 @@ impl AppContext {
                 globals_by_type: FxHashMap::default(),
                 entities,
                 new_view_observers: SubscriberSet::new(),
+                window_handles: FxHashMap::default(),
                 windows: SlotMap::with_key(),
                 keymap: Rc::new(RefCell::new(Keymap::default())),
                 global_action_listeners: FxHashMap::default(),
@@ -324,6 +326,7 @@ impl AppContext {
         }
 
         self.windows.clear();
+        self.window_handles.clear();
         self.flush_effects();
 
         let futures = futures::future::join_all(futures);
@@ -468,8 +471,8 @@ impl AppContext {
     /// To find all windows of a given type, you could filter on
     pub fn windows(&self) -> Vec<AnyWindowHandle> {
         self.windows
-            .values()
-            .filter_map(|window| Some(window.as_ref()?.handle))
+            .keys()
+            .flat_map(|window_id| self.window_handles.get(&window_id).copied())
             .collect()
     }
 
@@ -492,6 +495,7 @@ impl AppContext {
             let mut window = Window::new(handle.into(), options, cx);
             let root_view = build_root_view(&mut WindowContext::new(cx, &mut window));
             window.root_view.replace(root_view.into());
+            cx.window_handles.insert(id, window.handle);
             cx.windows.get_mut(id).unwrap().replace(window);
             handle
         })
@@ -1245,6 +1249,7 @@ impl Context for AppContext {
             let result = update(root_view, &mut WindowContext::new(cx, &mut window));
 
             if window.removed {
+                cx.window_handles.remove(&handle.id);
                 cx.windows.remove(handle.id);
             } else {
                 cx.windows

crates/gpui/src/window.rs 🔗

@@ -2528,11 +2528,11 @@ impl<V: 'static + Render> WindowHandle<V> {
 
     /// Check if this window is 'active'.
     ///
-    /// Will return `None` if the window is closed.
-    pub fn is_active(&self, cx: &AppContext) -> Option<bool> {
-        cx.windows
-            .get(self.id)
-            .and_then(|window| window.as_ref().map(|window| window.active.get()))
+    /// Will return `None` if the window is closed or currently
+    /// borrowed.
+    pub fn is_active(&self, cx: &mut AppContext) -> Option<bool> {
+        cx.update_window(self.any_handle, |_, cx| cx.is_window_active())
+            .ok()
     }
 }
 

crates/zed/src/zed.rs 🔗

@@ -422,8 +422,8 @@ fn quit(_: &Quit, cx: &mut AppContext) {
 
         // If multiple windows have unsaved changes, and need a save prompt,
         // prompt in the active window before switching to a different window.
-        cx.update(|cx| {
-            workspace_windows.sort_by_key(|window| window.is_active(&cx) == Some(false));
+        cx.update(|mut cx| {
+            workspace_windows.sort_by_key(|window| window.is_active(&mut cx) == Some(false));
         })
         .log_err();