Ariver
2026-06-18 1fcf6730652d509e78de4b80888772083f5b124a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
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
        )
    }
}