|
|
@@ -93,6 +93,64 @@ private final class PremiumPlansViewController: NSViewController {
|
|
93
|
93
|
}
|
|
94
|
94
|
}
|
|
95
|
95
|
|
|
|
96
|
+ /// Footer text actions: accent color + pointing hand on hover (matches pricing-card tracking behavior).
|
|
|
97
|
+ private final class FooterLinkButton: NSButton {
|
|
|
98
|
+ private var trackingAreaRef: NSTrackingArea?
|
|
|
99
|
+ private var didPushCursor = false
|
|
|
100
|
+
|
|
|
101
|
+ override func updateTrackingAreas() {
|
|
|
102
|
+ super.updateTrackingAreas()
|
|
|
103
|
+ if let trackingAreaRef {
|
|
|
104
|
+ removeTrackingArea(trackingAreaRef)
|
|
|
105
|
+ }
|
|
|
106
|
+ let options: NSTrackingArea.Options = [.activeInActiveApp, .mouseEnteredAndExited, .inVisibleRect]
|
|
|
107
|
+ let area = NSTrackingArea(rect: .zero, options: options, owner: self, userInfo: nil)
|
|
|
108
|
+ addTrackingArea(area)
|
|
|
109
|
+ trackingAreaRef = area
|
|
|
110
|
+ }
|
|
|
111
|
+
|
|
|
112
|
+ override func mouseEntered(with event: NSEvent) {
|
|
|
113
|
+ super.mouseEntered(with: event)
|
|
|
114
|
+ setHoverVisuals(hovered: true)
|
|
|
115
|
+ if !didPushCursor {
|
|
|
116
|
+ NSCursor.pointingHand.push()
|
|
|
117
|
+ didPushCursor = true
|
|
|
118
|
+ }
|
|
|
119
|
+ }
|
|
|
120
|
+
|
|
|
121
|
+ override func mouseExited(with event: NSEvent) {
|
|
|
122
|
+ super.mouseExited(with: event)
|
|
|
123
|
+ setHoverVisuals(hovered: false)
|
|
|
124
|
+ if didPushCursor {
|
|
|
125
|
+ NSCursor.pop()
|
|
|
126
|
+ didPushCursor = false
|
|
|
127
|
+ }
|
|
|
128
|
+ }
|
|
|
129
|
+
|
|
|
130
|
+ override func viewWillMove(toWindow newWindow: NSWindow?) {
|
|
|
131
|
+ super.viewWillMove(toWindow: newWindow)
|
|
|
132
|
+ if newWindow == nil, didPushCursor {
|
|
|
133
|
+ NSCursor.pop()
|
|
|
134
|
+ didPushCursor = false
|
|
|
135
|
+ }
|
|
|
136
|
+ if newWindow == nil {
|
|
|
137
|
+ setHoverVisuals(hovered: false, animated: false)
|
|
|
138
|
+ }
|
|
|
139
|
+ }
|
|
|
140
|
+
|
|
|
141
|
+ private func setHoverVisuals(hovered: Bool, animated: Bool = true) {
|
|
|
142
|
+ let color = hovered ? Theme.accent : Theme.secondaryText
|
|
|
143
|
+ if animated {
|
|
|
144
|
+ NSAnimationContext.runAnimationGroup { context in
|
|
|
145
|
+ context.duration = 0.15
|
|
|
146
|
+ self.animator().contentTintColor = color
|
|
|
147
|
+ }
|
|
|
148
|
+ } else {
|
|
|
149
|
+ contentTintColor = color
|
|
|
150
|
+ }
|
|
|
151
|
+ }
|
|
|
152
|
+ }
|
|
|
153
|
+
|
|
96
|
154
|
private struct Plan {
|
|
97
|
155
|
let id: String
|
|
98
|
156
|
let title: String
|
|
|
@@ -352,7 +410,6 @@ private final class PremiumPlansViewController: NSViewController {
|
|
352
|
410
|
let topRightTag = pillLabel(text: plan.billedPill, tint: Theme.bottomStrip, textColor: Theme.iconTint)
|
|
353
|
411
|
topRightTag.isHidden = plan.billedPill.isEmpty
|
|
354
|
412
|
topRightTag.font = .systemFont(ofSize: 10, weight: .bold)
|
|
355
|
|
- topRightTag.heightAnchor.constraint(equalToConstant: 20).isActive = true
|
|
356
|
413
|
|
|
357
|
414
|
let priceLabel = NSTextField(labelWithString: plan.price)
|
|
358
|
415
|
priceLabel.font = .systemFont(ofSize: 18, weight: .semibold)
|
|
|
@@ -549,7 +606,7 @@ private final class PremiumPlansViewController: NSViewController {
|
|
549
|
606
|
let container = NSView()
|
|
550
|
607
|
container.translatesAutoresizingMaskIntoConstraints = false
|
|
551
|
608
|
|
|
552
|
|
- let button = NSButton(title: title, target: self, action: action)
|
|
|
609
|
+ let button = FooterLinkButton(title: title, target: self, action: action)
|
|
553
|
610
|
button.isBordered = false
|
|
554
|
611
|
button.bezelStyle = .rounded
|
|
555
|
612
|
button.font = .systemFont(ofSize: 12, weight: .medium)
|