From 723d809c3fa8ae0a434701cdb526fbf6f5a7bb9e Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 16 Mar 2026 08:22:19 -0700 Subject: [PATCH] Require that drawer views be focusable --- 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(-) diff --git a/crates/agent_ui/src/agent_panel.rs b/crates/agent_ui/src/agent_panel.rs index 4526a87e19489b588ce22875803a2403d642591c..57507195a5d94be368d85e44db7f22eb6eef0ca2 100644 --- a/crates/agent_ui/src/agent_panel.rs +++ b/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::() 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::(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::(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::(window, cx); panel }); diff --git a/crates/agent_ui/src/connection_view.rs b/crates/agent_ui/src/connection_view.rs index 4798b4579ba6f05531845f7757e1e36085884a3e..880c3bd6d87a98790a12632b8dc038432968adcb 100644 --- a/crates/agent_ui/src/connection_view.rs +++ b/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::(window, cx); diff --git a/crates/agent_ui/src/threads_panel.rs b/crates/agent_ui/src/threads_panel.rs index af29e1de23a6ecf1e6794170d1c17cb276e5cdd1..f678f75525c635b14788314f0b121912e01ea13b 100644 --- a/crates/agent_ui/src/threads_panel.rs +++ b/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 }) } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 57cd92f129256585de277865e60ce45166cbbd39..7cc91d7e68ffcf5f73fdbe5661bcf5261cea0f7b 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -7007,13 +7007,23 @@ impl Workspace { ) } - pub fn set_left_drawer(&mut self, view: AnyView, cx: &mut Context) { - self.left_drawer = Some(Drawer::new(view)); + pub fn set_left_drawer( + &mut self, + view: Entity, + cx: &mut Context, + ) { + 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.right_drawer = Some(Drawer::new(view)); + pub fn set_right_drawer( + &mut self, + view: Entity, + cx: &mut Context, + ) { + 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, } impl Drawer { - fn new(view: AnyView) -> Self { + fn new(view: AnyView, focus_handle: FocusHandle) -> Self { Self { view, + focus_handle, open: true, custom_width: None, } diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index b63d79e22ded76675ec1325b8d2047e6f3854527..029ebb9807ed68922a5dbfca643b2e81c0e24641 100644 --- a/crates/zed/src/zed.rs +++ b/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")