소스 검색

Narrow the default window and scale card icons dynamically.

Centralize window dimensions in AppTheme so the app opens at 680px while preserving the existing layout with responsive icon sizing.

Co-authored-by: Cursor <cursoragent@cursor.com>
AhtashamShahzad1 1 개월 전
부모
커밋
0a655379e0
5개의 변경된 파일138개의 추가작업 그리고 70개의 파일을 삭제
  1. 2 2
      smart_printer/AppDelegate.swift
  2. 23 1
      smart_printer/AppTheme.swift
  3. 2 2
      smart_printer/Base.lproj/Main.storyboard
  4. 60 28
      smart_printer/UIComponents.swift
  5. 51 37
      smart_printer/ViewController.swift

+ 2 - 2
smart_printer/AppDelegate.swift

@@ -23,9 +23,9 @@ class AppDelegate: NSObject, NSApplicationDelegate {
         window.styleMask.insert(.fullSizeContentView)
         window.isMovableByWindowBackground = true
         window.backgroundColor = AppTheme.background
-        window.setContentSize(NSSize(width: 1120, height: 720))
+        window.setContentSize(NSSize(width: AppTheme.windowWidth, height: AppTheme.windowHeight))
         window.center()
-        window.minSize = NSSize(width: 900, height: 600)
+        window.minSize = NSSize(width: AppTheme.windowMinWidth, height: AppTheme.windowMinHeight)
     }
 
     func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {

+ 23 - 1
smart_printer/AppTheme.swift

@@ -1,7 +1,29 @@
 import Cocoa
 
 enum AppTheme {
-    static let sidebarWidth: CGFloat = 220
+    static let windowWidth: CGFloat = 680
+    static let windowHeight: CGFloat = 720
+    static let windowMinWidth: CGFloat = 600
+    static let windowMinHeight: CGFloat = 600
+
+    static let sidebarWidth: CGFloat = 200
+    static let contentPadding: CGFloat = 20
+    static let quickStartSpacing: CGFloat = 12
+    static let featureGridSpacing: CGFloat = 10
+
+    static let quickStartIconMax: CGFloat = 80
+    static let quickStartIconMin: CGFloat = 56
+    static let featureIconMax: CGFloat = 56
+    static let featureIconMin: CGFloat = 36
+
+    static func quickStartIconSize(forCardWidth width: CGFloat) -> CGFloat {
+        min(quickStartIconMax, max(quickStartIconMin, width * 0.58))
+    }
+
+    static func featureIconSize(forCardWidth width: CGFloat) -> CGFloat {
+        min(featureIconMax, max(featureIconMin, width * 0.38))
+    }
+
     static let cornerRadius: CGFloat = 14
     static let cardCornerRadius: CGFloat = 20
     static let featureCardCornerRadius: CGFloat = 16

+ 2 - 2
smart_printer/Base.lproj/Main.storyboard

@@ -686,7 +686,7 @@
                     <window key="window" title="Smart Printer" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="YES" animationBehavior="default" id="IQv-IB-iLA">
                         <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
                         <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
-                        <rect key="contentRect" x="196" y="240" width="1120" height="720"/>
+                        <rect key="contentRect" x="196" y="240" width="680" height="720"/>
                         <rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
                         <connections>
                             <outlet property="delegate" destination="B8D-0N-5wS" id="98r-iN-zZc"/>
@@ -705,7 +705,7 @@
             <objects>
                 <viewController id="XfG-lQ-9wD" customClass="ViewController" customModuleProvider="target" sceneMemberID="viewController">
                     <view key="view" id="m2S-Jp-Qdl">
-                        <rect key="frame" x="0.0" y="0.0" width="1120" height="720"/>
+                        <rect key="frame" x="0.0" y="0.0" width="680" height="720"/>
                         <autoresizingMask key="autoresizingMask"/>
                     </view>
                 </viewController>

+ 60 - 28
smart_printer/UIComponents.swift

@@ -184,7 +184,12 @@ struct QuickStartCardData {
 }
 
 final class QuickStartCardView: NSView {
+    private let iconView: QuickStartIconView
+    private var iconWidthConstraint: NSLayoutConstraint!
+    private var iconHeightConstraint: NSLayoutConstraint!
+
     init(data: QuickStartCardData) {
+        iconView = QuickStartIconView(kind: data.iconKind)
         super.init(frame: .zero)
         translatesAutoresizingMaskIntoConstraints = false
 
@@ -211,8 +216,6 @@ final class QuickStartCardView: NSView {
         let wavePattern = WavePatternView()
         wavePattern.translatesAutoresizingMaskIntoConstraints = false
 
-        let iconView = QuickStartIconView(kind: data.iconKind)
-
         addSubview(gradient)
         gradient.addSubview(wavePattern)
         gradient.addSubview(titleLabel)
@@ -225,32 +228,44 @@ final class QuickStartCardView: NSView {
             gradient.trailingAnchor.constraint(equalTo: trailingAnchor),
             gradient.topAnchor.constraint(equalTo: topAnchor),
             gradient.bottomAnchor.constraint(equalTo: bottomAnchor),
-            heightAnchor.constraint(equalToConstant: 160),
+            heightAnchor.constraint(equalToConstant: 148),
 
-            titleLabel.leadingAnchor.constraint(equalTo: gradient.leadingAnchor, constant: 24),
-            titleLabel.topAnchor.constraint(equalTo: gradient.topAnchor, constant: 28),
+            titleLabel.leadingAnchor.constraint(equalTo: gradient.leadingAnchor, constant: 18),
+            titleLabel.topAnchor.constraint(equalTo: gradient.topAnchor, constant: 24),
 
             subtitleLabel.leadingAnchor.constraint(equalTo: titleLabel.leadingAnchor),
-            subtitleLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 6),
-            subtitleLabel.trailingAnchor.constraint(lessThanOrEqualTo: iconView.leadingAnchor, constant: -12),
+            subtitleLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 4),
+            subtitleLabel.trailingAnchor.constraint(lessThanOrEqualTo: iconView.leadingAnchor, constant: -8),
 
             button.leadingAnchor.constraint(equalTo: titleLabel.leadingAnchor),
-            button.bottomAnchor.constraint(equalTo: gradient.bottomAnchor, constant: -24),
+            button.bottomAnchor.constraint(equalTo: gradient.bottomAnchor, constant: -20),
 
             wavePattern.trailingAnchor.constraint(equalTo: gradient.trailingAnchor),
             wavePattern.bottomAnchor.constraint(equalTo: gradient.bottomAnchor),
-            wavePattern.widthAnchor.constraint(equalToConstant: 120),
-            wavePattern.heightAnchor.constraint(equalToConstant: 80),
+            wavePattern.widthAnchor.constraint(equalToConstant: 100),
+            wavePattern.heightAnchor.constraint(equalToConstant: 70),
 
-            iconView.trailingAnchor.constraint(equalTo: gradient.trailingAnchor, constant: -12),
+            iconView.trailingAnchor.constraint(equalTo: gradient.trailingAnchor, constant: -8),
             iconView.centerYAnchor.constraint(equalTo: gradient.centerYAnchor),
-            iconView.widthAnchor.constraint(equalToConstant: 110),
-            iconView.heightAnchor.constraint(equalToConstant: 110),
         ])
+
+        iconWidthConstraint = iconView.widthAnchor.constraint(equalToConstant: AppTheme.quickStartIconMax)
+        iconHeightConstraint = iconView.heightAnchor.constraint(equalToConstant: AppTheme.quickStartIconMax)
+        iconWidthConstraint.isActive = true
+        iconHeightConstraint.isActive = true
     }
 
     @available(*, unavailable)
     required init?(coder: NSCoder) { nil }
+
+    override func layout() {
+        super.layout()
+        let size = AppTheme.quickStartIconSize(forCardWidth: bounds.width)
+        if iconWidthConstraint.constant != size {
+            iconWidthConstraint.constant = size
+            iconHeightConstraint.constant = size
+        }
+    }
 }
 
 // MARK: - Feature Card
@@ -262,16 +277,21 @@ struct FeatureCardData {
 }
 
 final class FeatureCardView: NSView {
+    private let iconView: FeatureIconView
+    private var iconWidthConstraint: NSLayoutConstraint!
+    private var iconHeightConstraint: NSLayoutConstraint!
+
     init(data: FeatureCardData) {
+        iconView = FeatureIconView(kind: data.iconKind)
         super.init(frame: .zero)
         translatesAutoresizingMaskIntoConstraints = false
+        setContentHuggingPriority(.defaultLow, for: .horizontal)
+        setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
         wantsLayer = true
         layer?.backgroundColor = AppTheme.cardBackground.cgColor
         layer?.cornerRadius = AppTheme.featureCardCornerRadius
         applyCardShadow()
 
-        let iconView = FeatureIconView(kind: data.iconKind)
-
         let titleLabel = NSTextField(labelWithString: data.title)
         titleLabel.font = AppTheme.semiboldFont(size: 15)
         titleLabel.textColor = AppTheme.textPrimary
@@ -286,7 +306,7 @@ final class FeatureCardView: NSView {
         arrowButton.isBordered = false
         arrowButton.wantsLayer = true
         arrowButton.layer?.backgroundColor = NSColor.white.cgColor
-        arrowButton.layer?.cornerRadius = 15
+        arrowButton.layer?.cornerRadius = 13
         arrowButton.layer?.borderWidth = 1
         arrowButton.layer?.borderColor = NSColor(calibratedWhite: 0.88, alpha: 1).cgColor
         arrowButton.translatesAutoresizingMaskIntoConstraints = false
@@ -302,31 +322,43 @@ final class FeatureCardView: NSView {
         addSubview(arrowButton)
 
         NSLayoutConstraint.activate([
-            heightAnchor.constraint(equalToConstant: 118),
+            heightAnchor.constraint(equalToConstant: 104),
 
-            iconView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 14),
+            iconView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10),
             iconView.centerYAnchor.constraint(equalTo: centerYAnchor),
-            iconView.widthAnchor.constraint(equalToConstant: 72),
-            iconView.heightAnchor.constraint(equalToConstant: 72),
 
-            titleLabel.leadingAnchor.constraint(equalTo: iconView.trailingAnchor, constant: 10),
-            titleLabel.topAnchor.constraint(equalTo: topAnchor, constant: 28),
-            titleLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -40),
+            titleLabel.leadingAnchor.constraint(equalTo: iconView.trailingAnchor, constant: 8),
+            titleLabel.topAnchor.constraint(equalTo: topAnchor, constant: 22),
+            titleLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -32),
 
             subtitleLabel.leadingAnchor.constraint(equalTo: titleLabel.leadingAnchor),
-            subtitleLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 4),
+            subtitleLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 3),
             subtitleLabel.trailingAnchor.constraint(equalTo: titleLabel.trailingAnchor),
 
-            arrowButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -14),
-            arrowButton.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -14),
-            arrowButton.widthAnchor.constraint(equalToConstant: 30),
-            arrowButton.heightAnchor.constraint(equalToConstant: 30),
+            arrowButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10),
+            arrowButton.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -10),
+            arrowButton.widthAnchor.constraint(equalToConstant: 26),
+            arrowButton.heightAnchor.constraint(equalToConstant: 26),
         ])
+
+        iconWidthConstraint = iconView.widthAnchor.constraint(equalToConstant: AppTheme.featureIconMax)
+        iconHeightConstraint = iconView.heightAnchor.constraint(equalToConstant: AppTheme.featureIconMax)
+        iconWidthConstraint.isActive = true
+        iconHeightConstraint.isActive = true
     }
 
     @available(*, unavailable)
     required init?(coder: NSCoder) { nil }
 
+    override func layout() {
+        super.layout()
+        let size = AppTheme.featureIconSize(forCardWidth: bounds.width)
+        if iconWidthConstraint.constant != size {
+            iconWidthConstraint.constant = size
+            iconHeightConstraint.constant = size
+        }
+    }
+
     override func resetCursorRects() {
         addCursorRect(bounds, cursor: .pointingHand)
     }

+ 51 - 37
smart_printer/ViewController.swift

@@ -8,7 +8,7 @@ import Cocoa
 class ViewController: NSViewController {
 
     override func loadView() {
-        let container = NSView(frame: NSRect(x: 0, y: 0, width: 1120, height: 720))
+        let container = NSView(frame: NSRect(x: 0, y: 0, width: AppTheme.windowWidth, height: AppTheme.windowHeight))
         container.autoresizingMask = [.width, .height]
         container.wantsLayer = true
         container.layer?.backgroundColor = AppTheme.background.cgColor
@@ -62,8 +62,8 @@ class ViewController: NSViewController {
 
             wavePattern.trailingAnchor.constraint(equalTo: mainContent.trailingAnchor),
             wavePattern.bottomAnchor.constraint(equalTo: mainContent.bottomAnchor),
-            wavePattern.widthAnchor.constraint(equalToConstant: 280),
-            wavePattern.heightAnchor.constraint(equalToConstant: 180),
+            wavePattern.widthAnchor.constraint(equalToConstant: 200),
+            wavePattern.heightAnchor.constraint(equalToConstant: 140),
         ])
     }
 
@@ -129,12 +129,12 @@ class ViewController: NSViewController {
             documentView.trailingAnchor.constraint(equalTo: contentGuide.trailingAnchor),
             documentView.widthAnchor.constraint(equalTo: contentGuide.widthAnchor),
 
-            quickStartSection.leadingAnchor.constraint(equalTo: documentView.leadingAnchor, constant: 32),
-            quickStartSection.trailingAnchor.constraint(equalTo: documentView.trailingAnchor, constant: -32),
+            quickStartSection.leadingAnchor.constraint(equalTo: documentView.leadingAnchor, constant: AppTheme.contentPadding),
+            quickStartSection.trailingAnchor.constraint(equalTo: documentView.trailingAnchor, constant: -AppTheme.contentPadding),
             quickStartSection.topAnchor.constraint(equalTo: documentView.topAnchor, constant: 8),
 
-            createPrintSection.leadingAnchor.constraint(equalTo: documentView.leadingAnchor, constant: 32),
-            createPrintSection.trailingAnchor.constraint(equalTo: documentView.trailingAnchor, constant: -32),
+            createPrintSection.leadingAnchor.constraint(equalTo: documentView.leadingAnchor, constant: AppTheme.contentPadding),
+            createPrintSection.trailingAnchor.constraint(equalTo: documentView.trailingAnchor, constant: -AppTheme.contentPadding),
             createPrintSection.topAnchor.constraint(equalTo: quickStartSection.bottomAnchor, constant: 36),
             createPrintSection.bottomAnchor.constraint(equalTo: documentView.bottomAnchor, constant: -40),
         ])
@@ -192,7 +192,7 @@ class ViewController: NSViewController {
 
         let cardsStack = NSStackView()
         cardsStack.orientation = .horizontal
-        cardsStack.spacing = 20
+        cardsStack.spacing = AppTheme.quickStartSpacing
         cardsStack.distribution = .fillEqually
         cardsStack.translatesAutoresizingMaskIntoConstraints = false
 
@@ -258,51 +258,65 @@ class ViewController: NSViewController {
             FeatureCardData(title: "OCR File", subtitle: "Scan and print text from images", iconKind: .ocrFile),
         ]
 
-        let row1 = makeFeatureRow(features: Array(features[0..<4]), columns: 4)
-        let row2 = makeFeatureRow(features: Array(features[4..<6]), columns: 4)
-
-        let gridStack = NSStackView(views: [row1, row2])
-        gridStack.orientation = .vertical
-        gridStack.spacing = 16
-        gridStack.distribution = .fill
-        gridStack.translatesAutoresizingMaskIntoConstraints = false
-        section.addSubview(gridStack)
+        let grid = makeFeatureGrid(features: features, columns: 4)
+        section.addSubview(grid)
 
         NSLayoutConstraint.activate([
             title.leadingAnchor.constraint(equalTo: section.leadingAnchor),
             title.topAnchor.constraint(equalTo: section.topAnchor),
 
-            gridStack.leadingAnchor.constraint(equalTo: section.leadingAnchor),
-            gridStack.trailingAnchor.constraint(equalTo: section.trailingAnchor),
-            gridStack.topAnchor.constraint(equalTo: title.bottomAnchor, constant: 16),
-            gridStack.bottomAnchor.constraint(equalTo: section.bottomAnchor),
+            grid.leadingAnchor.constraint(equalTo: section.leadingAnchor),
+            grid.trailingAnchor.constraint(equalTo: section.trailingAnchor),
+            grid.topAnchor.constraint(equalTo: title.bottomAnchor, constant: 16),
+            grid.bottomAnchor.constraint(equalTo: section.bottomAnchor),
         ])
 
         return section
     }
 
-    private func makeFeatureRow(features: [FeatureCardData], columns: Int) -> NSStackView {
-        let row = NSStackView()
-        row.orientation = .horizontal
-        row.spacing = 16
-        row.distribution = .fillEqually
-        row.alignment = .centerY
-        row.translatesAutoresizingMaskIntoConstraints = false
+    private func makeFeatureGrid(features: [FeatureCardData], columns: Int) -> NSView {
+        let grid = NSView()
+        grid.translatesAutoresizingMaskIntoConstraints = false
+
+        let spacing = AppTheme.featureGridSpacing
+        let cards = features.map { FeatureCardView(data: $0) }
+        let rowCount = (cards.count + columns - 1) / columns
 
-        for feature in features {
-            row.addArrangedSubview(FeatureCardView(data: feature))
+        for card in cards {
+            grid.addSubview(card)
         }
 
-        if features.count < columns {
-            let spacerCount = columns - features.count
-            for _ in 0..<spacerCount {
-                let spacer = NSView()
-                spacer.translatesAutoresizingMaskIntoConstraints = false
-                row.addArrangedSubview(spacer)
+        for (index, card) in cards.enumerated() {
+            let row = index / columns
+            let col = index % columns
+            let columnAnchor = cards[col]
+
+            if col == 0 {
+                card.leadingAnchor.constraint(equalTo: grid.leadingAnchor).isActive = true
+            } else {
+                let leftCard = cards[index - 1]
+                card.leadingAnchor.constraint(equalTo: leftCard.trailingAnchor, constant: spacing).isActive = true
+            }
+
+            card.widthAnchor.constraint(equalTo: columnAnchor.widthAnchor).isActive = true
+
+            if row == 0 {
+                card.topAnchor.constraint(equalTo: grid.topAnchor).isActive = true
+            } else {
+                let aboveCard = cards[index - columns]
+                card.topAnchor.constraint(equalTo: aboveCard.bottomAnchor, constant: spacing).isActive = true
+            }
+
+            if col == columns - 1 {
+                card.trailingAnchor.constraint(equalTo: grid.trailingAnchor).isActive = true
+            }
+
+            if row == rowCount - 1 {
+                card.bottomAnchor.constraint(equalTo: grid.bottomAnchor).isActive = true
             }
         }
 
-        return row
+        return grid
     }
 }