瀏覽代碼

Add hover feedback to settings list rows.

Match the sidebar interaction pattern so action, toggle, and popup rows highlight on pointer hover.

Co-authored-by: Cursor <cursoragent@cursor.com>
AhtashamShahzad1 4 小時之前
父節點
當前提交
bce13572cd
共有 1 個文件被更改,包括 33 次插入5 次删除
  1. 33 5
      smart_printer/SettingsView.swift

+ 33 - 5
smart_printer/SettingsView.swift

@@ -215,17 +215,45 @@ private final class SettingsIconBadge: NSView, AppearanceRefreshable {
215
 
215
 
216
 // MARK: - Rows
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
         super.init(frame: .zero)
225
         super.init(frame: .zero)
221
         translatesAutoresizingMaskIntoConstraints = false
226
         translatesAutoresizingMaskIntoConstraints = false
222
         heightAnchor.constraint(equalToConstant: 56).isActive = true
227
         heightAnchor.constraint(equalToConstant: 56).isActive = true
223
         if !isLast { addDivider() }
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
     @available(*, unavailable)
238
     @available(*, unavailable)
227
     required init?(coder: NSCoder) { nil }
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
     func addDivider() {
257
     func addDivider() {
230
         let divider = NSBox()
258
         let divider = NSBox()
231
         divider.boxType = .separator
259
         divider.boxType = .separator
@@ -267,7 +295,7 @@ private class SettingsRowBase: NSView {
267
 
295
 
268
 private final class SettingsActionRow: SettingsRowBase {
296
 private final class SettingsActionRow: SettingsRowBase {
269
     init(symbolName: String, title: String, isLast: Bool = false, action: @escaping () -> Void) {
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
         let badge = SettingsIconBadge(symbolName: symbolName)
300
         let badge = SettingsIconBadge(symbolName: symbolName)
273
         let titleLabel = NSTextField.themeLabel(title, style: .primary, font: AppTheme.mediumFont(size: 15))
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
     init(symbolName: String, title: String, options: [String], selection: String, isLast: Bool = false, onChange: @escaping (String) -> Void) {
333
     init(symbolName: String, title: String, options: [String], selection: String, isLast: Bool = false, onChange: @escaping (String) -> Void) {
306
         popupTarget = PopupTarget(handler: onChange)
334
         popupTarget = PopupTarget(handler: onChange)
307
-        super.init(isLast: isLast)
335
+        super.init(isLast: isLast, isInteractive: true)
308
 
336
 
309
         let popup = NSPopUpButton()
337
         let popup = NSPopUpButton()
310
         popup.bezelStyle = .rounded
338
         popup.bezelStyle = .rounded
@@ -326,7 +354,7 @@ private final class SettingsToggleRow: SettingsRowBase {
326
 
354
 
327
     init(symbolName: String, title: String, isOn: Bool, isLast: Bool = false, onChange: @escaping (Bool) -> Void) {
355
     init(symbolName: String, title: String, isOn: Bool, isLast: Bool = false, onChange: @escaping (Bool) -> Void) {
328
         toggleTarget = ToggleTarget(handler: onChange)
356
         toggleTarget = ToggleTarget(handler: onChange)
329
-        super.init(isLast: isLast)
357
+        super.init(isLast: isLast, isInteractive: true)
330
 
358
 
331
         let toggle = NSSwitch()
359
         let toggle = NSSwitch()
332
         toggle.state = isOn ? .on : .off
360
         toggle.state = isOn ? .on : .off