Detailed changes
@@ -4908,7 +4908,8 @@ 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.add_panel(panel, window, cx);
+ let position = panel.read(cx).position(window, cx);
+ workspace.add_panel(panel, position, window, cx);
});
cx.run_until_parked();
@@ -5132,7 +5133,8 @@ 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.add_panel(panel.clone(), window, cx);
+ let position = panel.read(cx).position(window, cx);
+ workspace.add_panel(panel.clone(), position, window, cx);
panel
});
@@ -5242,7 +5244,8 @@ 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.add_panel(panel.clone(), window, cx);
+ let position = panel.read(cx).position(window, cx);
+ workspace.add_panel(panel.clone(), position, window, cx);
panel
});
@@ -5327,7 +5330,8 @@ 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.add_panel(panel.clone(), window, cx);
+ let position = panel.read(cx).position(window, cx);
+ workspace.add_panel(panel.clone(), position, window, cx);
panel
});
@@ -2780,6 +2780,7 @@ pub(crate) mod tests {
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::sync::Arc;
+ use workspace::dock::Panel;
use workspace::{Item, MultiWorkspace};
use crate::agent_panel;
@@ -3459,7 +3460,8 @@ 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.add_panel(panel, window, cx);
+ let position = panel.read(cx).position(window, cx);
+ workspace.add_panel(panel, position, window, cx);
// Open the dock and activate the agent panel so it's visible
workspace.focus_panel::<crate::AgentPanel>(window, cx);
@@ -18,8 +18,8 @@ use settings::SettingsStore;
use text::{Point, ToPoint};
use util::{path, rel_path::rel_path, test::sample_text};
use workspace::{
- CloseWindow, CollaboratorId, MultiWorkspace, ParticipantLocation, SplitDirection, Workspace,
- item::ItemHandle as _,
+ CloseWindow, CollaboratorId, MultiWorkspace, Panel as _, ParticipantLocation, SplitDirection,
+ Workspace, item::ItemHandle as _,
};
use super::TestClient;
@@ -534,7 +534,8 @@ async fn test_basic_following(
// Client B activates a panel, and the previously-opened screen-sharing item gets activated.
let panel = cx_b.new(|cx| TestPanel::new(DockPosition::Left, 100, cx));
workspace_b.update_in(cx_b, |workspace, window, cx| {
- workspace.add_panel(panel, window, cx);
+ let position = panel.read(cx).position(window, cx);
+ workspace.add_panel(panel, position, window, cx);
workspace.toggle_panel_focus::<TestPanel>(window, cx);
});
executor.run_until_parked();
@@ -12,6 +12,7 @@ use project::ProjectPath;
use serde_json::json;
use util::{path, rel_path::rel_path};
+use workspace::dock::Panel;
use workspace::{MultiWorkspace, Workspace};
use crate::TestServer;
@@ -371,12 +372,14 @@ async fn test_diff_stat_sync_between_host_and_downstream_client(
let panel_a = workspace_a.update_in(cx_a, GitPanel::new_test);
workspace_a.update_in(cx_a, |workspace, window, cx| {
- workspace.add_panel(panel_a.clone(), window, cx);
+ let position = panel_a.read(cx).position(window, cx);
+ workspace.add_panel(panel_a.clone(), position, window, cx);
});
let panel_b = workspace_b.update_in(cx_b, GitPanel::new_test);
workspace_b.update_in(cx_b, |workspace, window, cx| {
- workspace.add_panel(panel_b.clone(), window, cx);
+ let position = panel_b.read(cx).position(window, cx);
+ workspace.add_panel(panel_b.clone(), position, window, cx);
});
cx_a.run_until_parked();
@@ -488,7 +491,8 @@ async fn test_diff_stat_sync_between_host_and_downstream_client(
let (workspace_b, cx_b) = client_b.build_workspace(&project_b, cx_b);
let panel_b = workspace_b.update_in(cx_b, GitPanel::new_test);
workspace_b.update_in(cx_b, |workspace, window, cx| {
- workspace.add_panel(panel_b.clone(), window, cx);
+ let position = panel_b.read(cx).position(window, cx);
+ workspace.add_panel(panel_b.clone(), position, window, cx);
});
cx_b.run_until_parked();
@@ -42,6 +42,7 @@ use std::{
};
use task::TcpArgumentsTemplate;
use util::{path, rel_path::rel_path};
+use workspace::dock::Panel;
#[gpui::test(iterations = 10)]
async fn test_sharing_an_ssh_remote_project(
@@ -783,7 +784,8 @@ async fn test_remote_server_debugger(
.unwrap();
workspace.update_in(cx_a, |workspace, window, cx| {
- workspace.add_panel(debugger_panel, window, cx);
+ let position = debugger_panel.read(cx).position(window, cx);
+ workspace.add_panel(debugger_panel, position, window, cx);
});
cx_a.run_until_parked();
@@ -896,7 +898,8 @@ async fn test_slow_adapter_startup_retries(
.unwrap();
workspace.update_in(cx_a, |workspace, window, cx| {
- workspace.add_panel(debugger_panel, window, cx);
+ let position = debugger_panel.read(cx).position(window, cx);
+ workspace.add_panel(debugger_panel, position, window, cx);
});
cx_a.run_until_parked();
@@ -9,6 +9,7 @@ use settings::SettingsStore;
use task::SharedTaskContext;
use terminal_view::terminal_panel::TerminalPanel;
use workspace::MultiWorkspace;
+use workspace::dock::Panel;
use crate::{debugger_panel::DebugPanel, session::DebugSession};
@@ -82,8 +83,10 @@ pub async fn init_test_workspace(
workspace_handle
.update(cx, |multi, window, cx| {
multi.workspace().update(cx, |workspace, cx| {
- workspace.add_panel(debugger_panel, window, cx);
- workspace.add_panel(terminal_panel, window, cx);
+ let position = debugger_panel.read(cx).position(window, cx);
+ workspace.add_panel(debugger_panel, position, window, cx);
+ let position = terminal_panel.read(cx).position(window, cx);
+ workspace.add_panel(terminal_panel, position, window, cx);
});
})
.unwrap();
@@ -6812,7 +6812,8 @@ outline: struct OutlineEntryExcerpt
window
.update(cx, |multi_workspace, window, cx| {
multi_workspace.workspace().update(cx, |workspace, cx| {
- workspace.add_panel(outline_panel, window, cx);
+ let position = outline_panel.read(cx).position(window, cx);
+ workspace.add_panel(outline_panel, position, window, cx);
});
})
.unwrap();
@@ -11,6 +11,7 @@ use std::path::{Path, PathBuf};
use util::{path, paths::PathStyle, rel_path::rel_path};
use workspace::{
AppState, ItemHandle, MultiWorkspace, Pane, Workspace,
+ dock::DockPosition,
item::{Item, ProjectItem},
register_project_item,
};
@@ -527,7 +528,7 @@ async fn test_editing_files(cx: &mut gpui::TestAppContext) {
let cx = &mut VisualTestContext::from_window(window.into(), cx);
let panel = workspace.update_in(cx, |workspace, window, cx| {
let panel = ProjectPanel::new(workspace, window, cx);
- workspace.add_panel(panel.clone(), window, cx);
+ workspace.add_panel(panel.clone(), DockPosition::Left, window, cx);
panel
});
cx.run_until_parked();
@@ -960,7 +961,7 @@ async fn test_adding_directories_via_file(cx: &mut gpui::TestAppContext) {
let cx = &mut VisualTestContext::from_window(window.into(), cx);
let panel = workspace.update_in(cx, |workspace, window, cx| {
let panel = ProjectPanel::new(workspace, window, cx);
- workspace.add_panel(panel.clone(), window, cx);
+ workspace.add_panel(panel.clone(), DockPosition::Left, window, cx);
panel
});
cx.run_until_parked();
@@ -1073,7 +1074,7 @@ async fn test_adding_directory_via_file(cx: &mut gpui::TestAppContext) {
let cx = &mut VisualTestContext::from_window(window.into(), cx);
let panel = workspace.update_in(cx, |workspace, window, cx| {
let panel = ProjectPanel::new(workspace, window, cx);
- workspace.add_panel(panel.clone(), window, cx);
+ workspace.add_panel(panel.clone(), DockPosition::Left, window, cx);
panel
});
cx.run_until_parked();
@@ -2335,7 +2336,7 @@ async fn test_create_duplicate_items(cx: &mut gpui::TestAppContext) {
let cx = &mut VisualTestContext::from_window(window.into(), cx);
let panel = workspace.update_in(cx, |workspace, window, cx| {
let panel = ProjectPanel::new(workspace, window, cx);
- workspace.add_panel(panel.clone(), window, cx);
+ workspace.add_panel(panel.clone(), DockPosition::Left, window, cx);
panel
});
cx.run_until_parked();
@@ -2541,7 +2542,7 @@ async fn test_create_duplicate_items_and_check_history(cx: &mut gpui::TestAppCon
let cx = &mut VisualTestContext::from_window(window.into(), cx);
let panel = workspace.update_in(cx, |workspace, window, cx| {
let panel = ProjectPanel::new(workspace, window, cx);
- workspace.add_panel(panel.clone(), window, cx);
+ workspace.add_panel(panel.clone(), DockPosition::Left, window, cx);
panel
});
cx.run_until_parked();
@@ -2807,7 +2808,7 @@ async fn test_rename_item_and_check_history(cx: &mut gpui::TestAppContext) {
let cx = &mut VisualTestContext::from_window(window.into(), cx);
let panel = workspace.update_in(cx, |workspace, window, cx| {
let panel = ProjectPanel::new(workspace, window, cx);
- workspace.add_panel(panel.clone(), window, cx);
+ workspace.add_panel(panel.clone(), DockPosition::Left, window, cx);
panel
});
cx.run_until_parked();
@@ -5269,7 +5270,7 @@ async fn test_creating_excluded_entries(cx: &mut gpui::TestAppContext) {
let cx = &mut VisualTestContext::from_window(window.into(), cx);
let panel = workspace.update_in(cx, |workspace, window, cx| {
let panel = ProjectPanel::new(workspace, window, cx);
- workspace.add_panel(panel.clone(), window, cx);
+ workspace.add_panel(panel.clone(), DockPosition::Left, window, cx);
panel
});
cx.run_until_parked();
@@ -5454,7 +5455,7 @@ async fn test_selection_restored_when_creation_cancelled(cx: &mut gpui::TestAppC
let cx = &mut VisualTestContext::from_window(window.into(), cx);
let panel = workspace.update_in(cx, |workspace, window, cx| {
let panel = ProjectPanel::new(workspace, window, cx);
- workspace.add_panel(panel.clone(), window, cx);
+ workspace.add_panel(panel.clone(), DockPosition::Left, window, cx);
panel
});
cx.run_until_parked();
@@ -7471,7 +7472,7 @@ async fn test_create_entries_without_selection(cx: &mut gpui::TestAppContext) {
let panel = workspace.update_in(cx, |workspace, window, cx| {
let panel = ProjectPanel::new(workspace, window, cx);
- workspace.add_panel(panel.clone(), window, cx);
+ workspace.add_panel(panel.clone(), DockPosition::Left, window, cx);
panel
});
cx.run_until_parked();
@@ -7551,7 +7552,7 @@ async fn test_create_entries_without_selection_hide_root(cx: &mut gpui::TestAppC
let panel = workspace.update_in(cx, |workspace, window, cx| {
let panel = ProjectPanel::new(workspace, window, cx);
- workspace.add_panel(panel.clone(), window, cx);
+ workspace.add_panel(panel.clone(), DockPosition::Left, window, cx);
panel
});
cx.run_until_parked();
@@ -7696,7 +7697,7 @@ async fn test_create_entry_with_trailing_dot_windows(cx: &mut gpui::TestAppConte
let panel = workspace.update_in(cx, |workspace, window, cx| {
let panel = ProjectPanel::new(workspace, window, cx);
- workspace.add_panel(panel.clone(), window, cx);
+ workspace.add_panel(panel.clone(), DockPosition::Left, window, cx);
panel
});
cx.run_until_parked();
@@ -9305,7 +9306,7 @@ async fn test_preserve_temporary_unfolded_active_index_on_blur_from_context_menu
let panel = workspace.update_in(cx, |workspace, window, cx| {
let panel = ProjectPanel::new(workspace, window, cx);
- workspace.add_panel(panel.clone(), window, cx);
+ workspace.add_panel(panel.clone(), DockPosition::Left, window, cx);
panel
});
@@ -9489,7 +9490,7 @@ async fn run_create_file_in_folded_path_case(
let panel = workspace.update_in(cx, |workspace, window, cx| {
let panel = ProjectPanel::new(workspace, window, cx);
- workspace.add_panel(panel.clone(), window, cx);
+ workspace.add_panel(panel.clone(), DockPosition::Left, window, cx);
panel
});
@@ -1494,6 +1494,7 @@ mod tests {
use settings::SettingsStore;
use std::sync::Arc;
use util::path_list::PathList;
+ use workspace::dock::Panel;
fn init_test(cx: &mut TestAppContext) {
cx.update(|cx| {
@@ -2500,7 +2501,8 @@ 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.add_panel(panel.clone(), window, cx);
+ let position = panel.read(cx).position(window, cx);
+ workspace.add_panel(panel.clone(), position, window, cx);
panel
})
}
@@ -83,6 +83,13 @@ 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: &mut Dock,
+ workspace: WeakEntity<Workspace>,
+ window: &mut Window,
+ cx: &mut Context<Dock>,
+ ) -> usize;
fn move_to_next_position(&self, window: &mut Window, cx: &mut App) {
let current_position = self.position(window, cx);
let next_position = [
@@ -187,6 +194,16 @@ where
fn enabled(&self, cx: &App) -> bool {
self.read(cx).enabled(cx)
}
+
+ fn add_to_dock(
+ &self,
+ dock: &mut Dock,
+ workspace: WeakEntity<Workspace>,
+ window: &mut Window,
+ cx: &mut Context<Dock>,
+ ) -> usize {
+ dock.add_panel(self.clone(), workspace, window, cx)
+ }
}
impl From<&dyn PanelHandle> for AnyView {
@@ -262,7 +279,7 @@ impl DockPosition {
struct PanelEntry {
panel: Arc<dyn PanelHandle>,
- _subscriptions: [Subscription; 3],
+ _subscriptions: [Subscription; 2],
}
pub struct PanelButtons {
@@ -467,57 +484,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,
@@ -666,6 +632,46 @@ impl Dock {
}
}
+ pub fn remove_panel_by_id(
+ &mut self,
+ panel_id: EntityId,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) -> bool {
+ if let Some(panel_ix) = self
+ .panel_entries
+ .iter()
+ .position(|entry| entry.panel.panel_id() == panel_id)
+ {
+ 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, window, cx);
+ }
+ std::cmp::Ordering::Greater => {}
+ }
+ }
+
+ self.panel_entries.remove(panel_ix);
+ cx.notify();
+
+ true
+ } else {
+ false
+ }
+ }
+
+ pub fn panel_ids(&self) -> Vec<EntityId> {
+ self.panel_entries
+ .iter()
+ .map(|entry| entry.panel.panel_id())
+ .collect()
+ }
+
pub fn panels_len(&self) -> usize {
self.panel_entries.len()
}
@@ -146,7 +146,7 @@ impl MultiWorkspace {
active_workspace_index: 0,
sidebar: None,
sidebar_open: false,
- is_singleton: false,
+ is_singleton: true,
_sidebar_subscription: None,
pending_removal_tasks: Vec::new(),
_serialize_task: None,
@@ -488,11 +488,12 @@ impl MultiWorkspace {
pub fn add_panel<T: Panel>(
&mut self,
panel: Entity<T>,
+ position: DockPosition,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.workspace().update(cx, |workspace, cx| {
- workspace.add_panel(panel, window, cx);
+ workspace.add_panel(panel, position, window, cx);
});
}
@@ -2121,6 +2121,7 @@ impl Workspace {
pub fn add_panel<T: Panel>(
&mut self,
panel: Entity<T>,
+ position: DockPosition,
window: &mut Window,
cx: &mut Context<Self>,
) {
@@ -2128,8 +2129,7 @@ impl Workspace {
cx.on_focus_in(&focus_handle, window, Self::handle_panel_focused)
.detach();
- let dock_position = panel.position(window, cx);
- let dock = self.dock_at_position(dock_position);
+ let dock = self.dock_at_position(position);
let any_panel = panel.to_any();
dock.update(cx, |dock, cx| {
@@ -2139,6 +2139,79 @@ impl Workspace {
cx.emit(Event::PanelAdded(any_panel));
}
+ pub fn move_panel_to_dock(
+ &mut self,
+ panel_id: EntityId,
+ new_position: DockPosition,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) {
+ let current_dock_position = self
+ .all_docks()
+ .iter()
+ .find(|dock| dock.read(cx).panel_for_id(panel_id).is_some())
+ .map(|dock| dock.read(cx).position());
+
+ let Some(current_dock_position) = current_dock_position else {
+ return;
+ };
+
+ if current_dock_position == new_position {
+ return;
+ }
+
+ let current_dock = self.dock_at_position(current_dock_position).clone();
+
+ let was_visible = current_dock.read(cx).is_open()
+ && current_dock
+ .read(cx)
+ .visible_panel()
+ .is_some_and(|active_panel| active_panel.panel_id() == panel_id);
+
+ let panel_handle = current_dock.read(cx).panel_for_id(panel_id).cloned();
+
+ let Some(panel_handle) = panel_handle else {
+ return;
+ };
+
+ if panel_handle.is_zoomed(window, cx) {
+ self.zoomed_position = Some(new_position);
+ }
+
+ current_dock.update(cx, |dock, cx| {
+ dock.remove_panel_by_id(panel_id, window, cx);
+ });
+
+ let new_dock = self.dock_at_position(new_position).clone();
+
+ new_dock.update(cx, |dock, cx| {
+ dock.remove_panel_by_id(panel_id, window, cx);
+ });
+
+ let weak_self = self.weak_self.clone();
+ new_dock.update(cx, |dock, cx| {
+ let index = panel_handle.add_to_dock(dock, weak_self, window, cx);
+ if was_visible {
+ dock.set_open(true, window, cx);
+ dock.activate_panel(index, window, cx);
+ }
+ });
+
+ self.serialize_workspace(window, cx);
+ }
+
+ pub fn all_panel_ids_and_positions(&self, cx: &App) -> Vec<(EntityId, DockPosition)> {
+ let mut result = Vec::new();
+ for dock in self.all_docks() {
+ let dock = dock.read(cx);
+ let position = dock.position();
+ for panel_id in dock.panel_ids() {
+ result.push((panel_id, position));
+ }
+ }
+ result
+ }
+
pub fn remove_panel<T: Panel>(
&mut self,
panel: &Entity<T>,
@@ -10907,7 +10980,8 @@ mod tests {
let panel = workspace.update_in(cx, |workspace, window, cx| {
let panel = cx.new(|cx| TestPanel::new(DockPosition::Right, 100, cx));
- workspace.add_panel(panel.clone(), window, cx);
+ let position = panel.read(cx).position(window, cx);
+ workspace.add_panel(panel.clone(), position, window, cx);
workspace
.right_dock()
@@ -11057,7 +11131,8 @@ mod tests {
let panel = workspace.update_in(cx, |workspace, window, cx| {
let panel = cx.new(|cx| TestPanel::new(DockPosition::Right, 100, cx));
- workspace.add_panel(panel.clone(), window, cx);
+ let position = panel.read(cx).position(window, cx);
+ workspace.add_panel(panel.clone(), position, window, cx);
panel
});
@@ -11352,10 +11427,12 @@ mod tests {
// Open two docks (left and right) with one panel each
let (left_panel, right_panel) = workspace.update_in(cx, |workspace, window, cx| {
let left_panel = cx.new(|cx| TestPanel::new(DockPosition::Left, 100, cx));
- workspace.add_panel(left_panel.clone(), window, cx);
+ let position = left_panel.read(cx).position(window, cx);
+ workspace.add_panel(left_panel.clone(), position, window, cx);
let right_panel = cx.new(|cx| TestPanel::new(DockPosition::Right, 101, cx));
- workspace.add_panel(right_panel.clone(), window, cx);
+ let position = right_panel.read(cx).position(window, cx);
+ workspace.add_panel(right_panel.clone(), position, window, cx);
workspace.toggle_dock(DockPosition::Left, window, cx);
workspace.toggle_dock(DockPosition::Right, window, cx);
@@ -11784,10 +11861,12 @@ mod tests {
let (panel_1, panel_2) = workspace.update_in(cx, |workspace, window, cx| {
let panel_1 = cx.new(|cx| TestPanel::new(DockPosition::Left, 100, cx));
- workspace.add_panel(panel_1.clone(), window, cx);
+ let position = panel_1.read(cx).position(window, cx);
+ workspace.add_panel(panel_1.clone(), position, window, cx);
workspace.toggle_dock(DockPosition::Left, window, cx);
let panel_2 = cx.new(|cx| TestPanel::new(DockPosition::Right, 101, cx));
- workspace.add_panel(panel_2.clone(), window, cx);
+ let position = panel_2.read(cx).position(window, cx);
+ workspace.add_panel(panel_2.clone(), position, window, cx);
workspace.toggle_dock(DockPosition::Right, window, cx);
let left_dock = workspace.left_dock();
@@ -12695,7 +12774,8 @@ mod tests {
// focus to the new panel.
let panel = workspace.update_in(cx, |workspace, window, cx| {
let panel = cx.new(|cx| TestPanel::new(DockPosition::Right, 100, cx));
- workspace.add_panel(panel.clone(), window, cx);
+ let position = panel.read(cx).position(window, cx);
+ workspace.add_panel(panel.clone(), position, window, cx);
workspace
.right_dock()
@@ -13383,7 +13463,8 @@ mod tests {
let workspace = multi_workspace.read_with(cx, |mw, _| mw.workspace().clone());
let panel = workspace.update_in(cx, |workspace, window, cx| {
let panel = cx.new(|cx| TestPanel::new(DockPosition::Right, 100, cx));
- workspace.add_panel(panel.clone(), window, cx);
+ let position = panel.read(cx).position(window, cx);
+ workspace.add_panel(panel.clone(), position, window, cx);
workspace
.right_dock()
@@ -13468,7 +13549,8 @@ mod tests {
// Add a panel to workspace A's right dock and open the dock
let panel = workspace_a.update_in(cx, |workspace, window, cx| {
let panel = cx.new(|cx| TestPanel::new(DockPosition::Right, 100, cx));
- workspace.add_panel(panel.clone(), window, cx);
+ let position = panel.read(cx).position(window, cx);
+ workspace.add_panel(panel.clone(), position, window, cx);
workspace
.right_dock()
.update(cx, |dock, cx| dock.set_open(true, window, cx));
@@ -13570,7 +13652,8 @@ mod tests {
let panel = workspace.update_in(cx, |workspace, window, cx| {
let panel = cx.new(|cx| TestPanel::new(DockPosition::Right, 100, cx));
- workspace.add_panel(panel.clone(), window, cx);
+ let position = panel.read(cx).position(window, cx);
+ workspace.add_panel(panel.clone(), position, window, cx);
workspace
.right_dock()
.update(cx, |dock, cx| dock.set_open(true, window, cx));
@@ -321,7 +321,8 @@ fn run_visual_tests(project_path: PathBuf, update_baseline: bool) -> Result<()>
workspace_window
.update(&mut cx, |workspace, window, cx| {
- workspace.add_panel(panel, window, cx);
+ let position = panel.read(cx).position(window, cx);
+ workspace.add_panel(panel, position, window, cx);
})
.log_err();
@@ -84,10 +84,12 @@ 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,
};
+use workspace::dock::DockPosition;
use workspace::{
AppState, MultiWorkspace, NewFile, NewWindow, OpenLog, Panel, Toast, Workspace,
WorkspaceSettings, create_and_open_local_file,
@@ -293,11 +295,13 @@ pub fn init(cx: &mut App) {
let Some(handle) = window.downcast::<MultiWorkspace>() else {
return;
};
- handle
- .update(cx, |multi_workspace, window, cx| {
- toggle_agent_mode(multi_workspace, window, cx);
- })
- .log_err();
+ cx.defer(move |cx| {
+ handle
+ .update(cx, |multi_workspace, window, cx| {
+ toggle_agent_mode(multi_workspace, window, cx);
+ })
+ .log_err();
+ });
});
}
@@ -306,13 +310,19 @@ fn toggle_agent_mode(
window: &mut Window,
cx: &mut Context<'_, MultiWorkspace>,
) {
- let is_singleton = multi_workspace.is_singleton();
+ let mut is_singleton = multi_workspace.is_singleton();
if is_singleton {
multi_workspace.set_singleton(false, window, cx);
multi_workspace.open_sidebar(cx);
} else {
multi_workspace.set_singleton(true, window, cx);
}
+ is_singleton = !is_singleton;
+ let agent_mode = !is_singleton;
+ let workspace = multi_workspace.workspace();
+ workspace.update(cx, |workspace, cx| {
+ update_panel_positions(workspace, window, agent_mode, cx);
+ });
}
fn bind_on_window_closed(cx: &mut App) -> Option<gpui::Subscription> {
@@ -648,6 +658,33 @@ fn show_software_emulation_warning_if_needed(
}
}
+fn is_agent_mode(cx: &mut AsyncWindowContext) -> bool {
+ cx.window_handle()
+ .downcast::<MultiWorkspace>()
+ .and_then(|handle| {
+ handle
+ .read_with(cx, |multi_workspace, _| !multi_workspace.is_singleton())
+ .ok()
+ })
+ .unwrap_or(false)
+}
+
+fn interpret_panel_dock_position(
+ panel: &dyn PanelHandle,
+ preferred: DockPosition,
+ agent_mode: bool,
+) -> DockPosition {
+ let is_agent_panel = panel.panel_key() == agent_ui::AgentPanel::panel_key();
+ if agent_mode {
+ if is_agent_panel {
+ return DockPosition::Left;
+ } else if preferred == DockPosition::Left {
+ return DockPosition::Right;
+ }
+ }
+ preferred
+}
+
fn initialize_panels(
prompt_builder: Arc<PromptBuilder>,
window: &mut Window,
@@ -666,16 +703,18 @@ fn initialize_panels(
);
let debug_panel = DebugPanel::load(workspace_handle.clone(), cx);
- async fn add_panel_when_ready(
- panel_task: impl Future<Output = anyhow::Result<Entity<impl workspace::Panel>>> + 'static,
+ async fn add_panel_when_ready<T: workspace::Panel>(
+ panel_task: impl Future<Output = anyhow::Result<Entity<T>>> + 'static,
workspace_handle: WeakEntity<Workspace>,
mut cx: gpui::AsyncWindowContext,
) {
- if let Some(panel) = panel_task.await.context("failed to load panel").log_err()
- {
+ if let Some(panel) = panel_task.await.context("failed to load panel").log_err() {
+ let agent_mode = is_agent_mode(&mut cx);
workspace_handle
.update_in(&mut cx, |workspace, window, cx| {
- workspace.add_panel(panel, window, cx);
+ let preferred = panel.position(window, cx);
+ let position = interpret_panel_dock_position(&panel, preferred, agent_mode);
+ workspace.add_panel(panel, position, window, cx);
})
.log_err();
}
@@ -689,13 +728,62 @@ fn initialize_panels(
add_panel_when_ready(channels_panel, workspace_handle.clone(), cx.clone()),
add_panel_when_ready(notification_panel, workspace_handle.clone(), cx.clone()),
add_panel_when_ready(debug_panel, workspace_handle.clone(), cx.clone()),
- initialize_agent_panel(workspace_handle, prompt_builder, cx.clone()).map(|r| r.log_err()),
+ initialize_agent_panel(workspace_handle.clone(), prompt_builder, cx.clone())
+ .map(|r| r.log_err()),
);
+ let mut cx = cx.clone();
+ workspace_handle.update_in(&mut cx, |workspace, window, cx| {
+ observe_settings_for_panel_positions(workspace, window, cx);
+ })?;
+
anyhow::Ok(())
})
}
+fn observe_settings_for_panel_positions(
+ _workspace: &mut Workspace,
+ window: &mut Window,
+ cx: &mut Context<Workspace>,
+) {
+ cx.observe_global_in::<settings::SettingsStore>(window, move |workspace, window, cx| {
+ let agent_mode = window
+ .root::<MultiWorkspace>()
+ .flatten()
+ .is_some_and(|handle| !handle.read(cx).is_singleton());
+ update_panel_positions(workspace, window, agent_mode, cx);
+ })
+ .detach();
+}
+
+fn update_panel_positions(
+ workspace: &mut Workspace,
+ window: &mut Window,
+ agent_mode: bool,
+ cx: &mut Context<Workspace>,
+) {
+ let panels_and_positions = workspace.all_panel_ids_and_positions(cx);
+ for (panel_id, current_position) in panels_and_positions {
+ let panel_handle = workspace
+ .dock_at_position(current_position)
+ .read(cx)
+ .panel_for_id(panel_id)
+ .cloned();
+
+ let Some(panel_handle) = panel_handle else {
+ continue;
+ };
+
+ let preferred_position = panel_handle.position(window, cx);
+ let target_position =
+ interpret_panel_dock_position(panel_handle.as_ref(), preferred_position, agent_mode);
+
+ if target_position != current_position {
+ workspace.move_panel_to_dock(panel_id, target_position, window, cx);
+ }
+ }
+}
+
fn setup_or_teardown_ai_panel<P: Panel>(
workspace: &mut Workspace,
window: &mut Window,
@@ -712,15 +800,18 @@ fn setup_or_teardown_ai_panel<P: Panel>(
|| cfg!(test);
let existing_panel = workspace.panel::<P>(cx);
match (disable_ai, existing_panel) {
- (false, None) => cx.spawn_in(window, async move |workspace, cx| {
+ (false, None) => cx.spawn_in(window, async move |workspace, mut cx| {
let panel = load_panel(workspace.clone(), cx.clone()).await?;
+ let agent_mode = is_agent_mode(&mut cx);
workspace.update_in(cx, |workspace, window, cx| {
let disable_ai = SettingsStore::global(cx)
.get::<DisableAiSettings>(None)
.disable_ai;
let have_panel = workspace.panel::<P>(cx).is_some();
if !disable_ai && !have_panel {
- workspace.add_panel(panel, window, cx);
+ let preferred = panel.position(window, cx);
+ let position = interpret_panel_dock_position(&panel, preferred, agent_mode);
+ workspace.add_panel(panel, position, window, cx);
}
})
}),