/
SettingsWindowController.swift
163 lines (135 loc) · 4.38 KB
/
SettingsWindowController.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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
import Cocoa
extension NSWindow.FrameAutosaveName {
static let settings: NSWindow.FrameAutosaveName = "com.sindresorhus.Preferences.FrameAutosaveName"
}
public final class SettingsWindowController: NSWindowController {
private let tabViewController = SettingsTabViewController()
public var isAnimated: Bool {
get { tabViewController.isAnimated }
set {
tabViewController.isAnimated = newValue
}
}
public var hidesToolbarForSingleItem: Bool {
didSet {
updateToolbarVisibility()
}
}
private func updateToolbarVisibility() {
window?.toolbar?.isVisible = (hidesToolbarForSingleItem == false)
|| (tabViewController.settingsPanesCount > 1)
}
public init(
panes: [SettingsPane],
style: Settings.Style = .toolbarItems,
animated: Bool = true,
hidesToolbarForSingleItem: Bool = true
) {
precondition(!panes.isEmpty, "You need to set at least one pane")
let window = UserInteractionPausableWindow(
contentRect: panes[0].view.bounds,
styleMask: [
.titled,
.closable
],
backing: .buffered,
defer: true
)
self.hidesToolbarForSingleItem = hidesToolbarForSingleItem
super.init(window: window)
window.contentViewController = tabViewController
window.titleVisibility = {
switch style {
case .toolbarItems:
return .visible
case .segmentedControl:
return panes.count <= 1 ? .visible : .hidden
}
}()
if #available(macOS 11.0, *), style == .toolbarItems {
window.toolbarStyle = .preference
}
tabViewController.isAnimated = animated
tabViewController.configure(panes: panes, style: style)
updateToolbarVisibility()
}
@available(*, unavailable)
override public init(window: NSWindow?) {
fatalError("init(window:) is not supported, use init(panes:style:animated:hidesToolbarForSingleItem:)")
}
@available(*, unavailable)
public required init?(coder: NSCoder) {
fatalError("init(coder:) is not supported, use init(panes:style:animated:hidesToolbarForSingleItem:hidesToolbarForSingleItem:)")
}
/**
Show the settings window and brings it to front.
If you pass a `Settings.PaneIdentifier`, the window will activate the corresponding tab.
- Parameter paneIdentifier: Identifier of the settings pane to display, or `nil` to show the tab that was open when the user last closed the window.
- Note: Unless you need to open a specific pane, prefer not to pass a parameter at all or `nil`.
- See `close()` to close the window again.
- See `showWindow(_:)` to show the window without the convenience of activating the app.
*/
public func show(pane paneIdentifier: Settings.PaneIdentifier? = nil) {
if let paneIdentifier {
tabViewController.activateTab(paneIdentifier: paneIdentifier, animated: false)
} else {
tabViewController.restoreInitialTab()
}
showWindow(self)
restoreWindowPosition()
NSApp.activate(ignoringOtherApps: true)
}
private func restoreWindowPosition() {
guard
let window,
let screenContainingWindow = window.screen
else {
return
}
window.setFrameOrigin(CGPoint(
x: screenContainingWindow.visibleFrame.midX - window.frame.width / 2,
y: screenContainingWindow.visibleFrame.midY - window.frame.height / 2
))
window.setFrameUsingName(.settings)
window.setFrameAutosaveName(.settings)
}
}
extension SettingsWindowController {
/**
Returns the active pane if it responds to the given action.
*/
override public func supplementalTarget(forAction action: Selector, sender: Any?) -> Any? {
if let target = super.supplementalTarget(forAction: action, sender: sender) {
return target
}
guard let activeViewController = tabViewController.activeViewController else {
return nil
}
if let target = NSApp.target(forAction: action, to: activeViewController, from: sender) as? NSResponder, target.responds(to: action) {
return target
}
if let target = activeViewController.supplementalTarget(forAction: action, sender: sender) as? NSResponder, target.responds(to: action) {
return target
}
return nil
}
}
@available(macOS 10.15, *)
extension SettingsWindowController {
/**
Create a settings window from only SwiftUI-based settings panes.
*/
public convenience init(
panes: [SettingsPaneConvertible],
style: Settings.Style = .toolbarItems,
animated: Bool = true,
hidesToolbarForSingleItem: Bool = true
) {
self.init(
panes: panes.map { $0.asSettingsPane() },
style: style,
animated: animated,
hidesToolbarForSingleItem: hidesToolbarForSingleItem
)
}
}