Kaynağa Gözat

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 16 saat önce
ebeveyn
işleme
0a655379e0

+ 2 - 2
smart_printer/AppDelegate.swift

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

+ 23 - 1
smart_printer/AppTheme.swift

@@ -1,7 +1,29 @@
1 1
 import Cocoa
2 2
 
3 3
 enum AppTheme {
4
-    static let sidebarWidth: CGFloat = 220
4
+    static let windowWidth: CGFloat = 680
5
+    static let windowHeight: CGFloat = 720
6
+    static let windowMinWidth: CGFloat = 600
7
+    static let windowMinHeight: CGFloat = 600
8
+
9
+    static let sidebarWidth: CGFloat = 200
10
+    static let contentPadding: CGFloat = 20
11
+    static let quickStartSpacing: CGFloat = 12
12
+    static let featureGridSpacing: CGFloat = 10
13
+
14
+    static let quickStartIconMax: CGFloat = 80
15
+    static let quickStartIconMin: CGFloat = 56
16
+    static let featureIconMax: CGFloat = 56
17
+    static let featureIconMin: CGFloat = 36
18
+
19
+    static func quickStartIconSize(forCardWidth width: CGFloat) -> CGFloat {
20
+        min(quickStartIconMax, max(quickStartIconMin, width * 0.58))
21
+    }
22
+
23
+    static func featureIconSize(forCardWidth width: CGFloat) -> CGFloat {
24
+        min(featureIconMax, max(featureIconMin, width * 0.38))
25
+    }
26
+
5 27
     static let cornerRadius: CGFloat = 14
6 28
     static let cardCornerRadius: CGFloat = 20
7 29
     static let featureCardCornerRadius: CGFloat = 16

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

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

+ 60 - 28
smart_printer/UIComponents.swift

@@ -184,7 +184,12 @@ struct QuickStartCardData {
184 184
 }
185 185
 
186 186
 final class QuickStartCardView: NSView {
187
+    private let iconView: QuickStartIconView
188
+    private var iconWidthConstraint: NSLayoutConstraint!
189
+    private var iconHeightConstraint: NSLayoutConstraint!
190
+
187 191
     init(data: QuickStartCardData) {
192
+        iconView = QuickStartIconView(kind: data.iconKind)
188 193
         super.init(frame: .zero)
189 194
         translatesAutoresizingMaskIntoConstraints = false
190 195
 
@@ -211,8 +216,6 @@ final class QuickStartCardView: NSView {
211 216
         let wavePattern = WavePatternView()
212 217
         wavePattern.translatesAutoresizingMaskIntoConstraints = false
213 218
 
214
-        let iconView = QuickStartIconView(kind: data.iconKind)
215
-
216 219
         addSubview(gradient)
217 220
         gradient.addSubview(wavePattern)
218 221
         gradient.addSubview(titleLabel)
@@ -225,32 +228,44 @@ final class QuickStartCardView: NSView {
225 228
             gradient.trailingAnchor.constraint(equalTo: trailingAnchor),
226 229
             gradient.topAnchor.constraint(equalTo: topAnchor),
227 230
             gradient.bottomAnchor.constraint(equalTo: bottomAnchor),
228
-            heightAnchor.constraint(equalToConstant: 160),
231
+            heightAnchor.constraint(equalToConstant: 148),
229 232
 
230
-            titleLabel.leadingAnchor.constraint(equalTo: gradient.leadingAnchor, constant: 24),
231
-            titleLabel.topAnchor.constraint(equalTo: gradient.topAnchor, constant: 28),
233
+            titleLabel.leadingAnchor.constraint(equalTo: gradient.leadingAnchor, constant: 18),
234
+            titleLabel.topAnchor.constraint(equalTo: gradient.topAnchor, constant: 24),
232 235
 
233 236
             subtitleLabel.leadingAnchor.constraint(equalTo: titleLabel.leadingAnchor),
234
-            subtitleLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 6),
235
-            subtitleLabel.trailingAnchor.constraint(lessThanOrEqualTo: iconView.leadingAnchor, constant: -12),
237
+            subtitleLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 4),
238
+            subtitleLabel.trailingAnchor.constraint(lessThanOrEqualTo: iconView.leadingAnchor, constant: -8),
236 239
 
237 240
             button.leadingAnchor.constraint(equalTo: titleLabel.leadingAnchor),
238
-            button.bottomAnchor.constraint(equalTo: gradient.bottomAnchor, constant: -24),
241
+            button.bottomAnchor.constraint(equalTo: gradient.bottomAnchor, constant: -20),
239 242
 
240 243
             wavePattern.trailingAnchor.constraint(equalTo: gradient.trailingAnchor),
241 244
             wavePattern.bottomAnchor.constraint(equalTo: gradient.bottomAnchor),
242
-            wavePattern.widthAnchor.constraint(equalToConstant: 120),
243
-            wavePattern.heightAnchor.constraint(equalToConstant: 80),
245
+            wavePattern.widthAnchor.constraint(equalToConstant: 100),
246
+            wavePattern.heightAnchor.constraint(equalToConstant: 70),
244 247
 
245
-            iconView.trailingAnchor.constraint(equalTo: gradient.trailingAnchor, constant: -12),
248
+            iconView.trailingAnchor.constraint(equalTo: gradient.trailingAnchor, constant: -8),
246 249
             iconView.centerYAnchor.constraint(equalTo: gradient.centerYAnchor),
247
-            iconView.widthAnchor.constraint(equalToConstant: 110),
248
-            iconView.heightAnchor.constraint(equalToConstant: 110),
249 250
         ])
251
+
252
+        iconWidthConstraint = iconView.widthAnchor.constraint(equalToConstant: AppTheme.quickStartIconMax)
253
+        iconHeightConstraint = iconView.heightAnchor.constraint(equalToConstant: AppTheme.quickStartIconMax)
254
+        iconWidthConstraint.isActive = true
255
+        iconHeightConstraint.isActive = true
250 256
     }
251 257
 
252 258
     @available(*, unavailable)
253 259
     required init?(coder: NSCoder) { nil }
260
+
261
+    override func layout() {
262
+        super.layout()
263
+        let size = AppTheme.quickStartIconSize(forCardWidth: bounds.width)
264
+        if iconWidthConstraint.constant != size {
265
+            iconWidthConstraint.constant = size
266
+            iconHeightConstraint.constant = size
267
+        }
268
+    }
254 269
 }
255 270
 
256 271
 // MARK: - Feature Card
@@ -262,16 +277,21 @@ struct FeatureCardData {
262 277
 }
263 278
 
264 279
 final class FeatureCardView: NSView {
280
+    private let iconView: FeatureIconView
281
+    private var iconWidthConstraint: NSLayoutConstraint!
282
+    private var iconHeightConstraint: NSLayoutConstraint!
283
+
265 284
     init(data: FeatureCardData) {
285
+        iconView = FeatureIconView(kind: data.iconKind)
266 286
         super.init(frame: .zero)
267 287
         translatesAutoresizingMaskIntoConstraints = false
288
+        setContentHuggingPriority(.defaultLow, for: .horizontal)
289
+        setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
268 290
         wantsLayer = true
269 291
         layer?.backgroundColor = AppTheme.cardBackground.cgColor
270 292
         layer?.cornerRadius = AppTheme.featureCardCornerRadius
271 293
         applyCardShadow()
272 294
 
273
-        let iconView = FeatureIconView(kind: data.iconKind)
274
-
275 295
         let titleLabel = NSTextField(labelWithString: data.title)
276 296
         titleLabel.font = AppTheme.semiboldFont(size: 15)
277 297
         titleLabel.textColor = AppTheme.textPrimary
@@ -286,7 +306,7 @@ final class FeatureCardView: NSView {
286 306
         arrowButton.isBordered = false
287 307
         arrowButton.wantsLayer = true
288 308
         arrowButton.layer?.backgroundColor = NSColor.white.cgColor
289
-        arrowButton.layer?.cornerRadius = 15
309
+        arrowButton.layer?.cornerRadius = 13
290 310
         arrowButton.layer?.borderWidth = 1
291 311
         arrowButton.layer?.borderColor = NSColor(calibratedWhite: 0.88, alpha: 1).cgColor
292 312
         arrowButton.translatesAutoresizingMaskIntoConstraints = false
@@ -302,31 +322,43 @@ final class FeatureCardView: NSView {
302 322
         addSubview(arrowButton)
303 323
 
304 324
         NSLayoutConstraint.activate([
305
-            heightAnchor.constraint(equalToConstant: 118),
325
+            heightAnchor.constraint(equalToConstant: 104),
306 326
 
307
-            iconView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 14),
327
+            iconView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10),
308 328
             iconView.centerYAnchor.constraint(equalTo: centerYAnchor),
309
-            iconView.widthAnchor.constraint(equalToConstant: 72),
310
-            iconView.heightAnchor.constraint(equalToConstant: 72),
311 329
 
312
-            titleLabel.leadingAnchor.constraint(equalTo: iconView.trailingAnchor, constant: 10),
313
-            titleLabel.topAnchor.constraint(equalTo: topAnchor, constant: 28),
314
-            titleLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -40),
330
+            titleLabel.leadingAnchor.constraint(equalTo: iconView.trailingAnchor, constant: 8),
331
+            titleLabel.topAnchor.constraint(equalTo: topAnchor, constant: 22),
332
+            titleLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -32),
315 333
 
316 334
             subtitleLabel.leadingAnchor.constraint(equalTo: titleLabel.leadingAnchor),
317
-            subtitleLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 4),
335
+            subtitleLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 3),
318 336
             subtitleLabel.trailingAnchor.constraint(equalTo: titleLabel.trailingAnchor),
319 337
 
320
-            arrowButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -14),
321
-            arrowButton.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -14),
322
-            arrowButton.widthAnchor.constraint(equalToConstant: 30),
323
-            arrowButton.heightAnchor.constraint(equalToConstant: 30),
338
+            arrowButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10),
339
+            arrowButton.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -10),
340
+            arrowButton.widthAnchor.constraint(equalToConstant: 26),
341
+            arrowButton.heightAnchor.constraint(equalToConstant: 26),
324 342
         ])
343
+
344
+        iconWidthConstraint = iconView.widthAnchor.constraint(equalToConstant: AppTheme.featureIconMax)
345
+        iconHeightConstraint = iconView.heightAnchor.constraint(equalToConstant: AppTheme.featureIconMax)
346
+        iconWidthConstraint.isActive = true
347
+        iconHeightConstraint.isActive = true
325 348
     }
326 349
 
327 350
     @available(*, unavailable)
328 351
     required init?(coder: NSCoder) { nil }
329 352
 
353
+    override func layout() {
354
+        super.layout()
355
+        let size = AppTheme.featureIconSize(forCardWidth: bounds.width)
356
+        if iconWidthConstraint.constant != size {
357
+            iconWidthConstraint.constant = size
358
+            iconHeightConstraint.constant = size
359
+        }
360
+    }
361
+
330 362
     override func resetCursorRects() {
331 363
         addCursorRect(bounds, cursor: .pointingHand)
332 364
     }

+ 51 - 37
smart_printer/ViewController.swift

@@ -8,7 +8,7 @@ import Cocoa
8 8
 class ViewController: NSViewController {
9 9
 
10 10
     override func loadView() {
11
-        let container = NSView(frame: NSRect(x: 0, y: 0, width: 1120, height: 720))
11
+        let container = NSView(frame: NSRect(x: 0, y: 0, width: AppTheme.windowWidth, height: AppTheme.windowHeight))
12 12
         container.autoresizingMask = [.width, .height]
13 13
         container.wantsLayer = true
14 14
         container.layer?.backgroundColor = AppTheme.background.cgColor
@@ -62,8 +62,8 @@ class ViewController: NSViewController {
62 62
 
63 63
             wavePattern.trailingAnchor.constraint(equalTo: mainContent.trailingAnchor),
64 64
             wavePattern.bottomAnchor.constraint(equalTo: mainContent.bottomAnchor),
65
-            wavePattern.widthAnchor.constraint(equalToConstant: 280),
66
-            wavePattern.heightAnchor.constraint(equalToConstant: 180),
65
+            wavePattern.widthAnchor.constraint(equalToConstant: 200),
66
+            wavePattern.heightAnchor.constraint(equalToConstant: 140),
67 67
         ])
68 68
     }
69 69
 
@@ -129,12 +129,12 @@ class ViewController: NSViewController {
129 129
             documentView.trailingAnchor.constraint(equalTo: contentGuide.trailingAnchor),
130 130
             documentView.widthAnchor.constraint(equalTo: contentGuide.widthAnchor),
131 131
 
132
-            quickStartSection.leadingAnchor.constraint(equalTo: documentView.leadingAnchor, constant: 32),
133
-            quickStartSection.trailingAnchor.constraint(equalTo: documentView.trailingAnchor, constant: -32),
132
+            quickStartSection.leadingAnchor.constraint(equalTo: documentView.leadingAnchor, constant: AppTheme.contentPadding),
133
+            quickStartSection.trailingAnchor.constraint(equalTo: documentView.trailingAnchor, constant: -AppTheme.contentPadding),
134 134
             quickStartSection.topAnchor.constraint(equalTo: documentView.topAnchor, constant: 8),
135 135
 
136
-            createPrintSection.leadingAnchor.constraint(equalTo: documentView.leadingAnchor, constant: 32),
137
-            createPrintSection.trailingAnchor.constraint(equalTo: documentView.trailingAnchor, constant: -32),
136
+            createPrintSection.leadingAnchor.constraint(equalTo: documentView.leadingAnchor, constant: AppTheme.contentPadding),
137
+            createPrintSection.trailingAnchor.constraint(equalTo: documentView.trailingAnchor, constant: -AppTheme.contentPadding),
138 138
             createPrintSection.topAnchor.constraint(equalTo: quickStartSection.bottomAnchor, constant: 36),
139 139
             createPrintSection.bottomAnchor.constraint(equalTo: documentView.bottomAnchor, constant: -40),
140 140
         ])
@@ -192,7 +192,7 @@ class ViewController: NSViewController {
192 192
 
193 193
         let cardsStack = NSStackView()
194 194
         cardsStack.orientation = .horizontal
195
-        cardsStack.spacing = 20
195
+        cardsStack.spacing = AppTheme.quickStartSpacing
196 196
         cardsStack.distribution = .fillEqually
197 197
         cardsStack.translatesAutoresizingMaskIntoConstraints = false
198 198
 
@@ -258,51 +258,65 @@ class ViewController: NSViewController {
258 258
             FeatureCardData(title: "OCR File", subtitle: "Scan and print text from images", iconKind: .ocrFile),
259 259
         ]
260 260
 
261
-        let row1 = makeFeatureRow(features: Array(features[0..<4]), columns: 4)
262
-        let row2 = makeFeatureRow(features: Array(features[4..<6]), columns: 4)
263
-
264
-        let gridStack = NSStackView(views: [row1, row2])
265
-        gridStack.orientation = .vertical
266
-        gridStack.spacing = 16
267
-        gridStack.distribution = .fill
268
-        gridStack.translatesAutoresizingMaskIntoConstraints = false
269
-        section.addSubview(gridStack)
261
+        let grid = makeFeatureGrid(features: features, columns: 4)
262
+        section.addSubview(grid)
270 263
 
271 264
         NSLayoutConstraint.activate([
272 265
             title.leadingAnchor.constraint(equalTo: section.leadingAnchor),
273 266
             title.topAnchor.constraint(equalTo: section.topAnchor),
274 267
 
275
-            gridStack.leadingAnchor.constraint(equalTo: section.leadingAnchor),
276
-            gridStack.trailingAnchor.constraint(equalTo: section.trailingAnchor),
277
-            gridStack.topAnchor.constraint(equalTo: title.bottomAnchor, constant: 16),
278
-            gridStack.bottomAnchor.constraint(equalTo: section.bottomAnchor),
268
+            grid.leadingAnchor.constraint(equalTo: section.leadingAnchor),
269
+            grid.trailingAnchor.constraint(equalTo: section.trailingAnchor),
270
+            grid.topAnchor.constraint(equalTo: title.bottomAnchor, constant: 16),
271
+            grid.bottomAnchor.constraint(equalTo: section.bottomAnchor),
279 272
         ])
280 273
 
281 274
         return section
282 275
     }
283 276
 
284
-    private func makeFeatureRow(features: [FeatureCardData], columns: Int) -> NSStackView {
285
-        let row = NSStackView()
286
-        row.orientation = .horizontal
287
-        row.spacing = 16
288
-        row.distribution = .fillEqually
289
-        row.alignment = .centerY
290
-        row.translatesAutoresizingMaskIntoConstraints = false
277
+    private func makeFeatureGrid(features: [FeatureCardData], columns: Int) -> NSView {
278
+        let grid = NSView()
279
+        grid.translatesAutoresizingMaskIntoConstraints = false
280
+
281
+        let spacing = AppTheme.featureGridSpacing
282
+        let cards = features.map { FeatureCardView(data: $0) }
283
+        let rowCount = (cards.count + columns - 1) / columns
291 284
 
292
-        for feature in features {
293
-            row.addArrangedSubview(FeatureCardView(data: feature))
285
+        for card in cards {
286
+            grid.addSubview(card)
294 287
         }
295 288
 
296
-        if features.count < columns {
297
-            let spacerCount = columns - features.count
298
-            for _ in 0..<spacerCount {
299
-                let spacer = NSView()
300
-                spacer.translatesAutoresizingMaskIntoConstraints = false
301
-                row.addArrangedSubview(spacer)
289
+        for (index, card) in cards.enumerated() {
290
+            let row = index / columns
291
+            let col = index % columns
292
+            let columnAnchor = cards[col]
293
+
294
+            if col == 0 {
295
+                card.leadingAnchor.constraint(equalTo: grid.leadingAnchor).isActive = true
296
+            } else {
297
+                let leftCard = cards[index - 1]
298
+                card.leadingAnchor.constraint(equalTo: leftCard.trailingAnchor, constant: spacing).isActive = true
299
+            }
300
+
301
+            card.widthAnchor.constraint(equalTo: columnAnchor.widthAnchor).isActive = true
302
+
303
+            if row == 0 {
304
+                card.topAnchor.constraint(equalTo: grid.topAnchor).isActive = true
305
+            } else {
306
+                let aboveCard = cards[index - columns]
307
+                card.topAnchor.constraint(equalTo: aboveCard.bottomAnchor, constant: spacing).isActive = true
308
+            }
309
+
310
+            if col == columns - 1 {
311
+                card.trailingAnchor.constraint(equalTo: grid.trailingAnchor).isActive = true
312
+            }
313
+
314
+            if row == rowCount - 1 {
315
+                card.bottomAnchor.constraint(equalTo: grid.bottomAnchor).isActive = true
302 316
             }
303 317
         }
304 318
 
305
-        return row
319
+        return grid
306 320
     }
307 321
 }
308 322