Handle moving the last surface out of a window
This commit is contained in:
parent
b57087f796
commit
d8022db404
3 changed files with 58 additions and 2 deletions
|
|
@ -1193,6 +1193,12 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
|
|||
}
|
||||
}
|
||||
|
||||
cleanupEmptySourceWorkspaceAfterSurfaceMove(
|
||||
sourceWorkspace: sourceWorkspace,
|
||||
sourceManager: source.tabManager,
|
||||
sourceWindowId: source.windowId
|
||||
)
|
||||
|
||||
if focus {
|
||||
if focusWindow, let destinationWindowId = windowId(for: destinationManager) {
|
||||
_ = focusMainWindow(windowId: destinationWindowId)
|
||||
|
|
@ -1435,6 +1441,21 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
|
|||
)
|
||||
}
|
||||
|
||||
private func cleanupEmptySourceWorkspaceAfterSurfaceMove(
|
||||
sourceWorkspace: Workspace,
|
||||
sourceManager: TabManager,
|
||||
sourceWindowId: UUID
|
||||
) {
|
||||
guard sourceWorkspace.panels.isEmpty else { return }
|
||||
guard sourceManager.tabs.contains(where: { $0.id == sourceWorkspace.id }) else { return }
|
||||
|
||||
if sourceManager.tabs.count > 1 {
|
||||
sourceManager.closeWorkspace(sourceWorkspace)
|
||||
} else {
|
||||
_ = closeMainWindow(windowId: sourceWindowId)
|
||||
}
|
||||
}
|
||||
|
||||
private func windowForMainWindowId(_ windowId: UUID) -> NSWindow? {
|
||||
if let ctx = mainWindowContexts.values.first(where: { $0.windowId == windowId }),
|
||||
let window = ctx.window {
|
||||
|
|
|
|||
|
|
@ -2671,9 +2671,16 @@ extension Workspace: BonsplitDelegate {
|
|||
lastTerminalConfigInheritancePanelId = nil
|
||||
}
|
||||
|
||||
// Keep the workspace invariant: always retain at least one real panel.
|
||||
// This prevents runtime close callbacks from ever collapsing into a tabless workspace.
|
||||
// Keep the workspace invariant for normal close paths.
|
||||
// Detach/move flows intentionally allow a temporary empty workspace so AppDelegate can
|
||||
// prune the source workspace/window after the tab is attached elsewhere.
|
||||
if panels.isEmpty {
|
||||
if isDetaching {
|
||||
scheduleTerminalGeometryReconcile()
|
||||
scheduleFocusReconcile()
|
||||
return
|
||||
}
|
||||
|
||||
let replacement = createReplacementTerminalPanel()
|
||||
if let replacementTabId = surfaceIdFromPanelId(replacement.id),
|
||||
let replacementPane = bonsplitController.allPaneIds.first {
|
||||
|
|
|
|||
|
|
@ -2898,6 +2898,34 @@ final class WorkspacePanelGitBranchTests: XCTestCase {
|
|||
)
|
||||
}
|
||||
|
||||
func testDetachLastSurfaceLeavesWorkspaceTemporarilyEmptyForMoveFlow() {
|
||||
let workspace = Workspace()
|
||||
guard let panelId = workspace.focusedPanelId,
|
||||
let paneId = workspace.paneId(forPanelId: panelId) else {
|
||||
XCTFail("Expected initial panel and pane")
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssertEqual(workspace.panels.count, 1)
|
||||
|
||||
guard let detached = workspace.detachSurface(panelId: panelId) else {
|
||||
XCTFail("Expected detach of last surface to succeed")
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssertEqual(detached.panelId, panelId)
|
||||
XCTAssertTrue(
|
||||
workspace.panels.isEmpty,
|
||||
"Detaching the last surface should not auto-create a replacement panel"
|
||||
)
|
||||
XCTAssertNil(workspace.surfaceIdFromPanelId(panelId))
|
||||
XCTAssertEqual(workspace.bonsplitController.tabs(inPane: paneId).count, 0)
|
||||
|
||||
let restoredPanelId = workspace.attachDetachedSurface(detached, inPane: paneId, focus: false)
|
||||
XCTAssertEqual(restoredPanelId, panelId)
|
||||
XCTAssertEqual(workspace.panels.count, 1)
|
||||
}
|
||||
|
||||
func testBrowserSplitWithFocusFalseRecoversFromDelayedStaleSelection() {
|
||||
let workspace = Workspace()
|
||||
guard let originalFocusedPanelId = workspace.focusedPanelId else {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue