sidebar: Move to new window, going through sqlite (#53513)

Re-adds the `multi_workspace::MoveProjectToNewWindow` action

It serializes, then closes, then re-opens the project to avoid issues
with pending tasks

Release Notes:

- N/A or Added/Fixed/Improved ...
This commit is contained in:
Cameron Mcloughlin authored and GitHub committed 2026-04-13 10:02:14 -07:00
1 parent 412683f52d
commit 35566bca98
3 files changed
+95 -9

No files matched your search

+30
View File
@@ -1972,6 +1972,10 @@ impl Sidebar {
let multi_workspace = multi_workspace.clone();
let project_group_key = project_group_key.clone();
let has_multiple_projects = multi_workspace
.read_with(cx, |mw, _| mw.project_group_keys().len() >= 2)
.unwrap_or(false);
let menu =
ContextMenu::build_persistent(window, cx, move |menu, _window, menu_cx| {
let weak_menu = menu_cx.weak_entity();
@@ -2031,6 +2035,32 @@ impl Sidebar {
},
);
let menu = if project_group_key.host().is_none() && has_multiple_projects {
menu.entry(
"Open Project in New Window",
Some(Box::new(workspace::MoveProjectToNewWindow)),
{
let project_group_key = project_group_key.clone();
let multi_workspace = multi_workspace.clone();
move |window, cx| {
multi_workspace
.update(cx, |multi_workspace, cx| {
multi_workspace
.open_project_group_in_new_window(
&project_group_key,
window,
cx,
)
.detach_and_log_err(cx);
})
.ok();
}
},
)
} else {
menu
};
let project_group_key = project_group_key.clone();
let multi_workspace = multi_workspace.clone();
menu.separator()
+60 -4
View File
@@ -54,6 +54,8 @@ actions!(
ShowFewerThreads,
/// Creates a new thread in the current workspace.
NewThread,
/// Moves the active project to a new window.
MoveProjectToNewWindow,
]
);
@@ -1017,6 +1019,50 @@ impl MultiWorkspace {
)
}
/// Goes through sqlite: serialize -> close -> open new window
/// This avoids issues with pending tasks having the wrong window
pub fn open_project_group_in_new_window(
&mut self,
key: &ProjectGroupKey,
window: &mut Window,
cx: &mut Context<Self>,
) -> Task<Result<()>> {
let paths: Vec<PathBuf> = key.path_list().ordered_paths().cloned().collect();
if paths.is_empty() {
return Task::ready(Ok(()));
}
let app_state = self.workspace().read(cx).app_state().clone();
let workspaces: Vec<_> = self
.workspaces_for_project_group(key, cx)
.unwrap_or_default();
let mut serialization_tasks = Vec::new();
for workspace in &workspaces {
serialization_tasks.push(workspace.update(cx, |workspace, inner_cx| {
workspace.flush_serialization(window, inner_cx)
}));
}
let remove_task = self.remove_project_group(key, window, cx);
cx.spawn(async move |_this, cx| {
futures::future::join_all(serialization_tasks).await;
let removed = remove_task.await?;
if !removed {
return Ok(());
}
cx.update(|cx| {
Workspace::new_local(paths, app_state, None, None, None, OpenMode::NewWindow, cx)
})
.await?;
Ok(())
})
}
/// Finds an existing workspace whose root paths and host exactly match.
pub fn workspace_for_paths(
&self,
@@ -1836,13 +1882,23 @@ impl Render for MultiWorkspace {
sidebar.cycle_thread(true, window, cx);
}
}))
.on_action(cx.listener(
|this: &mut Self, _: &PreviousThread, window, cx| {
.on_action(
cx.listener(|this: &mut Self, _: &PreviousThread, window, cx| {
if let Some(sidebar) = &this.sidebar {
sidebar.cycle_thread(false, window, cx);
}
},
))
}),
)
.when(self.project_group_keys().len() >= 2, |el| {
el.on_action(cx.listener(
|this: &mut Self, _: &MoveProjectToNewWindow, window, cx| {
let key =
this.project_group_key_for_workspace(this.workspace(), cx);
this.open_project_group_in_new_window(&key, window, cx)
.detach_and_log_err(cx);
},
))
})
})
.when(
self.sidebar_open() && self.multi_workspace_enabled(cx),
+5 -5
View File
@@ -31,11 +31,11 @@ mod workspace_settings;
pub use crate::notifications::NotificationFrame;
pub use dock::Panel;
pub use multi_workspace::{
CloseWorkspaceSidebar, DraggedSidebar, FocusWorkspaceSidebar, MultiWorkspace,
MultiWorkspaceEvent, NewThread, NextProject, NextThread, PreviousProject, PreviousThread,
ProjectGroup, ProjectGroupKey, SerializedProjectGroupState, ShowFewerThreads, ShowMoreThreads,
Sidebar, SidebarEvent, SidebarHandle, SidebarRenderState, SidebarSide, ToggleWorkspaceSidebar,
sidebar_side_context_menu,
CloseWorkspaceSidebar, DraggedSidebar, FocusWorkspaceSidebar, MoveProjectToNewWindow,
MultiWorkspace, MultiWorkspaceEvent, NewThread, NextProject, NextThread, PreviousProject,
PreviousThread, ProjectGroup, ProjectGroupKey, SerializedProjectGroupState, ShowFewerThreads,
ShowMoreThreads, Sidebar, SidebarEvent, SidebarHandle, SidebarRenderState, SidebarSide,
ToggleWorkspaceSidebar, sidebar_side_context_menu,
};
pub use path_list::{PathList, SerializedPathList};
pub use toast_layer::{ToastAction, ToastLayer, ToastView};