Mission Control Turbo: macOS multitasking turbocharged
1import Foundation
2
3/// Pure geometry helpers for the layout engine.
4enum LayoutGeometry {
5 static let margin: CGFloat = 40
6 static let itemSpacing: CGFloat = 16
7 static let titleHeight: CGFloat = 36
8 static let minThumbnailWidth: CGFloat = 120
9
10 /// Find the optimal column count and thumbnail width for a flat grid of windows.
11 /// Tries each column count and picks the one that produces the largest thumbnails
12 /// while fitting everything on screen.
13 static func optimalGrid(
14 windows: [WindowInfo],
15 availableWidth: CGFloat,
16 availableHeight: CGFloat
17 ) -> (width: CGFloat, columns: Int) {
18 let count = windows.count
19 guard count > 0 else { return (200, 1) }
20
21 let maxCols = max(1, Int((availableWidth + itemSpacing) / (minThumbnailWidth + itemSpacing)))
22
23 var bestWidth: CGFloat = 0
24 var bestCols = 1
25
26 for cols in 1...maxCols {
27 let thumbWidth = (availableWidth - CGFloat(cols - 1) * itemSpacing) / CGFloat(cols)
28 guard thumbWidth >= minThumbnailWidth else { continue }
29
30 let needed = totalHeight(windows: windows, thumbWidth: thumbWidth, cols: cols)
31 if needed <= availableHeight && thumbWidth > bestWidth {
32 bestWidth = thumbWidth
33 bestCols = cols
34 }
35 }
36
37 // Binary search fallback if nothing fit
38 if bestWidth == 0 {
39 var lo: CGFloat = 20
40 var hi = availableWidth
41
42 for _ in 0..<40 {
43 let mid = (lo + hi) / 2
44 let cols = max(1, Int((availableWidth + itemSpacing) / (mid + itemSpacing)))
45 let needed = totalHeight(windows: windows, thumbWidth: mid, cols: cols)
46
47 if needed <= availableHeight {
48 bestWidth = mid
49 bestCols = cols
50 lo = mid
51 } else {
52 hi = mid
53 }
54 }
55
56 if bestWidth == 0 {
57 bestCols = max(1, Int(ceil(sqrt(Double(count)))))
58 bestWidth = max(20, (availableWidth - CGFloat(bestCols - 1) * itemSpacing) / CGFloat(bestCols))
59 }
60 }
61
62 return (bestWidth, bestCols)
63 }
64
65 /// Compute total height for a flat grid layout.
66 static func totalHeight(windows: [WindowInfo], thumbWidth: CGFloat, cols: Int) -> CGFloat {
67 let rowCount = Int(ceil(Double(windows.count) / Double(cols)))
68 var height: CGFloat = 0
69
70 for row in 0..<rowCount {
71 let start = row * cols
72 let end = min(start + cols, windows.count)
73 var maxRowH: CGFloat = 0
74 for idx in start..<end {
75 let w = windows[idx]
76 let ratio = w.frame.width > 0 ? w.frame.height / w.frame.width : 0.625
77 let cellH = thumbWidth * ratio + titleHeight
78 maxRowH = max(maxRowH, cellH)
79 }
80 height += maxRowH
81 if row < rowCount - 1 { height += itemSpacing }
82 }
83
84 return height
85 }
86
87 /// Fit a source rect into a target rect maintaining aspect ratio.
88 static func aspectFit(source: CGSize, target: CGSize) -> CGSize {
89 guard source.width > 0, source.height > 0 else { return target }
90 let scale = min(target.width / source.width, target.height / source.height)
91 return CGSize(width: source.width * scale, height: source.height * scale)
92 }
93}