Require that drawer views be focusable

Max Brunsfeld created

Change summary

crates/agent_ui/src/agent_panel.rs     | 19 +++++++++----------
crates/agent_ui/src/connection_view.rs |  2 +-
crates/agent_ui/src/threads_panel.rs   |  2 +-
crates/workspace/src/workspace.rs      | 23 ++++++++++++++++++-----
crates/zed/src/zed.rs                  |  4 ++--
5 files changed, 31 insertions(+), 19 deletions(-)

Detailed changes

crates/agent_ui/src/agent_panel.rs 🔗

@@ -65,9 +65,9 @@ use extension_host::ExtensionStore;
 use fs::Fs;
 use git::repository::validate_worktree_directory;
 use gpui::{
-    Action, Animation, AnimationExt, AnyElement, AnyView, App, AsyncWindowContext, ClipboardItem,
-    Corner, DismissEvent, Entity, EventEmitter, ExternalPaths, FocusHandle, Focusable, KeyContext,
-    Pixels, Subscription, Task, UpdateGlobal, WeakEntity, prelude::*, pulsating_between,
+    Action, Animation, AnimationExt, AnyElement, App, AsyncWindowContext, ClipboardItem, Corner,
+    DismissEvent, Entity, EventEmitter, ExternalPaths, FocusHandle, Focusable, KeyContext, Pixels,
+    Subscription, Task, UpdateGlobal, WeakEntity, prelude::*, pulsating_between,
 };
 use language::LanguageRegistry;
 use language_model::{ConfigurationError, LanguageModelRegistry};
@@ -423,19 +423,18 @@ pub fn init(cx: &mut App) {
                     let Some(panel) = workspace.drawer::<AgentPanel>() else {
                         return;
                     };
-                    let panel_view: AnyView = panel.into();
                     let dock_position = agent_panel_dock_position(cx);
                     match dock_position {
                         DockPosition::Right => {
                             if workspace.right_drawer_view().is_none() {
-                                workspace.set_right_drawer(panel_view, cx);
+                                workspace.set_right_drawer(panel, cx);
                             } else {
                                 workspace.toggle_drawer::<AgentPanel>(cx);
                             }
                         }
                         DockPosition::Left | DockPosition::Bottom => {
                             if workspace.left_drawer_view().is_none() {
-                                workspace.set_left_drawer(panel_view, cx);
+                                workspace.set_left_drawer(panel, cx);
                             } else {
                                 workspace.toggle_drawer::<AgentPanel>(cx);
                             }
@@ -5139,7 +5138,7 @@ mod tests {
             let text_thread_store = cx.new(|cx| TextThreadStore::fake(project.clone(), cx));
             let panel =
                 cx.new(|cx| AgentPanel::new(workspace, text_thread_store, None, window, cx));
-            workspace.set_left_drawer(panel.clone().into(), cx);
+            workspace.set_left_drawer(panel.clone(), cx);
         });
 
         cx.run_until_parked();
@@ -5651,7 +5650,7 @@ mod tests {
             let text_thread_store = cx.new(|cx| TextThreadStore::fake(project.clone(), cx));
             let panel =
                 cx.new(|cx| AgentPanel::new(workspace, text_thread_store, None, window, cx));
-            workspace.set_left_drawer(panel.clone().into(), cx);
+            workspace.set_left_drawer(panel.clone(), cx);
             panel
         });
 
@@ -5761,7 +5760,7 @@ mod tests {
             let text_thread_store = cx.new(|cx| TextThreadStore::fake(project.clone(), cx));
             let panel =
                 cx.new(|cx| AgentPanel::new(workspace, text_thread_store, None, window, cx));
-            workspace.set_left_drawer(panel.clone().into(), cx);
+            workspace.set_left_drawer(panel.clone(), cx);
             panel
         });
 
@@ -5846,7 +5845,7 @@ mod tests {
             let text_thread_store = cx.new(|cx| TextThreadStore::fake(project.clone(), cx));
             let panel =
                 cx.new(|cx| AgentPanel::new(workspace, text_thread_store, None, window, cx));
-            workspace.set_left_drawer(panel.clone().into(), cx);
+            workspace.set_left_drawer(panel.clone(), cx);
             workspace.focus_drawer::<AgentPanel>(window, cx);
             panel
         });

crates/agent_ui/src/connection_view.rs 🔗

@@ -3490,7 +3490,7 @@ pub(crate) mod tests {
                 cx.new(|cx| TextThreadStore::fake(workspace.project().clone(), cx));
             let panel =
                 cx.new(|cx| crate::AgentPanel::new(workspace, text_thread_store, None, window, cx));
-            workspace.set_left_drawer(panel.clone().into(), cx);
+            workspace.set_left_drawer(panel.clone(), cx);
 
             // Open the dock and activate the agent panel so it's visible
             workspace.focus_drawer::<crate::AgentPanel>(window, cx);

crates/agent_ui/src/threads_panel.rs 🔗

@@ -3171,7 +3171,7 @@ mod tests {
         workspace.update_in(cx, |workspace, window, cx| {
             let text_thread_store = cx.new(|cx| TextThreadStore::fake(project.clone(), cx));
             let panel = cx.new(|cx| AgentPanel::test_new(workspace, text_thread_store, window, cx));
-            workspace.set_left_drawer(panel.clone().into(), cx);
+            workspace.set_left_drawer(panel.clone(), cx);
             panel
         })
     }

crates/workspace/src/workspace.rs 🔗

@@ -7007,13 +7007,23 @@ impl Workspace {
         )
     }
 
-    pub fn set_left_drawer(&mut self, view: AnyView, cx: &mut Context<Self>) {
-        self.left_drawer = Some(Drawer::new(view));
+    pub fn set_left_drawer<V: Render + Focusable + 'static>(
+        &mut self,
+        view: Entity<V>,
+        cx: &mut Context<Self>,
+    ) {
+        let focus_handle = view.focus_handle(cx);
+        self.left_drawer = Some(Drawer::new(view.into(), focus_handle));
         cx.notify();
     }
 
-    pub fn set_right_drawer(&mut self, view: AnyView, cx: &mut Context<Self>) {
-        self.right_drawer = Some(Drawer::new(view));
+    pub fn set_right_drawer<V: Render + Focusable + 'static>(
+        &mut self,
+        view: Entity<V>,
+        cx: &mut Context<Self>,
+    ) {
+        let focus_handle = view.focus_handle(cx);
+        self.right_drawer = Some(Drawer::new(view.into(), focus_handle));
         cx.notify();
     }
 
@@ -7273,6 +7283,7 @@ impl Workspace {
         };
 
         let base = div()
+            .track_focus(&drawer.focus_handle)
             .flex()
             .flex_col()
             .overflow_hidden()
@@ -7910,14 +7921,16 @@ pub enum DrawerPosition {
 
 pub struct Drawer {
     view: AnyView,
+    focus_handle: FocusHandle,
     open: bool,
     custom_width: Option<Pixels>,
 }
 
 impl Drawer {
-    fn new(view: AnyView) -> Self {
+    fn new(view: AnyView, focus_handle: FocusHandle) -> Self {
         Self {
             view,
+            focus_handle,
             open: true,
             custom_width: None,
         }

crates/zed/src/zed.rs 🔗

@@ -685,10 +685,10 @@ fn setup_or_teardown_ai_panels(
                     workspace.add_panel(sidebar_panel, window, cx);
                     match position {
                         workspace::dock::DockPosition::Left => {
-                            workspace.set_left_drawer(agent_drawer.into(), cx)
+                            workspace.set_left_drawer(agent_drawer, cx)
                         }
                         workspace::dock::DockPosition::Right => {
-                            workspace.set_right_drawer(agent_drawer.into(), cx)
+                            workspace.set_right_drawer(agent_drawer, cx)
                         }
                         workspace::dock::DockPosition::Bottom => {
                             unreachable!("drawers cannot go on the bottom")