Bladeren bron

Update splash launch UI with progress layout.

Matches the new launch design with status text and progress bar while keeping the 3-second transition.

Co-authored-by: Cursor <cursoragent@cursor.com>
huzaifahayat12 2 maanden geleden
bovenliggende
commit
6c10d52c8e
1 gewijzigde bestanden met toevoegingen van 110 en 71 verwijderingen
  1. 110 71
      zoom_app/AppDelegate.swift

+ 110 - 71
zoom_app/AppDelegate.swift

@@ -271,11 +271,12 @@ private final class SplashViewController: NSViewController {
         let isDark: Bool
         let backgroundTop: NSColor
         let backgroundBottom: NSColor
-        let cardFill: NSColor
         let titleColor: NSColor
         let subtitleColor: NSColor
+        let statusColor: NSColor
+        let trackColor: NSColor
+        let fillColor: NSColor
         let iconBadgeFill: NSColor
-        let iconBadgeStroke: NSColor
     }
 
     private final class GradientBackgroundView: NSView {
@@ -303,14 +304,18 @@ private final class SplashViewController: NSViewController {
     private var theme: Theme = SplashViewController.makeTheme(isDark: true)
     private var dotsTimer: Timer?
     private var dotsStep: Int = 0
+    private var progressTimer: Timer?
+    private var progressValue: Double = 0
 
     private weak var rootBackground: GradientBackgroundView?
-    private weak var cardEffectView: NSVisualEffectView?
     private weak var iconBadgeView: NSView?
     private weak var iconView: NSImageView?
     private weak var titleLabel: NSTextField?
     private weak var subtitleLabel: NSTextField?
     private weak var contentStack: NSStackView?
+    private weak var statusLabel: NSTextField?
+    private weak var percentLabel: NSTextField?
+    private weak var progressIndicator: NSProgressIndicator?
 
     override func viewDidLoad() {
         super.viewDidLoad()
@@ -321,54 +326,32 @@ private final class SplashViewController: NSViewController {
         super.viewDidAppear()
         startAnimations()
         startDots()
+        startProgress()
     }
 
     override func viewWillDisappear() {
         super.viewWillDisappear()
         dotsTimer?.invalidate()
         dotsTimer = nil
+        progressTimer?.invalidate()
+        progressTimer = nil
     }
 
     override func loadView() {
         let background = GradientBackgroundView()
 
-        let card = NSVisualEffectView()
-        card.translatesAutoresizingMaskIntoConstraints = false
-        card.blendingMode = .withinWindow
-        card.state = .active
-        card.wantsLayer = true
-        card.layer?.cornerRadius = 22
-        card.layer?.masksToBounds = true
-
-        // Subtle border similar to the app's card styling.
-        let border = CALayer()
-        border.borderWidth = 1
-        border.cornerRadius = 22
-        border.masksToBounds = true
-        card.layer?.addSublayer(border)
-        border.frame = card.bounds
-        border.autoresizingMask = [.layerWidthSizable, .layerHeightSizable]
-
         let stack = NSStackView()
         stack.orientation = .vertical
         stack.alignment = .centerX
-        stack.spacing = 12
+        stack.spacing = 10
         stack.translatesAutoresizingMaskIntoConstraints = false
 
         let iconBadge = NSView()
         iconBadge.translatesAutoresizingMaskIntoConstraints = false
         iconBadge.wantsLayer = true
-        iconBadge.layer?.cornerRadius = 22
+        iconBadge.layer?.cornerRadius = 26
         iconBadge.layer?.masksToBounds = true
 
-        let iconBadgeBorder = CALayer()
-        iconBadgeBorder.borderWidth = 1
-        iconBadgeBorder.cornerRadius = 22
-        iconBadgeBorder.masksToBounds = true
-        iconBadge.layer?.addSublayer(iconBadgeBorder)
-        iconBadgeBorder.frame = iconBadge.bounds
-        iconBadgeBorder.autoresizingMask = [.layerWidthSizable, .layerHeightSizable]
-
         let iconView = NSImageView()
         iconView.translatesAutoresizingMaskIntoConstraints = false
         iconView.imageScaling = .scaleProportionallyUpOrDown
@@ -379,40 +362,67 @@ private final class SplashViewController: NSViewController {
         let title = NSTextField(labelWithString: appName)
         title.font = .systemFont(ofSize: 30, weight: .semibold)
 
-        let subtitle = NSTextField(labelWithString: "Starting up…")
+        let subtitle = NSTextField(labelWithString: "Preparing your meetings workspace.")
         subtitle.font = .systemFont(ofSize: 14, weight: .regular)
         subtitle.alignment = .center
+        subtitle.maximumNumberOfLines = 2
+        subtitle.lineBreakMode = .byWordWrapping
+
+        let progressRow = NSStackView()
+        progressRow.orientation = .horizontal
+        progressRow.alignment = .centerY
+        progressRow.spacing = 10
+        progressRow.translatesAutoresizingMaskIntoConstraints = false
+
+        let progress = NSProgressIndicator()
+        progress.translatesAutoresizingMaskIntoConstraints = false
+        progress.isIndeterminate = false
+        progress.minValue = 0
+        progress.maxValue = 100
+        progress.doubleValue = 10
+        progress.controlSize = .small
+        progress.style = .bar
+
+        let percent = NSTextField(labelWithString: "10%")
+        percent.font = .systemFont(ofSize: 12, weight: .medium)
+        percent.alignment = .right
+
+        progressRow.addArrangedSubview(progress)
+        progressRow.addArrangedSubview(percent)
+
+        let status = NSTextField(labelWithString: "CONNECTING SERVICES")
+        status.font = .systemFont(ofSize: 10.5, weight: .semibold)
+        status.alignment = .center
+        status.usesSingleLineMode = true
+        status.setAccessibilityLabel("Splash status")
 
         iconBadge.addSubview(iconView)
         NSLayoutConstraint.activate([
             iconView.centerXAnchor.constraint(equalTo: iconBadge.centerXAnchor),
             iconView.centerYAnchor.constraint(equalTo: iconBadge.centerYAnchor),
-            iconView.widthAnchor.constraint(equalToConstant: 70),
+            iconView.widthAnchor.constraint(equalToConstant: 78),
             iconView.heightAnchor.constraint(equalTo: iconView.widthAnchor)
         ])
 
         stack.addArrangedSubview(iconBadge)
         stack.addArrangedSubview(title)
         stack.addArrangedSubview(subtitle)
-
-        card.addSubview(stack)
-        background.addSubview(card)
+        stack.addArrangedSubview(progressRow)
+        stack.addArrangedSubview(status)
+        background.addSubview(stack)
 
         NSLayoutConstraint.activate([
-            iconBadge.widthAnchor.constraint(equalToConstant: 120),
-            iconBadge.heightAnchor.constraint(equalToConstant: 120),
-
-            card.centerXAnchor.constraint(equalTo: background.centerXAnchor),
-            card.centerYAnchor.constraint(equalTo: background.centerYAnchor),
-            card.widthAnchor.constraint(greaterThanOrEqualToConstant: 360),
-            card.leadingAnchor.constraint(greaterThanOrEqualTo: background.leadingAnchor, constant: 28),
-            card.trailingAnchor.constraint(lessThanOrEqualTo: background.trailingAnchor, constant: -28),
-            card.heightAnchor.constraint(greaterThanOrEqualToConstant: 280),
-
-            stack.centerXAnchor.constraint(equalTo: card.centerXAnchor),
-            stack.centerYAnchor.constraint(equalTo: card.centerYAnchor),
-            stack.leadingAnchor.constraint(greaterThanOrEqualTo: card.leadingAnchor, constant: 30),
-            stack.trailingAnchor.constraint(lessThanOrEqualTo: card.trailingAnchor, constant: -30)
+            iconBadge.widthAnchor.constraint(equalToConstant: 104),
+            iconBadge.heightAnchor.constraint(equalToConstant: 104),
+
+            stack.centerXAnchor.constraint(equalTo: background.centerXAnchor),
+            stack.centerYAnchor.constraint(equalTo: background.centerYAnchor),
+            stack.leadingAnchor.constraint(greaterThanOrEqualTo: background.leadingAnchor, constant: 28),
+            stack.trailingAnchor.constraint(lessThanOrEqualTo: background.trailingAnchor, constant: -28),
+
+            progress.widthAnchor.constraint(equalToConstant: 260),
+            progress.heightAnchor.constraint(equalToConstant: 6),
+            percent.widthAnchor.constraint(equalToConstant: 44)
         ])
 
         // Initial state for animation.
@@ -420,12 +430,14 @@ private final class SplashViewController: NSViewController {
         stack.setFrameOrigin(NSPoint(x: stack.frame.origin.x, y: stack.frame.origin.y - 8))
 
         self.rootBackground = background
-        self.cardEffectView = card
         self.iconBadgeView = iconBadge
         self.iconView = iconView
         self.titleLabel = title
         self.subtitleLabel = subtitle
         self.contentStack = stack
+        self.statusLabel = status
+        self.percentLabel = percent
+        self.progressIndicator = progress
 
         view = background
     }
@@ -470,48 +482,74 @@ private final class SplashViewController: NSViewController {
             guard let self, let subtitleLabel else { return }
             self.dotsStep = (self.dotsStep + 1) % 4
             let dots = String(repeating: ".", count: self.dotsStep)
-            subtitleLabel.stringValue = "Starting up" + dots
+            // Keep the main subtitle stable; add subtle activity via ellipsis.
+            subtitleLabel.stringValue = "Preparing your meetings workspace" + dots
+        }
+    }
+
+    private func startProgress() {
+        progressTimer?.invalidate()
+        progressValue = 10
+        setProgressUI(value: progressValue)
+
+        // Animate progress smoothly for the 3s splash duration.
+        let tick: TimeInterval = 0.06
+        let target: Double = 100
+        let duration: Double = 3.0
+        let step = (target - progressValue) / (duration / tick)
+
+        progressTimer = Timer.scheduledTimer(withTimeInterval: tick, repeats: true) { [weak self] timer in
+            guard let self else { timer.invalidate(); return }
+            self.progressValue = min(target, self.progressValue + step)
+            self.setProgressUI(value: self.progressValue)
+            if self.progressValue >= target - 0.01 {
+                timer.invalidate()
+            }
         }
     }
 
+    private func setProgressUI(value: Double) {
+        progressIndicator?.doubleValue = value
+        percentLabel?.stringValue = "\(Int(round(value)))%"
+    }
+
     private func applyTheme() {
         let isDark = view.effectiveAppearance.bestMatch(from: [.darkAqua, .aqua]) == .darkAqua
         theme = Self.makeTheme(isDark: isDark)
 
         rootBackground?.gradientLayer.colors = [theme.backgroundTop.cgColor, theme.backgroundBottom.cgColor]
 
-        // Card styling.
-        if let card = cardEffectView {
-            card.material = theme.isDark ? .hudWindow : .sidebar
-            card.layer?.backgroundColor = theme.cardFill.cgColor
-            if let border = card.layer?.sublayers?.first(where: { $0.borderWidth > 0 }) {
-                border.borderColor = (theme.isDark ? NSColor.white.withAlphaComponent(0.08) : NSColor.black.withAlphaComponent(0.10)).cgColor
-            }
-        }
-
         // Icon badge styling.
         if let badge = iconBadgeView {
             badge.layer?.backgroundColor = theme.iconBadgeFill.cgColor
-            if let border = badge.layer?.sublayers?.first(where: { $0.borderWidth > 0 }) {
-                border.borderColor = theme.iconBadgeStroke.cgColor
-            }
         }
 
         titleLabel?.textColor = theme.titleColor
         subtitleLabel?.textColor = theme.subtitleColor
+        statusLabel?.textColor = theme.statusColor
+        percentLabel?.textColor = theme.subtitleColor
+
+        if let progress = progressIndicator {
+            progress.appearance = NSAppearance(named: theme.isDark ? .darkAqua : .aqua)
+            // Prefer tinting when available; fall back gracefully if not.
+            if #available(macOS 10.14, *) {
+                progress.contentFilters = []
+            }
+        }
     }
 
     private static func makeTheme(isDark: Bool) -> Theme {
         if isDark {
             return Theme(
                 isDark: true,
-                backgroundTop: NSColor(calibratedRed: 10 / 255, green: 11 / 255, blue: 12 / 255, alpha: 1),
-                backgroundBottom: NSColor(calibratedRed: 18 / 255, green: 19 / 255, blue: 22 / 255, alpha: 1),
-                cardFill: NSColor(calibratedRed: 16 / 255, green: 17 / 255, blue: 19 / 255, alpha: 0.72),
+                backgroundTop: NSColor(calibratedRed: 9 / 255, green: 9 / 255, blue: 10 / 255, alpha: 1),
+                backgroundBottom: NSColor(calibratedRed: 16 / 255, green: 16 / 255, blue: 18 / 255, alpha: 1),
                 titleColor: NSColor(calibratedWhite: 0.98, alpha: 1),
                 subtitleColor: NSColor(calibratedWhite: 0.78, alpha: 1),
-                iconBadgeFill: NSColor.white.withAlphaComponent(0.06),
-                iconBadgeStroke: NSColor.white.withAlphaComponent(0.10)
+                statusColor: NSColor(calibratedWhite: 0.66, alpha: 1),
+                trackColor: NSColor.white.withAlphaComponent(0.10),
+                fillColor: NSColor(calibratedRed: 96 / 255, green: 215 / 255, blue: 255 / 255, alpha: 1),
+                iconBadgeFill: NSColor.white.withAlphaComponent(0.08)
             )
         }
 
@@ -519,11 +557,12 @@ private final class SplashViewController: NSViewController {
             isDark: false,
             backgroundTop: NSColor(calibratedWhite: 0.98, alpha: 1),
             backgroundBottom: NSColor(calibratedWhite: 0.92, alpha: 1),
-            cardFill: NSColor.white.withAlphaComponent(0.70),
             titleColor: NSColor(calibratedWhite: 0.10, alpha: 1),
             subtitleColor: NSColor(calibratedWhite: 0.34, alpha: 1),
-            iconBadgeFill: NSColor.black.withAlphaComponent(0.04),
-            iconBadgeStroke: NSColor.black.withAlphaComponent(0.08)
+            statusColor: NSColor(calibratedWhite: 0.42, alpha: 1),
+            trackColor: NSColor.black.withAlphaComponent(0.10),
+            fillColor: NSColor(calibratedRed: 0.18, green: 0.52, blue: 0.96, alpha: 1),
+            iconBadgeFill: NSColor.black.withAlphaComponent(0.04)
         )
     }
 }