|
|
@@ -125,6 +125,34 @@ class ViewController: NSViewController {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ private final class HoverTileView: NSView {
|
|
|
+ var onHoverChanged: ((Bool) -> Void)?
|
|
|
+ private var tracking: NSTrackingArea?
|
|
|
+
|
|
|
+ override func updateTrackingAreas() {
|
|
|
+ super.updateTrackingAreas()
|
|
|
+ if let tracking { removeTrackingArea(tracking) }
|
|
|
+ let area = NSTrackingArea(
|
|
|
+ rect: bounds,
|
|
|
+ options: [.activeInActiveApp, .mouseEnteredAndExited, .inVisibleRect],
|
|
|
+ owner: self,
|
|
|
+ userInfo: nil
|
|
|
+ )
|
|
|
+ addTrackingArea(area)
|
|
|
+ tracking = area
|
|
|
+ }
|
|
|
+
|
|
|
+ override func mouseEntered(with event: NSEvent) {
|
|
|
+ super.mouseEntered(with: event)
|
|
|
+ onHoverChanged?(true)
|
|
|
+ }
|
|
|
+
|
|
|
+ override func mouseExited(with event: NSEvent) {
|
|
|
+ super.mouseExited(with: event)
|
|
|
+ onHoverChanged?(false)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
private var palette = Palette(isDarkMode: true)
|
|
|
private var darkModeEnabled: Bool {
|
|
|
get {
|
|
|
@@ -3752,7 +3780,7 @@ class ViewController: NSViewController {
|
|
|
}
|
|
|
|
|
|
private func makeActionTile(title: String, symbol: String, color: NSColor, action: Selector? = nil) -> NSView {
|
|
|
- let root = NSView()
|
|
|
+ let root = HoverTileView()
|
|
|
root.translatesAutoresizingMaskIntoConstraints = false
|
|
|
root.widthAnchor.constraint(equalToConstant: 88).isActive = true
|
|
|
// Give caption text a little more vertical room so descenders (e.g. "g") are never clipped.
|
|
|
@@ -3779,8 +3807,9 @@ class ViewController: NSViewController {
|
|
|
$0.translatesAutoresizingMaskIntoConstraints = false
|
|
|
root.addSubview($0)
|
|
|
}
|
|
|
+ let iconTopConstraint = iconButton.topAnchor.constraint(equalTo: root.topAnchor)
|
|
|
NSLayoutConstraint.activate([
|
|
|
- iconButton.topAnchor.constraint(equalTo: root.topAnchor),
|
|
|
+ iconTopConstraint,
|
|
|
iconButton.centerXAnchor.constraint(equalTo: root.centerXAnchor),
|
|
|
iconButton.widthAnchor.constraint(equalToConstant: 50),
|
|
|
iconButton.heightAnchor.constraint(equalToConstant: 50),
|
|
|
@@ -3788,6 +3817,18 @@ class ViewController: NSViewController {
|
|
|
label.centerXAnchor.constraint(equalTo: root.centerXAnchor),
|
|
|
label.bottomAnchor.constraint(equalTo: root.bottomAnchor)
|
|
|
])
|
|
|
+ root.onHoverChanged = { [weak iconButton, weak label] hovering in
|
|
|
+ NSAnimationContext.runAnimationGroup { context in
|
|
|
+ context.duration = hovering ? 0.16 : 0.14
|
|
|
+ context.timingFunction = CAMediaTimingFunction(name: hovering ? .easeOut : .easeInEaseOut)
|
|
|
+ iconTopConstraint.animator().constant = hovering ? -3 : 0
|
|
|
+ label?.animator().alphaValue = hovering ? 1.0 : 0.96
|
|
|
+ }
|
|
|
+ guard let iconButton else { return }
|
|
|
+ iconButton.layer?.shadowOpacity = hovering ? 0.28 : 0.2
|
|
|
+ iconButton.layer?.shadowRadius = hovering ? 10 : 7
|
|
|
+ iconButton.layer?.shadowOffset = hovering ? NSSize(width: 0, height: -3) : NSSize(width: 0, height: -1)
|
|
|
+ }
|
|
|
return root
|
|
|
}
|
|
|
|