macos: implement goto_window:next/previous (#9899)
commit
2cc7341b08
|
|
@ -501,6 +501,9 @@ extension Ghostty {
|
||||||
case GHOSTTY_ACTION_GOTO_SPLIT:
|
case GHOSTTY_ACTION_GOTO_SPLIT:
|
||||||
return gotoSplit(app, target: target, direction: action.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:
|
case GHOSTTY_ACTION_RESIZE_SPLIT:
|
||||||
resizeSplit(app, target: target, resize: action.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(
|
private static func resizeSplit(
|
||||||
_ app: ghostty_app_t,
|
_ app: ghostty_app_t,
|
||||||
target: ghostty_target_s,
|
target: ghostty_target_s,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue