@@ -18,126 +18,192 @@ import Cocoa
18
18
19
19
class WifiMenuItemView : NSView {
20
20
21
- let statusImage : NSImageView = {
21
+ // MARK: Initializers
22
+
23
+ private var currentWindow : NSWindow ?
24
+ private var heightConstraint : NSLayoutConstraint !
25
+
26
+ private let menuBarHeight : CGFloat = {
27
+ if #available( macOS 11 , * ) {
28
+ return 22
29
+ } else {
30
+ return 19
31
+ }
32
+ } ( )
33
+
34
+ private let effectView : NSVisualEffectView = {
35
+ let effectView = NSVisualEffectView ( )
36
+ effectView. material = . popover
37
+ effectView. state = . active
38
+ effectView. isEmphasized = true
39
+ effectView. blendingMode = . behindWindow
40
+ return effectView
41
+ } ( )
42
+
43
+ private let statusImage : NSImageView = {
22
44
let statusImage = NSImageView ( )
23
- statusImage. image = NSImage ( named: NSImage . menuOnStateTemplateName)
24
- statusImage. image? . isTemplate = true
25
- statusImage. translatesAutoresizingMaskIntoConstraints = false
26
45
statusImage. setContentHuggingPriority ( . defaultHigh, for: . horizontal)
27
46
statusImage. isHidden = true
28
- return statusImage
29
- } ( )
30
47
31
- let ssidLabel : NSTextField = {
32
- let ssidLabel = NSTextField ( )
33
- ssidLabel. isBordered = false
34
- ssidLabel. usesSingleLineMode = true
35
- ssidLabel. maximumNumberOfLines = 1
36
- ssidLabel. drawsBackground = false
37
- ssidLabel. isEditable = false
38
- ssidLabel. isSelectable = false
39
- ssidLabel. font = NSFont . systemFont ( ofSize: 14 )
40
- ssidLabel. translatesAutoresizingMaskIntoConstraints = false
41
- return ssidLabel
48
+ if #available( OSX 11 . 0 , * ) {
49
+ statusImage. image = NSImage ( named: NSImage . menuOnStateTemplateName) ?
50
+ . withSymbolConfiguration ( NSImage . SymbolConfiguration ( pointSize: 13 ,
51
+ weight: . bold,
52
+ scale: . small) )
53
+ } else {
54
+ statusImage. image = NSImage ( named: NSImage . menuOnStateTemplateName)
55
+ }
56
+
57
+ return statusImage
42
58
} ( )
43
59
44
- let lockImage : NSImageView = {
60
+ private let lockImage : NSImageView = {
45
61
let lockImage = NSImageView ( )
46
- lockImage. image = NSImage . init ( named: NSImage . lockLockedTemplateName)
47
- lockImage. image? . isTemplate = true
48
- lockImage. translatesAutoresizingMaskIntoConstraints = false
49
62
lockImage. setContentHuggingPriority ( . defaultHigh, for: . horizontal)
63
+
64
+ if #available( OSX 11 . 0 , * ) {
65
+ lockImage. image = NSImage ( named: NSImage . lockLockedTemplateName) ?
66
+ . withSymbolConfiguration ( NSImage . SymbolConfiguration ( pointSize: 14 ,
67
+ weight: . semibold,
68
+ scale: . medium) )
69
+ } else {
70
+ lockImage. image = NSImage ( named: NSImage . lockLockedTemplateName)
71
+ }
72
+
50
73
return lockImage
51
74
} ( )
52
75
53
- let signalImage : NSImageView = {
76
+ private let signalImage : NSImageView = {
54
77
let signalImage = NSImageView ( )
55
- signalImage. image? . isTemplate = true
56
- signalImage. translatesAutoresizingMaskIntoConstraints = false
57
78
signalImage. setContentHuggingPriority ( . defaultHigh, for: . horizontal)
58
79
return signalImage
59
80
} ( )
60
81
61
- var isMouseOver : Bool = false {
62
- willSet( hover) {
63
- ( superview as? NSVisualEffectView ) ? . material = hover ? . selection : . popover
64
- ( superview as? NSVisualEffectView ) ? . isEmphasized = hover
65
- ssidLabel. textColor = hover ? . white : . textColor
66
- if #available( OSX 10 . 14 , * ) {
67
- statusImage. contentTintColor = hover ? . white : . textColor
68
- lockImage. contentTintColor = hover ? . white : . textColor
69
- signalImage. contentTintColor = hover ? . white : . textColor
70
- }
82
+ private let ssidLabel : NSTextField = {
83
+ let ssidLabel = NSTextField ( labelWithString: " " )
84
+
85
+ if #available( macOS 11 , * ) {
86
+ ssidLabel. font = NSFont . menuFont ( ofSize: 0 )
87
+ } else {
88
+ ssidLabel. font = NSFont . systemFont ( ofSize: 14 )
89
+ }
90
+
91
+ return ssidLabel
92
+ } ( )
93
+
94
+ public init ( networkInfo: NetworkInfo ) {
95
+ self . networkInfo = networkInfo
96
+ super. init ( frame: NSRect ( x: 0 , y: 0 , width: 0 , height: menuBarHeight) )
97
+
98
+ self . addSubview ( effectView)
99
+ effectView. addSubview ( statusImage)
100
+ effectView. addSubview ( ssidLabel)
101
+ effectView. addSubview ( lockImage)
102
+ effectView. addSubview ( signalImage)
103
+
104
+ setupLayout ( )
105
+ }
106
+
107
+ required init ? ( coder: NSCoder ) {
108
+ fatalError ( " init(coder:) has not been implemented " )
109
+ }
110
+
111
+ // MARK: Public
112
+
113
+ public var networkInfo : NetworkInfo {
114
+ willSet( networkInfo) {
115
+ ssidLabel. stringValue = networkInfo. ssid
116
+ lockImage. isHidden = networkInfo. auth. security == ITL80211_SECURITY_NONE
117
+ signalImage. image = StatusBarIcon . getRssiImage ( Int16 ( networkInfo. rssi) )
118
+ layoutSubtreeIfNeeded ( )
71
119
}
72
120
}
73
121
74
- var visible : Bool = true {
122
+ public var visible : Bool = true {
75
123
willSet( visible) {
76
- heightConstraint. constant = visible ? 19 : 0
124
+ isHidden = !visible
125
+ heightConstraint. constant = visible ? menuBarHeight : 0
77
126
layoutSubtreeIfNeeded ( )
78
127
}
79
128
}
80
129
81
- var connected : Bool = false {
130
+ public var connected : Bool = false {
82
131
willSet( connected) {
83
132
statusImage. isHidden = !connected
84
133
}
85
134
}
86
135
87
- var currentWindow : NSWindow ?
136
+ public func checkHighlight( ) {
137
+ if visible, let position = currentWindow? . mouseLocationOutsideOfEventStream {
138
+ isMouseOver = bounds. contains ( convert ( position, from: nil ) )
139
+ }
140
+ }
141
+
142
+ // MARK: Private
88
143
89
- var networkInfo : NetworkInfo {
90
- willSet( networkInfo) {
91
- ssidLabel. stringValue = networkInfo. ssid
92
- lockImage. isHidden = networkInfo. auth. security == ITL80211_SECURITY_NONE
93
- signalImage. image = WifiMenuItemView . getRssiImage ( networkInfo. rssi)
94
- layoutSubtreeIfNeeded ( )
144
+ private var isMouseOver : Bool = false {
145
+ willSet( hover) {
146
+ effectView. material = hover ? . selection : . popover
147
+ effectView. isEmphasized = hover
148
+
149
+ ssidLabel. textColor = hover ? . selectedMenuItemTextColor : . controlTextColor
150
+
151
+ statusImage. cell? . backgroundStyle = hover ? . emphasized : . normal
152
+ lockImage. cell? . backgroundStyle = hover ? . emphasized : . normal
153
+ signalImage. cell? . backgroundStyle = hover ? . emphasized : . normal
95
154
}
96
155
}
97
156
98
- var heightConstraint : NSLayoutConstraint !
157
+ private func setupLayout( ) {
158
+
159
+ let effectPadding : CGFloat
160
+ let statusPadding : CGFloat
161
+ let statusWidth : CGFloat
162
+ let lockWidth : CGFloat
163
+ if #available( macOS 11 , * ) {
164
+ effectView. wantsLayer = true
165
+ effectView. layer? . cornerRadius = 4
166
+ effectView. layer? . masksToBounds = true
167
+ effectPadding = 5
168
+ statusPadding = 10
169
+ statusWidth = 15
170
+ lockWidth = 16
171
+ } else {
172
+ effectPadding = 0
173
+ statusPadding = 6
174
+ statusWidth = 12
175
+ lockWidth = 10
176
+ }
99
177
100
- func setupLayout( ) {
101
- heightConstraint = heightAnchor. constraint ( equalToConstant: 19 )
178
+ heightConstraint = heightAnchor. constraint ( equalToConstant: menuBarHeight)
102
179
heightConstraint. priority = NSLayoutConstraint . Priority ( rawValue: 1000 )
103
180
heightConstraint. isActive = true
104
181
105
182
statusImage. centerYAnchor. constraint ( equalTo: self . centerYAnchor) . isActive = true
106
- statusImage. leadingAnchor. constraint ( equalTo: self . leadingAnchor, constant: 6 ) . isActive = true
107
- statusImage. widthAnchor. constraint ( equalToConstant: 12 ) . isActive = true
183
+ statusImage. leadingAnchor. constraint ( equalTo: self . leadingAnchor, constant: statusPadding ) . isActive = true
184
+ statusImage. widthAnchor. constraint ( equalToConstant: statusWidth ) . isActive = true
108
185
109
186
ssidLabel. centerYAnchor. constraint ( equalTo: self . centerYAnchor) . isActive = true
110
187
ssidLabel. leadingAnchor. constraint ( equalTo: statusImage. trailingAnchor, constant: 3 ) . isActive = true
111
- ssidLabel. topAnchor. constraint ( equalTo: self . topAnchor) . isActive = true
112
- ssidLabel. bottomAnchor. constraint ( equalTo: self . bottomAnchor) . isActive = true
113
188
114
189
lockImage. centerYAnchor. constraint ( equalTo: self . centerYAnchor) . isActive = true
115
190
lockImage. leadingAnchor. constraint ( equalTo: ssidLabel. trailingAnchor, constant: 10 ) . isActive = true
116
- lockImage. widthAnchor. constraint ( equalToConstant: 10 ) . isActive = true
191
+ lockImage. widthAnchor. constraint ( equalToConstant: lockWidth ) . isActive = true
117
192
118
193
signalImage. centerYAnchor. constraint ( equalTo: self . centerYAnchor, constant: 1 ) . isActive = true
119
194
signalImage. leadingAnchor. constraint ( equalTo: lockImage. trailingAnchor, constant: 12 ) . isActive = true
120
195
signalImage. trailingAnchor. constraint ( equalTo: self . trailingAnchor, constant: - 12 ) . isActive = true
121
196
signalImage. widthAnchor. constraint ( equalToConstant: 18 ) . isActive = true
122
- }
123
197
124
- init ( networkInfo: NetworkInfo ) {
125
- self . networkInfo = networkInfo
126
- super. init ( frame: NSRect . zero)
127
-
128
- self . addSubview ( statusImage)
129
- self . addSubview ( ssidLabel)
130
- self . addSubview ( lockImage)
131
- self . addSubview ( signalImage)
132
-
133
- setupLayout ( )
198
+ effectView. translatesAutoresizingMaskIntoConstraints = false
199
+ effectView. subviews. forEach { $0. translatesAutoresizingMaskIntoConstraints = false }
200
+ effectView. leftAnchor. constraint ( equalTo: self . leftAnchor, constant: effectPadding) . isActive = true
201
+ effectView. rightAnchor. constraint ( equalTo: self . rightAnchor, constant: - effectPadding) . isActive = true
202
+ effectView. topAnchor. constraint ( equalTo: topAnchor) . isActive = true
203
+ effectView. bottomAnchor. constraint ( equalTo: bottomAnchor) . isActive = true
134
204
}
135
205
136
- func checkHighlight( ) {
137
- if visible, let position = currentWindow? . mouseLocationOutsideOfEventStream {
138
- isMouseOver = bounds. contains ( convert ( position, from: nil ) )
139
- }
140
- }
206
+ // MARK: Overrides
141
207
142
208
override func mouseUp( with event: NSEvent ) {
143
209
isMouseOver = false // NSWindow pop up could escape mouseExit
@@ -159,22 +225,15 @@ class WifiMenuItemView: NSView {
159
225
checkHighlight ( )
160
226
}
161
227
162
- required init ? ( coder: NSCoder ) {
163
- fatalError ( " init(coder:) has not been implemented " )
164
- }
165
-
166
- class func getRssiImage( _ RSSI: Int ) -> NSImage ? {
167
- var signalImageName : NSImage
168
- switch RSSI {
169
- case ..< ( - 100 ) :
170
- signalImageName = #imageLiteral( resourceName: " WiFiStateScanning1 " )
171
- case ..< ( - 80 ) :
172
- signalImageName = #imageLiteral( resourceName: " WiFiSignalStrengthFair " )
173
- case ..< ( - 60 ) :
174
- signalImageName = #imageLiteral( resourceName: " WiFiSignalStrengthGood " )
175
- default :
176
- signalImageName = #imageLiteral( resourceName: " WiFiStateOn " )
228
+ override func layout( ) {
229
+ super. layout ( )
230
+ if #available( macOS 11 , * ) {
231
+ effectView. frame = CGRect ( x: 5 , // effectPadding
232
+ y: 0 ,
233
+ width: bounds. width - 10 , // effectPadding * 2
234
+ height: bounds. height)
235
+ } else {
236
+ effectView. frame = bounds
177
237
}
178
- return signalImageName
179
238
}
180
239
}
0 commit comments