From dcbff982ad2e8dc16722ea5383622a29bd487ab0 Mon Sep 17 00:00:00 2001 From: Cole Miller Date: Mon, 30 Dec 2024 09:43:17 -0500 Subject: [PATCH] Decide which panel should be active for a dock based on ordering panels (#22346) This means that `workspace::ToggleRightDock` will open the assistant if no right-dock panel has been manually activated, instead of the chat as before. Also cleans up the `active_panel_index` logic a bit. cc @nathansobo Release Notes: - Make `workspace::ToggleRightDock` open the assistant panel if no right-dock panel has yet been activated --- crates/assistant/src/assistant_panel.rs | 4 + crates/assistant2/src/assistant_panel.rs | 4 + crates/collab_ui/src/chat_panel.rs | 5 ++ crates/collab_ui/src/collab_panel.rs | 4 + crates/collab_ui/src/notification_panel.rs | 4 + crates/git_ui/src/git_panel.rs | 4 + crates/outline_panel/src/outline_panel.rs | 4 + crates/project_panel/src/project_panel.rs | 4 + crates/terminal_view/src/terminal_panel.rs | 4 + crates/workspace/src/dock.rs | 95 ++++++++++++++-------- crates/workspace/src/workspace.rs | 12 +-- crates/zed/src/zed.rs | 6 +- 12 files changed, 109 insertions(+), 41 deletions(-) diff --git a/crates/assistant/src/assistant_panel.rs b/crates/assistant/src/assistant_panel.rs index ebf8326a68..e75a9d88e0 100644 --- a/crates/assistant/src/assistant_panel.rs +++ b/crates/assistant/src/assistant_panel.rs @@ -1458,6 +1458,10 @@ impl Panel for AssistantPanel { fn toggle_action(&self) -> Box { Box::new(ToggleFocus) } + + fn activation_priority(&self) -> u32 { + 4 + } } impl EventEmitter for AssistantPanel {} diff --git a/crates/assistant2/src/assistant_panel.rs b/crates/assistant2/src/assistant_panel.rs index a42e35cabe..522d2b4eea 100644 --- a/crates/assistant2/src/assistant_panel.rs +++ b/crates/assistant2/src/assistant_panel.rs @@ -286,6 +286,10 @@ impl Panel for AssistantPanel { fn toggle_action(&self) -> Box { Box::new(ToggleFocus) } + + fn activation_priority(&self) -> u32 { + 3 + } } impl AssistantPanel { diff --git a/crates/collab_ui/src/chat_panel.rs b/crates/collab_ui/src/chat_panel.rs index 4e6d8940b6..6ac6f007ed 100644 --- a/crates/collab_ui/src/chat_panel.rs +++ b/crates/collab_ui/src/chat_panel.rs @@ -1141,6 +1141,7 @@ impl Panel for ChatPanel { ChatPanelButton::WhenInCall => ActiveCall::global(cx) .read(cx) .room() + .filter(|room| room.read(cx).contains_guests()) .map(|_| ui::IconName::MessageBubbles), } } @@ -1159,6 +1160,10 @@ impl Panel for ChatPanel { .room() .is_some_and(|room| room.read(cx).contains_guests()) } + + fn activation_priority(&self) -> u32 { + 7 + } } impl EventEmitter for ChatPanel {} diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index 1f60f65b41..b9fa39ed96 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -2763,6 +2763,10 @@ impl Panel for CollabPanel { fn persistent_name() -> &'static str { "CollabPanel" } + + fn activation_priority(&self) -> u32 { + 6 + } } impl FocusableView for CollabPanel { diff --git a/crates/collab_ui/src/notification_panel.rs b/crates/collab_ui/src/notification_panel.rs index 005220471a..574316fa4a 100644 --- a/crates/collab_ui/src/notification_panel.rs +++ b/crates/collab_ui/src/notification_panel.rs @@ -731,6 +731,10 @@ impl Panel for NotificationPanel { fn toggle_action(&self) -> Box { Box::new(ToggleFocus) } + + fn activation_priority(&self) -> u32 { + 8 + } } pub struct NotificationToast { diff --git a/crates/git_ui/src/git_panel.rs b/crates/git_ui/src/git_panel.rs index 4dca33e3ad..be3f4485fb 100644 --- a/crates/git_ui/src/git_panel.rs +++ b/crates/git_ui/src/git_panel.rs @@ -1237,6 +1237,10 @@ impl Panel for GitPanel { fn toggle_action(&self) -> Box { Box::new(ToggleFocus) } + + fn activation_priority(&self) -> u32 { + 2 + } } fn diff_display_editor(cx: &mut WindowContext) -> View { diff --git a/crates/outline_panel/src/outline_panel.rs b/crates/outline_panel/src/outline_panel.rs index 6ac58fab15..d43b76671a 100644 --- a/crates/outline_panel/src/outline_panel.rs +++ b/crates/outline_panel/src/outline_panel.rs @@ -4468,6 +4468,10 @@ impl Panel for OutlinePanel { }) .detach() } + + fn activation_priority(&self) -> u32 { + 5 + } } impl FocusableView for OutlinePanel { diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 483d64d8eb..c2afc7aefd 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -4252,6 +4252,10 @@ impl Panel for ProjectPanel { .map_or(false, |entry| entry.is_dir()) }) } + + fn activation_priority(&self) -> u32 { + 0 + } } impl FocusableView for ProjectPanel { diff --git a/crates/terminal_view/src/terminal_panel.rs b/crates/terminal_view/src/terminal_panel.rs index 772ed1b80d..3203f0455f 100644 --- a/crates/terminal_view/src/terminal_panel.rs +++ b/crates/terminal_view/src/terminal_panel.rs @@ -1393,6 +1393,10 @@ impl Panel for TerminalPanel { fn pane(&self) -> Option> { Some(self.active_pane.clone()) } + + fn activation_priority(&self) -> u32 { + 1 + } } struct InlineAssistTabBarButton { diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 46201de2a3..813afd8c10 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -53,6 +53,7 @@ pub trait Panel: FocusableView + EventEmitter { fn remote_id() -> Option { None } + fn activation_priority(&self) -> u32; } pub trait PanelHandle: Send + Sync { @@ -74,6 +75,7 @@ pub trait PanelHandle: Send + Sync { fn icon_label(&self, cx: &WindowContext) -> Option; fn focus_handle(&self, cx: &AppContext) -> FocusHandle; fn to_any(&self) -> AnyView; + fn activation_priority(&self, cx: &AppContext) -> u32; } impl PanelHandle for View @@ -151,6 +153,10 @@ where fn focus_handle(&self, cx: &AppContext) -> FocusHandle { self.read(cx).focus_handle(cx).clone() } + + fn activation_priority(&self, cx: &AppContext) -> u32 { + self.read(cx).activation_priority() + } } impl From<&dyn PanelHandle> for AnyView { @@ -165,7 +171,7 @@ pub struct Dock { position: DockPosition, panel_entries: Vec, is_open: bool, - active_panel_index: usize, + active_panel_index: Option, focus_handle: FocusHandle, pub(crate) serialized_dock: Option, resizeable: bool, @@ -218,7 +224,7 @@ impl Dock { let workspace = cx.view().clone(); let dock = cx.new_view(|cx: &mut ViewContext| { let focus_subscription = cx.on_focus(&focus_handle, |dock, cx| { - if let Some(active_entry) = dock.panel_entries.get(dock.active_panel_index) { + if let Some(active_entry) = dock.active_panel_entry() { active_entry.panel.focus_handle(cx).focus(cx) } }); @@ -231,7 +237,7 @@ impl Dock { Self { position, panel_entries: Default::default(), - active_panel_index: 0, + active_panel_index: None, is_open: false, focus_handle: focus_handle.clone(), _subscriptions: [focus_subscription, zoom_subscription], @@ -321,14 +327,15 @@ impl Dock { .position(|entry| entry.panel.remote_id() == Some(panel_id)) } - pub fn active_panel_index(&self) -> usize { + fn active_panel_entry(&self) -> Option<&PanelEntry> { self.active_panel_index + .and_then(|index| self.panel_entries.get(index)) } pub(crate) fn set_open(&mut self, open: bool, cx: &mut ViewContext) { if open != self.is_open { self.is_open = open; - if let Some(active_panel) = self.panel_entries.get(self.active_panel_index) { + if let Some(active_panel) = self.active_panel_entry() { active_panel.panel.set_active(open, cx); } @@ -363,7 +370,7 @@ impl Dock { panel: View, workspace: WeakView, cx: &mut ViewContext, - ) { + ) -> usize { let subscriptions = [ cx.observe(&panel, |_, _, cx| cx.notify()), cx.observe_global::({ @@ -399,10 +406,10 @@ impl Dock { new_dock.update(cx, |new_dock, cx| { new_dock.remove_panel(&panel, cx); - new_dock.add_panel(panel.clone(), workspace.clone(), cx); + let index = new_dock.add_panel(panel.clone(), workspace.clone(), cx); if was_visible { new_dock.set_open(true, cx); - new_dock.activate_panel(new_dock.panels_len() - 1, cx); + new_dock.activate_panel(index, cx); } }); } @@ -456,17 +463,34 @@ impl Dock { }), ]; - self.panel_entries.push(PanelEntry { - panel: Arc::new(panel.clone()), - _subscriptions: subscriptions, - }); + let index = match self + .panel_entries + .binary_search_by_key(&panel.read(cx).activation_priority(), |entry| { + entry.panel.activation_priority(cx) + }) { + Ok(ix) => ix, + Err(ix) => ix, + }; + if let Some(active_index) = self.active_panel_index.as_mut() { + if *active_index >= index { + *active_index += 1; + } + } + self.panel_entries.insert( + index, + PanelEntry { + panel: Arc::new(panel.clone()), + _subscriptions: subscriptions, + }, + ); if !self.restore_state(cx) && panel.read(cx).starts_open(cx) { - self.activate_panel(self.panel_entries.len() - 1, cx); + self.activate_panel(index, cx); self.set_open(true, cx); } - cx.notify() + cx.notify(); + index } pub fn restore_state(&mut self, cx: &mut ViewContext) -> bool { @@ -494,15 +518,17 @@ impl Dock { .iter() .position(|entry| entry.panel.panel_id() == Entity::entity_id(panel)) { - match panel_ix.cmp(&self.active_panel_index) { - std::cmp::Ordering::Less => { - self.active_panel_index -= 1; + if let Some(active_panel_index) = self.active_panel_index.as_mut() { + match panel_ix.cmp(active_panel_index) { + std::cmp::Ordering::Less => { + *active_panel_index -= 1; + } + std::cmp::Ordering::Equal => { + self.active_panel_index = None; + self.set_open(false, cx); + } + std::cmp::Ordering::Greater => {} } - std::cmp::Ordering::Equal => { - self.active_panel_index = 0; - self.set_open(false, cx); - } - std::cmp::Ordering::Greater => {} } self.panel_entries.remove(panel_ix); cx.notify(); @@ -514,13 +540,13 @@ impl Dock { } pub fn activate_panel(&mut self, panel_ix: usize, cx: &mut ViewContext) { - if panel_ix != self.active_panel_index { - if let Some(active_panel) = self.panel_entries.get(self.active_panel_index) { + if Some(panel_ix) != self.active_panel_index { + if let Some(active_panel) = self.active_panel_entry() { active_panel.panel.set_active(false, cx); } - self.active_panel_index = panel_ix; - if let Some(active_panel) = self.panel_entries.get(self.active_panel_index) { + self.active_panel_index = Some(panel_ix); + if let Some(active_panel) = self.active_panel_entry() { active_panel.panel.set_active(true, cx); } @@ -534,12 +560,13 @@ impl Dock { } pub fn active_panel(&self) -> Option<&Arc> { - Some(&self.panel_entries.get(self.active_panel_index)?.panel) + let panel_entry = self.active_panel_entry()?; + Some(&panel_entry.panel) } fn visible_entry(&self) -> Option<&PanelEntry> { if self.is_open { - self.panel_entries.get(self.active_panel_index) + self.active_panel_entry() } else { None } @@ -563,16 +590,14 @@ impl Dock { pub fn active_panel_size(&self, cx: &WindowContext) -> Option { if self.is_open { - self.panel_entries - .get(self.active_panel_index) - .map(|entry| entry.panel.size(cx)) + self.active_panel_entry().map(|entry| entry.panel.size(cx)) } else { None } } pub fn resize_active_panel(&mut self, size: Option, cx: &mut ViewContext) { - if let Some(entry) = self.panel_entries.get_mut(self.active_panel_index) { + if let Some(entry) = self.active_panel_entry() { let size = size.map(|size| size.max(RESIZE_HANDLE_SIZE).round()); entry.panel.set_size(size, cx); @@ -733,7 +758,7 @@ impl Render for PanelButtons { let name = entry.panel.persistent_name(); let panel = entry.panel.clone(); - let is_active_button = i == active_index && is_open; + let is_active_button = Some(i) == active_index && is_open; let (action, tooltip) = if is_active_button { let action = dock.toggle_action(); @@ -888,6 +913,10 @@ pub mod test { fn set_active(&mut self, active: bool, _cx: &mut ViewContext) { self.active = active; } + + fn activation_priority(&self) -> u32 { + 100 + } } impl FocusableView for TestPanel { diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index d27ba72b7a..1e285276cc 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -2297,6 +2297,10 @@ impl Workspace { let was_visible = dock.is_open() && !other_is_zoomed; dock.set_open(!was_visible, cx); + if dock.active_panel().is_none() && dock.panels_len() > 0 { + dock.activate_panel(0, cx); + } + if let Some(active_panel) = dock.active_panel() { if was_visible { if active_panel.focus_handle(cx).contains_focused(cx) { @@ -7267,14 +7271,10 @@ mod tests { let (panel_1, panel_2) = workspace.update(cx, |workspace, cx| { let panel_1 = cx.new_view(|cx| TestPanel::new(DockPosition::Left, cx)); workspace.add_panel(panel_1.clone(), cx); - workspace - .left_dock() - .update(cx, |left_dock, cx| left_dock.set_open(true, cx)); + workspace.toggle_dock(DockPosition::Left, cx); let panel_2 = cx.new_view(|cx| TestPanel::new(DockPosition::Right, cx)); workspace.add_panel(panel_2.clone(), cx); - workspace - .right_dock() - .update(cx, |right_dock, cx| right_dock.set_open(true, cx)); + workspace.toggle_dock(DockPosition::Right, cx); let left_dock = workspace.left_dock(); assert_eq!( diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index b6795cda14..f32441e840 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -345,7 +345,7 @@ fn initialize_panels(prompt_builder: Arc, cx: &mut ViewContext, cx: &mut ViewContext