-
Notifications
You must be signed in to change notification settings - Fork 78
/
Copy pathWindow.swift
142 lines (113 loc) · 5.32 KB
/
Window.swift
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
// ∅ 2025 lil org
import Cocoa
struct Window {
private static var isClosingAllWindows = false
static func showNew(closeOthers: Bool) -> NSWindowController {
if closeOthers {
closeAll()
}
let windowController = new
activate(windowController)
if !closeOthers {
if let frame = windowController.window?.frame {
let stepX: CGFloat = 18
let stepY: CGFloat = 16
let topLeft = CGPoint(x: frame.minX, y: frame.maxY)
var validCascadeIndexes = [Int]()
let validateActiveSpace = windowController.window?.isOnActiveSpace == true
for otherWindow in NSApplication.shared.windows where otherWindow !== windowController.window {
if validateActiveSpace, !otherWindow.isOnActiveSpace { continue }
let otherTopLeft = CGPoint(x: otherWindow.frame.minX, y: otherWindow.frame.maxY)
let deltaX = otherTopLeft.x - topLeft.x
let deltaY = otherTopLeft.y - topLeft.y
if deltaX.truncatingRemainder(dividingBy: stepX).isZero, deltaY.truncatingRemainder(dividingBy: stepY).isZero {
let xIndex = deltaX / stepX
let yIndex = deltaY / stepY
if xIndex == yIndex {
validCascadeIndexes.append(Int(xIndex))
}
}
}
if let previousCascadeIndex = validCascadeIndexes.max() {
let cascadeIndex = CGFloat(previousCascadeIndex + 1)
let newTopLeft = CGPoint(x: topLeft.x + stepX * cascadeIndex, y: topLeft.y + stepY * cascadeIndex)
windowController.window?.setFrameTopLeftPoint(newTopLeft)
}
}
}
return windowController
}
static private func activate(_ windowController: NSWindowController) {
windowController.showWindow(nil)
activateWindow(windowController.window)
}
static func activateWindow(_ window: NSWindow?) {
NSApp.activate(ignoringOtherApps: true)
window?.makeKeyAndOrderFront(nil)
}
static func closeWindow(idToClose: Int?) {
guard !isClosingAllWindows else { return }
if let id = idToClose, let windowToClose = NSApplication.shared.windows.first(where: { $0.windowNumber == id }) {
windowToClose.close()
}
}
static func closeWindowAndActivateNext(idToClose: Int?, specificBrowser: Browser?) {
guard !isClosingAllWindows else { return }
closeWindow(idToClose: idToClose)
if let window = NSApplication.shared.windows.last(where: { $0.windowNumber != idToClose && $0.isOnActiveSpace && $0.contentViewController != nil }) {
activateWindow(window)
} else {
activateBrowser(specific: specificBrowser)
}
}
static func closeAllAndActivateBrowser(specific browser: Browser?) {
closeAll()
activateBrowser(specific: browser)
}
// MARK: - Private
private static func closeAll() {
isClosingAllWindows = true
NSApplication.shared.windows.forEach { window in
if window.className != "NSStatusBarWindow" {
window.close()
}
}
isClosingAllWindows = false
}
static func activateBrowser(specific browser: Browser?) {
if let browser = browser, browser != .unknown {
activateBrowser(browser)
return
}
let browsers = NSWorkspace.shared.runningApplications.filter { app in
if let bundleId = app.bundleIdentifier {
return Browser.allBundleIds.contains(bundleId)
} else {
return false
}
}
guard !browsers.isEmpty else { return }
let browsersPids = Set(browsers.map { $0.processIdentifier })
let options = CGWindowListOption(arrayLiteral: [.excludeDesktopElements, .optionOnScreenOnly])
guard let windows = CGWindowListCopyWindowInfo(options, CGWindowID(0)) as? [[String: AnyObject]] else { return }
for window in windows {
if let pid = window[kCGWindowOwnerPID as String] as? pid_t, browsersPids.contains(pid) {
browsers.first(where: { $0.processIdentifier == pid })?.activate()
return
}
}
}
private static func activateBrowser(_ browser: Browser) {
NSWorkspace.shared.runningApplications.first(where: { $0.bundleIdentifier == browser.rawValue })?.activate()
}
private static var new: NSWindowController {
// ⚠️ windows cascading relies on consistent initial window size
return NSStoryboard.main.instantiateController(withIdentifier: "initial") as! NSWindowController
}
}
extension NSStoryboard {
static let main = NSStoryboard(name: "Main", bundle: nil)
}
func instantiate<ViewController: NSViewController>(_ type: ViewController.Type) -> ViewController {
return NSStoryboard.main.instantiateController(withIdentifier: String(describing: type)) as! ViewController
}