|
|
@@ -215,17 +215,45 @@ private final class SettingsIconBadge: NSView, AppearanceRefreshable {
|
|
215
|
215
|
|
|
216
|
216
|
// MARK: - Rows
|
|
217
|
217
|
|
|
218
|
|
-private class SettingsRowBase: NSView {
|
|
219
|
|
- init(isLast: Bool) {
|
|
|
218
|
+private class SettingsRowBase: NSView, AppearanceRefreshable {
|
|
|
219
|
+ private var hoverTracker: HoverTracker?
|
|
|
220
|
+ private var isHovered = false
|
|
|
221
|
+ private let isInteractive: Bool
|
|
|
222
|
+
|
|
|
223
|
+ init(isLast: Bool, isInteractive: Bool = false) {
|
|
|
224
|
+ self.isInteractive = isInteractive
|
|
220
|
225
|
super.init(frame: .zero)
|
|
221
|
226
|
translatesAutoresizingMaskIntoConstraints = false
|
|
222
|
227
|
heightAnchor.constraint(equalToConstant: 56).isActive = true
|
|
223
|
228
|
if !isLast { addDivider() }
|
|
|
229
|
+
|
|
|
230
|
+ if isInteractive {
|
|
|
231
|
+ wantsLayer = true
|
|
|
232
|
+ hoverTracker = HoverTracker(view: self) { [weak self] hovering in
|
|
|
233
|
+ self?.setHovered(hovering)
|
|
|
234
|
+ }
|
|
|
235
|
+ }
|
|
224
|
236
|
}
|
|
225
|
237
|
|
|
226
|
238
|
@available(*, unavailable)
|
|
227
|
239
|
required init?(coder: NSCoder) { nil }
|
|
228
|
240
|
|
|
|
241
|
+ func refreshAppearance() {
|
|
|
242
|
+ updateHoverAppearance()
|
|
|
243
|
+ }
|
|
|
244
|
+
|
|
|
245
|
+ private func setHovered(_ hovering: Bool) {
|
|
|
246
|
+ isHovered = hovering
|
|
|
247
|
+ updateHoverAppearance()
|
|
|
248
|
+ }
|
|
|
249
|
+
|
|
|
250
|
+ private func updateHoverAppearance() {
|
|
|
251
|
+ guard isInteractive else { return }
|
|
|
252
|
+ animateHover {
|
|
|
253
|
+ layer?.backgroundColor = (isHovered ? AppTheme.sidebarHoverBackground : .clear).cgColor
|
|
|
254
|
+ }
|
|
|
255
|
+ }
|
|
|
256
|
+
|
|
229
|
257
|
func addDivider() {
|
|
230
|
258
|
let divider = NSBox()
|
|
231
|
259
|
divider.boxType = .separator
|
|
|
@@ -267,7 +295,7 @@ private class SettingsRowBase: NSView {
|
|
267
|
295
|
|
|
268
|
296
|
private final class SettingsActionRow: SettingsRowBase {
|
|
269
|
297
|
init(symbolName: String, title: String, isLast: Bool = false, action: @escaping () -> Void) {
|
|
270
|
|
- super.init(isLast: isLast)
|
|
|
298
|
+ super.init(isLast: isLast, isInteractive: true)
|
|
271
|
299
|
|
|
272
|
300
|
let badge = SettingsIconBadge(symbolName: symbolName)
|
|
273
|
301
|
let titleLabel = NSTextField.themeLabel(title, style: .primary, font: AppTheme.mediumFont(size: 15))
|
|
|
@@ -304,7 +332,7 @@ private final class SettingsPopupRow: SettingsRowBase {
|
|
304
|
332
|
|
|
305
|
333
|
init(symbolName: String, title: String, options: [String], selection: String, isLast: Bool = false, onChange: @escaping (String) -> Void) {
|
|
306
|
334
|
popupTarget = PopupTarget(handler: onChange)
|
|
307
|
|
- super.init(isLast: isLast)
|
|
|
335
|
+ super.init(isLast: isLast, isInteractive: true)
|
|
308
|
336
|
|
|
309
|
337
|
let popup = NSPopUpButton()
|
|
310
|
338
|
popup.bezelStyle = .rounded
|
|
|
@@ -326,7 +354,7 @@ private final class SettingsToggleRow: SettingsRowBase {
|
|
326
|
354
|
|
|
327
|
355
|
init(symbolName: String, title: String, isOn: Bool, isLast: Bool = false, onChange: @escaping (Bool) -> Void) {
|
|
328
|
356
|
toggleTarget = ToggleTarget(handler: onChange)
|
|
329
|
|
- super.init(isLast: isLast)
|
|
|
357
|
+ super.init(isLast: isLast, isInteractive: true)
|
|
330
|
358
|
|
|
331
|
359
|
let toggle = NSSwitch()
|
|
332
|
360
|
toggle.state = isOn ? .on : .off
|