Преглед изворни кода

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 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