Pārlūkot izejas kodu

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 12 stundas atpakaļ
vecāks
revīzija
5e11cd0e2c
2 mainītis faili ar 68 papildinājumiem un 35 dzēšanām
  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 351
         translatesAutoresizingMaskIntoConstraints = false
352 352
         wantsLayer = true
353 353
         layer?.backgroundColor = NSColor.white.cgColor
354
-        layer?.cornerRadius = 20
355
-        applyCardShadow()
354
+        layer?.cornerRadius = 0
356 355
         setup()
357 356
     }
358 357
 
@@ -367,9 +366,6 @@ final class PaywallView: NSView {
367 366
         addSubview(rightPanel)
368 367
 
369 368
         NSLayoutConstraint.activate([
370
-            widthAnchor.constraint(equalToConstant: 780),
371
-            heightAnchor.constraint(equalToConstant: 500),
372
-
373 369
             leftPanel.leadingAnchor.constraint(equalTo: leadingAnchor),
374 370
             leftPanel.topAnchor.constraint(equalTo: topAnchor),
375 371
             leftPanel.bottomAnchor.constraint(equalTo: bottomAnchor),
@@ -574,6 +570,7 @@ final class PaywallOverlayView: NSView {
574 570
     var onDismiss: (() -> Void)?
575 571
 
576 572
     private let paywallView: PaywallView
573
+    private let blurView = NSVisualEffectView()
577 574
     private let backdrop = NSView()
578 575
 
579 576
     init() {
@@ -587,9 +584,14 @@ final class PaywallOverlayView: NSView {
587 584
     required init?(coder: NSCoder) { nil }
588 585
 
589 586
     private func setup() {
587
+        blurView.translatesAutoresizingMaskIntoConstraints = false
588
+        blurView.material = .underWindowBackground
589
+        blurView.blendingMode = .withinWindow
590
+        blurView.state = .active
591
+
590 592
         backdrop.translatesAutoresizingMaskIntoConstraints = false
591 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 596
         let pattern = WavePatternView()
595 597
         pattern.translatesAutoresizingMaskIntoConstraints = false
@@ -604,11 +606,17 @@ final class PaywallOverlayView: NSView {
604 606
             NSLog("Restore purchases tapped")
605 607
         }
606 608
 
609
+        addSubview(blurView)
607 610
         addSubview(backdrop)
608 611
         backdrop.addSubview(pattern)
609 612
         addSubview(paywallView)
610 613
 
611 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 620
             backdrop.leadingAnchor.constraint(equalTo: leadingAnchor),
613 621
             backdrop.trailingAnchor.constraint(equalTo: trailingAnchor),
614 622
             backdrop.topAnchor.constraint(equalTo: topAnchor),
@@ -619,20 +627,33 @@ final class PaywallOverlayView: NSView {
619 627
             pattern.topAnchor.constraint(equalTo: backdrop.topAnchor),
620 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 637
     func present(in parent: NSView) {
628 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 657
         alphaValue = 0
637 658
         NSAnimationContext.runAnimationGroup { context in
638 659
             context.duration = 0.2

+ 32 - 20
smart_printer/ViewController.swift

@@ -8,6 +8,7 @@ import Cocoa
8 8
 class ViewController: NSViewController {
9 9
 
10 10
     private var sidebar: SidebarView!
11
+    private var mainContentView: NSView!
11 12
     private var homeContentView: NSView!
12 13
     private var scanContentView: NSView!
13 14
     private var scanAndHomeContentView: NSView!
@@ -30,10 +31,10 @@ class ViewController: NSViewController {
30 31
     private func setupLayout() {
31 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 39
         let header = makeHeader()
39 40
 
@@ -52,10 +53,10 @@ class ViewController: NSViewController {
52 53
         wavePattern.translatesAutoresizingMaskIntoConstraints = false
53 54
 
54 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 61
         sidebar.onDestinationSelected = { [weak self] destination in
61 62
             self?.showDestination(destination)
@@ -66,23 +67,23 @@ class ViewController: NSViewController {
66 67
             sidebar.topAnchor.constraint(equalTo: view.topAnchor),
67 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 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 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 87
             wavePattern.widthAnchor.constraint(equalToConstant: 200),
87 88
             wavePattern.heightAnchor.constraint(equalToConstant: 140),
88 89
         ])
@@ -116,8 +117,10 @@ class ViewController: NSViewController {
116 117
 
117 118
     private func presentPaywall() {
118 119
         guard paywallOverlay == nil else { return }
120
+        setTrafficLightsHidden(true)
119 121
         let overlay = PaywallOverlayView()
120 122
         overlay.onDismiss = { [weak self] in
123
+            self?.setTrafficLightsHidden(false)
121 124
             self?.paywallOverlay = nil
122 125
             self?.sidebar.select(.home)
123 126
             self?.showDestination(.home)
@@ -129,6 +132,15 @@ class ViewController: NSViewController {
129 132
     private func dismissPaywall() {
130 133
         paywallOverlay?.removeFromSuperview()
131 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 146
     private func makeHomeContentView() -> NSView {