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

Dashboard: fix job card description wrapping and alignment

Use attributed paragraph style for multiline NSTextField, constrain
description width to the content column, and compute
preferredMaxLayoutWidth from card width without an incorrect button-
strip reserve so wrapped text lays out left-aligned after resize.

Co-authored-by: Cursor <cursoragent@cursor.com>
AhtashamShahzad1 2 месяцев назад
Родитель
Сommit
fcf191b30f
1 измененных файлов с 35 добавлено и 5 удалено
  1. 35 5
      App for Indeed/Views/DashboardView.swift

+ 35 - 5
App for Indeed/Views/DashboardView.swift

@@ -39,6 +39,20 @@ final class DashboardView: NSView, NSTextFieldDelegate {
         static let findJobsCTAHighlight = NSColor(srgbRed: 54 / 255, green: 110 / 255, blue: 198 / 255, alpha: 1)
         static let findJobsCTAHighlight = NSColor(srgbRed: 54 / 255, green: 110 / 255, blue: 198 / 255, alpha: 1)
     }
     }
 
 
+    /// Multiline `NSTextField` often ignores `alignment` for wrapped runs; explicit paragraph alignment matches the title.
+    private static func jobListingDescriptionAttributedString(_ plain: String) -> NSAttributedString {
+        let paragraph = NSMutableParagraphStyle()
+        paragraph.alignment = .left
+        paragraph.lineBreakMode = .byWordWrapping
+        paragraph.baseWritingDirection = .leftToRight
+        let font = NSFont.systemFont(ofSize: 13, weight: .regular)
+        return NSAttributedString(string: plain, attributes: [
+            .font: font,
+            .foregroundColor: Theme.secondaryText,
+            .paragraphStyle: paragraph
+        ])
+    }
+
     private let contentStack = NSStackView()
     private let contentStack = NSStackView()
     private let chromeContainer = NSView()
     private let chromeContainer = NSView()
     private let sidebar = NSStackView()
     private let sidebar = NSStackView()
@@ -270,16 +284,20 @@ final class DashboardView: NSView, NSTextFieldDelegate {
 
 
     private func updateDescriptionColumnWidths(in stack: NSStackView, containerWidth: CGFloat) {
     private func updateDescriptionColumnWidths(in stack: NSStackView, containerWidth: CGFloat) {
         guard containerWidth > 1 else { return }
         guard containerWidth > 1 else { return }
-        let buttonStripReserve: CGFloat = 200
-        let fallbackTextColumn = max(1, containerWidth - 32 - buttonStripReserve)
+        // Matches `contentColumn` insets on the card (16 leading + 16 trailing). The description spans the full row below the buttons, so never subtract a “button strip” here — a too-narrow `preferredMaxLayoutWidth` inside a wider field makes wrapped `NSTextField` text lay out like a trailing-aligned band after resizes.
+        let contentHorizontalInset: CGFloat = 32
         var didChange = false
         var didChange = false
         for card in stack.arrangedSubviews {
         for card in stack.arrangedSubviews {
             guard let desc = card.viewWithTag(502) as? NSTextField else { continue }
             guard let desc = card.viewWithTag(502) as? NSTextField else { continue }
+            let cardWidth = card.bounds.width > 1 ? card.bounds.width : containerWidth
+            let fallbackColumn = max(1, cardWidth - contentHorizontalInset)
             let columnWidth: CGFloat
             let columnWidth: CGFloat
-            if let column = desc.superview, column.bounds.width > 1 {
+            if desc.bounds.width > 1 {
+                columnWidth = desc.bounds.width
+            } else if let column = desc.superview, column.bounds.width > 1 {
                 columnWidth = column.bounds.width
                 columnWidth = column.bounds.width
             } else {
             } else {
-                columnWidth = fallbackTextColumn
+                columnWidth = fallbackColumn
             }
             }
             if abs(desc.preferredMaxLayoutWidth - columnWidth) > 0.5 {
             if abs(desc.preferredMaxLayoutWidth - columnWidth) > 0.5 {
                 desc.preferredMaxLayoutWidth = columnWidth
                 desc.preferredMaxLayoutWidth = columnWidth
@@ -369,6 +387,16 @@ final class DashboardView: NSView, NSTextFieldDelegate {
         descriptionField.font = .systemFont(ofSize: 13, weight: .regular)
         descriptionField.font = .systemFont(ofSize: 13, weight: .regular)
         descriptionField.textColor = Theme.secondaryText
         descriptionField.textColor = Theme.secondaryText
         descriptionField.maximumNumberOfLines = 0
         descriptionField.maximumNumberOfLines = 0
+        descriptionField.alignment = .left
+        descriptionField.lineBreakMode = .byWordWrapping
+        descriptionField.baseWritingDirection = .leftToRight
+        descriptionField.attributedStringValue = Self.jobListingDescriptionAttributedString(job.description)
+        if let cell = descriptionField.cell as? NSTextFieldCell {
+            cell.alignment = .left
+            cell.wraps = true
+        }
+        descriptionField.setContentHuggingPriority(.defaultLow, for: .horizontal)
+        descriptionField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
         descriptionField.tag = 502
         descriptionField.tag = 502
         descriptionField.translatesAutoresizingMaskIntoConstraints = false
         descriptionField.translatesAutoresizingMaskIntoConstraints = false
 
 
@@ -460,7 +488,9 @@ final class DashboardView: NSView, NSTextFieldDelegate {
             savedButton.widthAnchor.constraint(greaterThanOrEqualToConstant: 72),
             savedButton.widthAnchor.constraint(greaterThanOrEqualToConstant: 72),
             savedButton.heightAnchor.constraint(equalToConstant: 28),
             savedButton.heightAnchor.constraint(equalToConstant: 28),
             dismissButton.widthAnchor.constraint(equalToConstant: 28),
             dismissButton.widthAnchor.constraint(equalToConstant: 28),
-            dismissButton.heightAnchor.constraint(equalToConstant: 28)
+            dismissButton.heightAnchor.constraint(equalToConstant: 28),
+
+            descriptionField.widthAnchor.constraint(equalTo: contentColumn.widthAnchor)
         ])
         ])
 
 
         return card
         return card