macos: implement goto_window:next/previous (#9899)
commit
2cc7341b08
|
|
@ -501,6 +501,9 @@ extension Ghostty {
|
|||
case GHOSTTY_ACTION_GOTO_SPLIT:
|
||||
return gotoSplit(app, target: target, direction: action.action.goto_split)
|
||||
|
||||
case GHOSTTY_ACTION_GOTO_WINDOW:
|
||||
return gotoWindow(app, target: target, direction: action.action.goto_window)
|
||||
|
||||
case GHOSTTY_ACTION_RESIZE_SPLIT:
|
||||
resizeSplit(app, target: target, resize: action.action.resize_split)
|
||||
|
||||
|
|
@ -1149,6 +1152,64 @@ extension Ghostty {
|
|||
}
|
||||
}
|
||||
|
||||
private static func gotoWindow(
|
||||
_ app: ghostty_app_t,
|
||||
target: ghostty_target_s,
|
||||
direction: ghostty_action_goto_window_e
|
||||
) -> Bool {
|
||||
// Collect candidate windows: visible terminal windows that are either
|
||||
// standalone or the currently selected tab in their tab group. This
|
||||
// treats each native tab group as a single "window" for navigation
|
||||
// purposes, since goto_tab handles per-tab navigation.
|
||||
let candidates: [NSWindow] = NSApplication.shared.windows.filter { window in
|
||||
guard window.windowController is BaseTerminalController else { return false }
|
||||
guard window.isVisible, !window.isMiniaturized else { return false }
|
||||
// For native tabs, only include the selected tab in each group
|
||||
if let group = window.tabGroup, group.selectedWindow !== window {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Need at least two windows to navigate between
|
||||
guard candidates.count > 1 else { return false }
|
||||
|
||||
// Find starting index from the current key/main window
|
||||
let startIndex = candidates.firstIndex(where: { $0.isKeyWindow })
|
||||
?? candidates.firstIndex(where: { $0.isMainWindow })
|
||||
?? 0
|
||||
|
||||
let step: Int
|
||||
switch direction {
|
||||
case GHOSTTY_GOTO_WINDOW_NEXT:
|
||||
step = 1
|
||||
case GHOSTTY_GOTO_WINDOW_PREVIOUS:
|
||||
step = -1
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
// Iterate with wrap-around until we find a valid window or return to start
|
||||
let count = candidates.count
|
||||
var index = (startIndex + step + count) % count
|
||||
|
||||
while index != startIndex {
|
||||
let candidate = candidates[index]
|
||||
if candidate.isVisible, !candidate.isMiniaturized {
|
||||
candidate.makeKeyAndOrderFront(nil)
|
||||
// Also focus the terminal surface within the window
|
||||
if let controller = candidate.windowController as? BaseTerminalController,
|
||||
let surface = controller.focusedSurface {
|
||||
Ghostty.moveFocus(to: surface)
|
||||
}
|
||||
return true
|
||||
}
|
||||
index = (index + step + count) % count
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private static func resizeSplit(
|
||||
_ app: ghostty_app_t,
|
||||
target: ghostty_target_s,
|
||||
|
|
|
|||
Loading…
Reference in New Issue