import Foundation
|
|
public struct QuickSwitchSelection: Equatable, Sendable {
|
public let appGroupIndex: Int
|
public let windowIndex: Int
|
public let windowID: UInt32
|
|
public init(appGroupIndex: Int, windowIndex: Int, windowID: UInt32) {
|
self.appGroupIndex = appGroupIndex
|
self.windowIndex = windowIndex
|
self.windowID = windowID
|
}
|
}
|
|
public enum QuickSwitchKeyboardDirection: Equatable, Sendable {
|
case left
|
case right
|
case up
|
case down
|
}
|
|
public enum QuickSwitchVisualState: Equatable, Sendable {
|
case normal
|
case hover
|
case selected
|
case selectedAndHover
|
}
|
|
public enum QuickSwitchFocusPolicy {
|
public static func initialSelection(in snapshot: QuickSwitchSnapshot) -> QuickSwitchSelection? {
|
for (appGroupIndex, group) in snapshot.appGroups.enumerated() {
|
guard let firstWindow = group.windows.first else { continue }
|
|
return QuickSwitchSelection(
|
appGroupIndex: appGroupIndex,
|
windowIndex: 0,
|
windowID: firstWindow.window.id
|
)
|
}
|
|
return nil
|
}
|
|
public static func nextSelection(
|
from current: QuickSwitchSelection,
|
in snapshot: QuickSwitchSnapshot,
|
direction: QuickSwitchKeyboardDirection
|
) -> QuickSwitchSelection? {
|
nextSelection(from: current, in: snapshot, direction: direction, columnHistory: [:])
|
}
|
|
public static func nextSelection(
|
from current: QuickSwitchSelection,
|
in snapshot: QuickSwitchSnapshot,
|
direction: QuickSwitchKeyboardDirection,
|
columnHistory: [Int: Int]
|
) -> QuickSwitchSelection? {
|
guard snapshot.appGroups.indices.contains(current.appGroupIndex) else {
|
return initialSelection(in: snapshot)
|
}
|
|
let currentGroup = snapshot.appGroups[current.appGroupIndex]
|
guard currentGroup.windows.indices.contains(current.windowIndex) else {
|
return selection(appGroupIndex: current.appGroupIndex, windowIndex: 0, in: snapshot)
|
}
|
|
switch direction {
|
case .left:
|
return selectionInTargetColumn(
|
current: current,
|
targetAppGroupIndex: max(0, current.appGroupIndex - 1),
|
snapshot: snapshot,
|
columnHistory: columnHistory
|
)
|
case .right:
|
return selectionInTargetColumn(
|
current: current,
|
targetAppGroupIndex: min(snapshot.appGroups.count - 1, current.appGroupIndex + 1),
|
snapshot: snapshot,
|
columnHistory: columnHistory
|
)
|
case .up:
|
return selection(
|
appGroupIndex: current.appGroupIndex,
|
windowIndex: max(0, current.windowIndex - 1),
|
in: snapshot
|
)
|
case .down:
|
return selection(
|
appGroupIndex: current.appGroupIndex,
|
windowIndex: min(currentGroup.windows.count - 1, current.windowIndex + 1),
|
in: snapshot
|
)
|
}
|
}
|
|
public static func visualState(
|
for index: Int,
|
selectedIndex: Int?,
|
hoveredIndex: Int?
|
) -> QuickSwitchVisualState {
|
switch (selectedIndex == index, hoveredIndex == index) {
|
case (true, true):
|
return .selectedAndHover
|
case (true, false):
|
return .selected
|
case (false, true):
|
return .hover
|
case (false, false):
|
return .normal
|
}
|
}
|
|
private static func selectionInTargetColumn(
|
current: QuickSwitchSelection,
|
targetAppGroupIndex: Int,
|
snapshot: QuickSwitchSnapshot,
|
columnHistory: [Int: Int]
|
) -> QuickSwitchSelection? {
|
guard targetAppGroupIndex != current.appGroupIndex else {
|
return current
|
}
|
|
let rememberedWindowIndex = columnHistory[targetAppGroupIndex] ?? 0
|
return selection(
|
appGroupIndex: targetAppGroupIndex,
|
windowIndex: rememberedWindowIndex,
|
in: snapshot
|
)
|
}
|
|
private static func selection(
|
appGroupIndex: Int,
|
windowIndex: Int,
|
in snapshot: QuickSwitchSnapshot
|
) -> QuickSwitchSelection? {
|
guard snapshot.appGroups.indices.contains(appGroupIndex) else {
|
return nil
|
}
|
|
let group = snapshot.appGroups[appGroupIndex]
|
guard !group.windows.isEmpty else {
|
return nil
|
}
|
|
let boundedWindowIndex = min(max(windowIndex, 0), group.windows.count - 1)
|
return QuickSwitchSelection(
|
appGroupIndex: appGroupIndex,
|
windowIndex: boundedWindowIndex,
|
windowID: group.windows[boundedWindowIndex].window.id
|
)
|
}
|
}
|