crates/agent_ui/src/threads_panel.rs 🔗
@@ -2209,7 +2209,7 @@ impl Panel for ThreadsPanel {
}
fn activation_priority(&self) -> u32 {
- 4
+ 0
}
fn enabled(&self, cx: &App) -> bool {
Max Brunsfeld created
crates/agent_ui/src/threads_panel.rs | 2
crates/git_ui/src/git_panel.rs | 2
crates/project_panel/src/project_panel.rs | 2
crates/terminal_view/src/terminal_panel.rs | 2
crates/workspace/src/dock.rs | 95 +++++++++--------------
crates/workspace/src/persistence.rs | 1
crates/workspace/src/workspace.rs | 49 +++++++++++
crates/zed/src/zed.rs | 79 ++++++++++++-------
8 files changed, 140 insertions(+), 92 deletions(-)
@@ -2209,7 +2209,7 @@ impl Panel for ThreadsPanel {
}
fn activation_priority(&self) -> u32 {
- 4
+ 0
}
fn enabled(&self, cx: &App) -> bool {
@@ -5810,7 +5810,7 @@ impl Panel for GitPanel {
}
fn activation_priority(&self) -> u32 {
- 2
+ 3
}
}
@@ -7091,7 +7091,7 @@ impl Panel for ProjectPanel {
}
fn activation_priority(&self) -> u32 {
- 0
+ 1
}
}
@@ -1643,7 +1643,7 @@ impl Panel for TerminalPanel {
}
fn activation_priority(&self) -> u32 {
- 1
+ 2
}
fn enabled(&self, cx: &App) -> bool {
@@ -90,6 +90,14 @@ pub trait PanelHandle: Send + Sync {
fn to_any(&self) -> AnyView;
fn activation_priority(&self, cx: &App) -> u32;
fn enabled(&self, cx: &App) -> bool;
+ fn add_to_dock(
+ &self,
+ dock: &Entity<Dock>,
+ workspace: WeakEntity<Workspace>,
+ window: &mut Window,
+ cx: &mut App,
+ ) -> usize;
+ fn remove_from_dock(&self, dock: &Entity<Dock>, window: &mut Window, cx: &mut App);
fn move_to_next_position(&self, window: &mut Window, cx: &mut App) {
let current_position = self.position(window, cx);
let next_position = [
@@ -190,6 +198,22 @@ where
fn enabled(&self, cx: &App) -> bool {
self.read(cx).enabled(cx)
}
+
+ fn add_to_dock(
+ &self,
+ dock: &Entity<Dock>,
+ workspace: WeakEntity<Workspace>,
+ window: &mut Window,
+ cx: &mut App,
+ ) -> usize {
+ dock.update(cx, |dock, cx| {
+ dock.add_panel(self.clone(), workspace, window, cx)
+ })
+ }
+
+ fn remove_from_dock(&self, dock: &Entity<Dock>, window: &mut Window, cx: &mut App) {
+ dock.update(cx, |dock, cx| dock.remove_panel(self, window, cx));
+ }
}
impl From<&dyn PanelHandle> for AnyView {
@@ -265,7 +289,7 @@ impl DockPosition {
struct PanelEntry {
panel: Arc<dyn PanelHandle>,
- _subscriptions: [Subscription; 3],
+ _subscriptions: [Subscription; 2],
}
impl Dock {
@@ -465,57 +489,6 @@ impl Dock {
) -> usize {
let subscriptions = [
cx.observe(&panel, |_, _, cx| cx.notify()),
- cx.observe_global_in::<SettingsStore>(window, {
- let workspace = workspace.clone();
- let panel = panel.clone();
-
- move |this, window, cx| {
- let new_position = panel.read(cx).position(window, cx);
- if new_position == this.position {
- return;
- }
-
- let Ok(new_dock) = workspace.update(cx, |workspace, cx| {
- if panel.is_zoomed(window, cx) {
- workspace.zoomed_position = Some(new_position);
- }
- match new_position {
- DockPosition::Left => &workspace.left_dock,
- DockPosition::Bottom => &workspace.bottom_dock,
- DockPosition::Right => &workspace.right_dock,
- }
- .clone()
- }) else {
- return;
- };
-
- let was_visible = this.is_open()
- && this.visible_panel().is_some_and(|active_panel| {
- active_panel.panel_id() == Entity::entity_id(&panel)
- });
-
- this.remove_panel(&panel, window, cx);
-
- new_dock.update(cx, |new_dock, cx| {
- new_dock.remove_panel(&panel, window, cx);
- });
-
- new_dock.update(cx, |new_dock, cx| {
- let index =
- new_dock.add_panel(panel.clone(), workspace.clone(), window, cx);
- if was_visible {
- new_dock.set_open(true, window, cx);
- new_dock.activate_panel(index, window, cx);
- }
- });
-
- workspace
- .update(cx, |workspace, cx| {
- workspace.serialize_workspace(window, cx);
- })
- .ok();
- }
- }),
cx.subscribe_in(
&panel,
window,
@@ -571,11 +544,15 @@ impl Dock {
),
];
- let index = match self
- .panel_entries
- .binary_search_by_key(&panel.read(cx).activation_priority(), |entry| {
- entry.panel.activation_priority(cx)
- }) {
+ let position = self.position;
+ let priority = panel.activation_priority(cx);
+ let index = match self.panel_entries.binary_search_by(|probe| {
+ let other_priority = probe.panel.activation_priority(cx);
+ match position {
+ DockPosition::Left | DockPosition::Bottom => other_priority.cmp(&priority),
+ DockPosition::Right => priority.cmp(&other_priority),
+ }
+ }) {
Ok(ix) => {
if cfg!(debug_assertions) {
panic!(
@@ -668,6 +645,10 @@ impl Dock {
self.panel_entries.len()
}
+ pub fn panels(&self) -> impl Iterator<Item = &Arc<dyn PanelHandle>> {
+ self.panel_entries.iter().map(|entry| &entry.panel)
+ }
+
pub fn activate_panel(&mut self, panel_ix: usize, window: &mut Window, cx: &mut Context<Self>) {
if Some(panel_ix) != self.active_panel_index {
if let Some(active_panel) = self.active_panel_entry() {
@@ -4002,7 +4002,6 @@ mod tests {
use crate::multi_workspace::MultiWorkspace;
use crate::persistence::read_multi_workspace_state;
use feature_flags::FeatureFlagAppExt;
-
use project::Project;
crate::tests::init_test(cx);
@@ -1394,7 +1394,7 @@ impl Workspace {
})
.detach();
- cx.observe_global::<SettingsStore>(|_, cx| {
+ cx.observe_global_in::<SettingsStore>(window, |this, window, cx| {
if ProjectSettings::get_global(cx).session.trust_all_worktrees {
if let Some(trusted_worktrees) = TrustedWorktrees::try_get_global(cx) {
trusted_worktrees.update(cx, |trusted_worktrees, cx| {
@@ -1402,6 +1402,7 @@ impl Workspace {
})
}
}
+ this.reposition_panels(window, cx);
})
.detach();
}
@@ -2153,6 +2154,40 @@ impl Workspace {
}
}
+ fn reposition_panels(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+ let mut panels_to_move = Vec::new();
+ for dock_entity in [&self.left_dock, &self.bottom_dock, &self.right_dock] {
+ let dock = dock_entity.read(cx);
+ let old_position = dock.position();
+ let visible_panel_id = dock.visible_panel().map(|panel| panel.panel_id());
+ for panel in dock.panels() {
+ let new_position = panel.position(window, cx);
+ if new_position != old_position {
+ let was_visible = Some(panel.panel_id()) == visible_panel_id;
+ panels_to_move.push((dock_entity, panel.clone(), new_position, was_visible));
+ }
+ }
+ }
+
+ for (old_dock, panel, new_position, was_visible) in panels_to_move {
+ if panel.is_zoomed(window, cx) {
+ self.zoomed_position = Some(new_position);
+ }
+
+ panel.remove_from_dock(old_dock, window, cx);
+ let new_dock = self.dock_at_position(new_position).clone();
+ let index = panel.add_to_dock(&new_dock, self.weak_self.clone(), window, cx);
+ if was_visible {
+ new_dock.update(cx, |new_dock, cx| {
+ new_dock.set_open(true, window, cx);
+ new_dock.activate_panel(index, window, cx);
+ });
+ }
+ }
+
+ self.serialize_workspace(window, cx);
+ }
+
pub fn status_bar(&self) -> &Entity<StatusBar> {
&self.status_bar
}
@@ -7012,6 +7047,11 @@ impl Workspace {
view: Entity<V>,
cx: &mut Context<Self>,
) {
+ if let Some(drawer) = self.right_drawer.as_mut() {
+ if drawer.view.entity_id() == view.entity_id() {
+ self.right_drawer.take();
+ }
+ }
self.left_drawer = Some(Drawer::new(view));
cx.notify();
}
@@ -7021,6 +7061,11 @@ impl Workspace {
view: Entity<V>,
cx: &mut Context<Self>,
) {
+ if let Some(drawer) = self.left_drawer.as_mut() {
+ if drawer.view.entity_id() == view.entity_id() {
+ self.left_drawer.take();
+ }
+ }
self.right_drawer = Some(Drawer::new(view));
cx.notify();
}
@@ -7938,7 +7983,7 @@ impl Drawer {
Self {
view: view.into(),
focus_handle_fn: Box::new(move |cx| entity.focus_handle(cx)),
- open: true,
+ open: false,
custom_width: None,
}
}
@@ -83,6 +83,7 @@ use util::rel_path::RelPath;
use util::{ResultExt, asset_str, maybe};
use uuid::Uuid;
use vim_mode_setting::VimModeSetting;
+use workspace::dock::PanelHandle;
use workspace::notifications::{
NotificationId, SuppressEvent, dismiss_app_notification, show_app_notification,
};
@@ -669,41 +670,63 @@ fn setup_or_teardown_ai_panels(
|| cfg!(test);
let existing_panel = workspace.panel::<agent_ui::ThreadsPanel>(cx);
match (disable_ai, existing_panel) {
- (false, None) => cx.spawn_in(window, async move |workspace, cx| {
- let agent_drawer =
- agent_ui::AgentPanel::load(workspace.clone(), prompt_builder.clone(), cx.clone())
- .await?;
- let sidebar_panel = agent_ui::ThreadsPanel::load(workspace.clone(), cx.clone()).await?;
- workspace.update_in(cx, |workspace, window, cx| {
- let disable_ai = SettingsStore::global(cx)
- .get::<DisableAiSettings>(None)
- .disable_ai;
- let have_panel = workspace.panel::<agent_ui::ThreadsPanel>(cx).is_some();
- if !disable_ai && !have_panel {
- let position =
- workspace::dock::PanelHandle::position(&sidebar_panel, window, cx);
- workspace.add_panel(sidebar_panel, window, cx);
- match position {
- workspace::dock::DockPosition::Left => {
- workspace.set_left_drawer(agent_drawer, cx)
- }
- workspace::dock::DockPosition::Right => {
- workspace.set_right_drawer(agent_drawer, cx)
- }
- workspace::dock::DockPosition::Bottom => {
- unreachable!("drawers cannot go on the bottom")
+ (false, None) => {
+ return cx.spawn_in(window, async move |workspace, cx| {
+ let agent_drawer = agent_ui::AgentPanel::load(
+ workspace.clone(),
+ prompt_builder.clone(),
+ cx.clone(),
+ )
+ .await?;
+ let threads_panel =
+ agent_ui::ThreadsPanel::load(workspace.clone(), cx.clone()).await?;
+ workspace.update_in(cx, |workspace, window, cx| {
+ let disable_ai = SettingsStore::global(cx)
+ .get::<DisableAiSettings>(None)
+ .disable_ai;
+ let have_panel = workspace.panel::<agent_ui::ThreadsPanel>(cx).is_some();
+ let position = PanelHandle::position(&threads_panel, window, cx);
+ if !disable_ai && !have_panel {
+ workspace.add_panel(threads_panel, window, cx);
+ match position {
+ workspace::dock::DockPosition::Left => {
+ workspace.set_left_drawer(agent_drawer, cx)
+ }
+ workspace::dock::DockPosition::Right => {
+ workspace.set_right_drawer(agent_drawer, cx)
+ }
+ workspace::dock::DockPosition::Bottom => {
+ unreachable!("drawers cannot go on the bottom")
+ }
}
}
- }
- })
- }),
+ })
+ });
+ }
(true, Some(existing_panel)) => {
workspace.remove_panel(&existing_panel, window, cx);
workspace.remove_drawer::<agent_ui::AgentPanel>(cx);
- Task::ready(Ok(()))
}
- _ => Task::ready(Ok(())),
+ (false, Some(existing_panel)) => {
+ let position = PanelHandle::position(&existing_panel, window, cx);
+ if let Some(agent_drawer) = workspace.drawer::<agent_ui::AgentPanel>() {
+ match position {
+ workspace::dock::DockPosition::Left => {
+ workspace.set_left_drawer(agent_drawer, cx)
+ }
+ workspace::dock::DockPosition::Right => {
+ workspace.set_right_drawer(agent_drawer, cx)
+ }
+ workspace::dock::DockPosition::Bottom => {
+ unreachable!("drawers cannot go on the bottom")
+ }
+ }
+ }
+ }
+ _ => {}
}
+
+ Task::ready(Ok(()))
}
async fn initialize_agent_panel(