Просмотр исходного кода

Make the paywall a full-window overlay with blurred background.

Hide window traffic lights while the paywall is shown so the premium screen matches the app size and feels like a dedicated full-screen state.

Co-authored-by: Cursor <cursoragent@cursor.com>
AhtashamShahzad1 14 часов назад
Родитель
Сommit
5e11cd0e2c
2 измененных файлов с 68 добавлено и 35 удалено
  1. 36 15
      smart_printer/PaywallView.swift
  2. 32 20
      smart_printer/ViewController.swift

+ 36 - 15
smart_printer/PaywallView.swift

@@ -351,8 +351,7 @@ final class PaywallView: NSView {
351
         translatesAutoresizingMaskIntoConstraints = false
351
         translatesAutoresizingMaskIntoConstraints = false
352
         wantsLayer = true
352
         wantsLayer = true
353
         layer?.backgroundColor = NSColor.white.cgColor
353
         layer?.backgroundColor = NSColor.white.cgColor
354
-        layer?.cornerRadius = 20
355
-        applyCardShadow()
354
+        layer?.cornerRadius = 0
356
         setup()
355
         setup()
357
     }
356
     }
358
 
357
 
@@ -367,9 +366,6 @@ final class PaywallView: NSView {
367
         addSubview(rightPanel)
366
         addSubview(rightPanel)
368
 
367
 
369
         NSLayoutConstraint.activate([
368
         NSLayoutConstraint.activate([
370
-            widthAnchor.constraint(equalToConstant: 780),
371
-            heightAnchor.constraint(equalToConstant: 500),
372
-
373
             leftPanel.leadingAnchor.constraint(equalTo: leadingAnchor),
369
             leftPanel.leadingAnchor.constraint(equalTo: leadingAnchor),
374
             leftPanel.topAnchor.constraint(equalTo: topAnchor),
370
             leftPanel.topAnchor.constraint(equalTo: topAnchor),
375
             leftPanel.bottomAnchor.constraint(equalTo: bottomAnchor),
371
             leftPanel.bottomAnchor.constraint(equalTo: bottomAnchor),
@@ -574,6 +570,7 @@ final class PaywallOverlayView: NSView {
574
     var onDismiss: (() -> Void)?
570
     var onDismiss: (() -> Void)?
575
 
571
 
576
     private let paywallView: PaywallView
572
     private let paywallView: PaywallView
573
+    private let blurView = NSVisualEffectView()
577
     private let backdrop = NSView()
574
     private let backdrop = NSView()
578
 
575
 
579
     init() {
576
     init() {
@@ -587,9 +584,14 @@ final class PaywallOverlayView: NSView {
587
     required init?(coder: NSCoder) { nil }
584
     required init?(coder: NSCoder) { nil }
588
 
585
 
589
     private func setup() {
586
     private func setup() {
587
+        blurView.translatesAutoresizingMaskIntoConstraints = false
588
+        blurView.material = .underWindowBackground
589
+        blurView.blendingMode = .withinWindow
590
+        blurView.state = .active
591
+
590
         backdrop.translatesAutoresizingMaskIntoConstraints = false
592
         backdrop.translatesAutoresizingMaskIntoConstraints = false
591
         backdrop.wantsLayer = true
593
         backdrop.wantsLayer = true
592
-        backdrop.layer?.backgroundColor = NSColor(calibratedWhite: 0.15, alpha: 0.45).cgColor
594
+        backdrop.layer?.backgroundColor = NSColor(calibratedWhite: 0.15, alpha: 0.22).cgColor
593
 
595
 
594
         let pattern = WavePatternView()
596
         let pattern = WavePatternView()
595
         pattern.translatesAutoresizingMaskIntoConstraints = false
597
         pattern.translatesAutoresizingMaskIntoConstraints = false
@@ -604,11 +606,17 @@ final class PaywallOverlayView: NSView {
604
             NSLog("Restore purchases tapped")
606
             NSLog("Restore purchases tapped")
605
         }
607
         }
606
 
608
 
609
+        addSubview(blurView)
607
         addSubview(backdrop)
610
         addSubview(backdrop)
608
         backdrop.addSubview(pattern)
611
         backdrop.addSubview(pattern)
609
         addSubview(paywallView)
612
         addSubview(paywallView)
610
 
613
 
611
         NSLayoutConstraint.activate([
614
         NSLayoutConstraint.activate([
615
+            blurView.leadingAnchor.constraint(equalTo: leadingAnchor),
616
+            blurView.trailingAnchor.constraint(equalTo: trailingAnchor),
617
+            blurView.topAnchor.constraint(equalTo: topAnchor),
618
+            blurView.bottomAnchor.constraint(equalTo: bottomAnchor),
619
+
612
             backdrop.leadingAnchor.constraint(equalTo: leadingAnchor),
620
             backdrop.leadingAnchor.constraint(equalTo: leadingAnchor),
613
             backdrop.trailingAnchor.constraint(equalTo: trailingAnchor),
621
             backdrop.trailingAnchor.constraint(equalTo: trailingAnchor),
614
             backdrop.topAnchor.constraint(equalTo: topAnchor),
622
             backdrop.topAnchor.constraint(equalTo: topAnchor),
@@ -619,20 +627,33 @@ final class PaywallOverlayView: NSView {
619
             pattern.topAnchor.constraint(equalTo: backdrop.topAnchor),
627
             pattern.topAnchor.constraint(equalTo: backdrop.topAnchor),
620
             pattern.bottomAnchor.constraint(equalTo: backdrop.bottomAnchor),
628
             pattern.bottomAnchor.constraint(equalTo: backdrop.bottomAnchor),
621
 
629
 
622
-            paywallView.centerXAnchor.constraint(equalTo: centerXAnchor),
623
-            paywallView.centerYAnchor.constraint(equalTo: centerYAnchor),
630
+            paywallView.leadingAnchor.constraint(equalTo: leadingAnchor),
631
+            paywallView.trailingAnchor.constraint(equalTo: trailingAnchor),
632
+            paywallView.topAnchor.constraint(equalTo: topAnchor),
633
+            paywallView.bottomAnchor.constraint(equalTo: bottomAnchor),
624
         ])
634
         ])
625
     }
635
     }
626
 
636
 
627
     func present(in parent: NSView) {
637
     func present(in parent: NSView) {
628
         guard superview == nil else { return }
638
         guard superview == nil else { return }
629
-        parent.addSubview(self)
630
-        NSLayoutConstraint.activate([
631
-            leadingAnchor.constraint(equalTo: parent.leadingAnchor),
632
-            trailingAnchor.constraint(equalTo: parent.trailingAnchor),
633
-            topAnchor.constraint(equalTo: parent.topAnchor),
634
-            bottomAnchor.constraint(equalTo: parent.bottomAnchor),
635
-        ])
639
+        if let window = parent.window,
640
+           let windowFrameView = window.contentView?.superview {
641
+            windowFrameView.addSubview(self)
642
+            NSLayoutConstraint.activate([
643
+                leadingAnchor.constraint(equalTo: windowFrameView.leadingAnchor),
644
+                trailingAnchor.constraint(equalTo: windowFrameView.trailingAnchor),
645
+                topAnchor.constraint(equalTo: windowFrameView.topAnchor),
646
+                bottomAnchor.constraint(equalTo: windowFrameView.bottomAnchor),
647
+            ])
648
+        } else {
649
+            parent.addSubview(self)
650
+            NSLayoutConstraint.activate([
651
+                leadingAnchor.constraint(equalTo: parent.leadingAnchor),
652
+                trailingAnchor.constraint(equalTo: parent.trailingAnchor),
653
+                topAnchor.constraint(equalTo: parent.topAnchor),
654
+                bottomAnchor.constraint(equalTo: parent.bottomAnchor),
655
+            ])
656
+        }
636
         alphaValue = 0
657
         alphaValue = 0
637
         NSAnimationContext.runAnimationGroup { context in
658
         NSAnimationContext.runAnimationGroup { context in
638
             context.duration = 0.2
659
             context.duration = 0.2

+ 32 - 20
smart_printer/ViewController.swift

@@ -8,6 +8,7 @@ import Cocoa
8
 class ViewController: NSViewController {
8
 class ViewController: NSViewController {
9
 
9
 
10
     private var sidebar: SidebarView!
10
     private var sidebar: SidebarView!
11
+    private var mainContentView: NSView!
11
     private var homeContentView: NSView!
12
     private var homeContentView: NSView!
12
     private var scanContentView: NSView!
13
     private var scanContentView: NSView!
13
     private var scanAndHomeContentView: NSView!
14
     private var scanAndHomeContentView: NSView!
@@ -30,10 +31,10 @@ class ViewController: NSViewController {
30
     private func setupLayout() {
31
     private func setupLayout() {
31
         sidebar = SidebarView()
32
         sidebar = SidebarView()
32
 
33
 
33
-        let mainContent = NSView()
34
-        mainContent.translatesAutoresizingMaskIntoConstraints = false
35
-        mainContent.wantsLayer = true
36
-        mainContent.layer?.backgroundColor = AppTheme.background.cgColor
34
+        mainContentView = NSView()
35
+        mainContentView.translatesAutoresizingMaskIntoConstraints = false
36
+        mainContentView.wantsLayer = true
37
+        mainContentView.layer?.backgroundColor = AppTheme.background.cgColor
37
 
38
 
38
         let header = makeHeader()
39
         let header = makeHeader()
39
 
40
 
@@ -52,10 +53,10 @@ class ViewController: NSViewController {
52
         wavePattern.translatesAutoresizingMaskIntoConstraints = false
53
         wavePattern.translatesAutoresizingMaskIntoConstraints = false
53
 
54
 
54
         view.addSubview(sidebar)
55
         view.addSubview(sidebar)
55
-        view.addSubview(mainContent)
56
-        mainContent.addSubview(header)
57
-        mainContent.addSubview(contentContainer)
58
-        mainContent.addSubview(wavePattern)
56
+        view.addSubview(mainContentView)
57
+        mainContentView.addSubview(header)
58
+        mainContentView.addSubview(contentContainer)
59
+        mainContentView.addSubview(wavePattern)
59
 
60
 
60
         sidebar.onDestinationSelected = { [weak self] destination in
61
         sidebar.onDestinationSelected = { [weak self] destination in
61
             self?.showDestination(destination)
62
             self?.showDestination(destination)
@@ -66,23 +67,23 @@ class ViewController: NSViewController {
66
             sidebar.topAnchor.constraint(equalTo: view.topAnchor),
67
             sidebar.topAnchor.constraint(equalTo: view.topAnchor),
67
             sidebar.bottomAnchor.constraint(equalTo: view.bottomAnchor),
68
             sidebar.bottomAnchor.constraint(equalTo: view.bottomAnchor),
68
 
69
 
69
-            mainContent.leadingAnchor.constraint(equalTo: sidebar.trailingAnchor),
70
-            mainContent.trailingAnchor.constraint(equalTo: view.trailingAnchor),
71
-            mainContent.topAnchor.constraint(equalTo: view.topAnchor),
72
-            mainContent.bottomAnchor.constraint(equalTo: view.bottomAnchor),
70
+            mainContentView.leadingAnchor.constraint(equalTo: sidebar.trailingAnchor),
71
+            mainContentView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
72
+            mainContentView.topAnchor.constraint(equalTo: view.topAnchor),
73
+            mainContentView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
73
 
74
 
74
-            header.leadingAnchor.constraint(equalTo: mainContent.leadingAnchor),
75
-            header.trailingAnchor.constraint(equalTo: mainContent.trailingAnchor),
76
-            header.topAnchor.constraint(equalTo: mainContent.topAnchor, constant: 16),
75
+            header.leadingAnchor.constraint(equalTo: mainContentView.leadingAnchor),
76
+            header.trailingAnchor.constraint(equalTo: mainContentView.trailingAnchor),
77
+            header.topAnchor.constraint(equalTo: mainContentView.topAnchor, constant: 16),
77
             header.heightAnchor.constraint(equalToConstant: 44),
78
             header.heightAnchor.constraint(equalToConstant: 44),
78
 
79
 
79
-            contentContainer.leadingAnchor.constraint(equalTo: mainContent.leadingAnchor),
80
-            contentContainer.trailingAnchor.constraint(equalTo: mainContent.trailingAnchor),
80
+            contentContainer.leadingAnchor.constraint(equalTo: mainContentView.leadingAnchor),
81
+            contentContainer.trailingAnchor.constraint(equalTo: mainContentView.trailingAnchor),
81
             contentContainer.topAnchor.constraint(equalTo: header.bottomAnchor, constant: 8),
82
             contentContainer.topAnchor.constraint(equalTo: header.bottomAnchor, constant: 8),
82
-            contentContainer.bottomAnchor.constraint(equalTo: mainContent.bottomAnchor),
83
+            contentContainer.bottomAnchor.constraint(equalTo: mainContentView.bottomAnchor),
83
 
84
 
84
-            wavePattern.trailingAnchor.constraint(equalTo: mainContent.trailingAnchor),
85
-            wavePattern.bottomAnchor.constraint(equalTo: mainContent.bottomAnchor),
85
+            wavePattern.trailingAnchor.constraint(equalTo: mainContentView.trailingAnchor),
86
+            wavePattern.bottomAnchor.constraint(equalTo: mainContentView.bottomAnchor),
86
             wavePattern.widthAnchor.constraint(equalToConstant: 200),
87
             wavePattern.widthAnchor.constraint(equalToConstant: 200),
87
             wavePattern.heightAnchor.constraint(equalToConstant: 140),
88
             wavePattern.heightAnchor.constraint(equalToConstant: 140),
88
         ])
89
         ])
@@ -116,8 +117,10 @@ class ViewController: NSViewController {
116
 
117
 
117
     private func presentPaywall() {
118
     private func presentPaywall() {
118
         guard paywallOverlay == nil else { return }
119
         guard paywallOverlay == nil else { return }
120
+        setTrafficLightsHidden(true)
119
         let overlay = PaywallOverlayView()
121
         let overlay = PaywallOverlayView()
120
         overlay.onDismiss = { [weak self] in
122
         overlay.onDismiss = { [weak self] in
123
+            self?.setTrafficLightsHidden(false)
121
             self?.paywallOverlay = nil
124
             self?.paywallOverlay = nil
122
             self?.sidebar.select(.home)
125
             self?.sidebar.select(.home)
123
             self?.showDestination(.home)
126
             self?.showDestination(.home)
@@ -129,6 +132,15 @@ class ViewController: NSViewController {
129
     private func dismissPaywall() {
132
     private func dismissPaywall() {
130
         paywallOverlay?.removeFromSuperview()
133
         paywallOverlay?.removeFromSuperview()
131
         paywallOverlay = nil
134
         paywallOverlay = nil
135
+        setTrafficLightsHidden(false)
136
+    }
137
+
138
+    private func setTrafficLightsHidden(_ isHidden: Bool) {
139
+        guard let window = view.window else { return }
140
+        let buttons: [NSWindow.ButtonType] = [.closeButton, .miniaturizeButton, .zoomButton]
141
+        for buttonType in buttons {
142
+            window.standardWindowButton(buttonType)?.isHidden = isHidden
143
+        }
132
     }
144
     }
133
 
145
 
134
     private func makeHomeContentView() -> NSView {
146
     private func makeHomeContentView() -> NSView {