Bläddra i källkod

Fix Add Text dialog placeholder alignment with the text cursor.

Draw the placeholder inside the text view at the insertion point rect so it shares the same layout as the caret.

Co-authored-by: Cursor <cursoragent@cursor.com>
AhtashamShahzad1 1 månad sedan
förälder
incheckning
99c6a3ec51
1 ändrade filer med 44 tillägg och 14 borttagningar
  1. 44 14
      smart_printer/DrawPrintView.swift

+ 44 - 14
smart_printer/DrawPrintView.swift

@@ -1087,7 +1087,43 @@ final class CanvasToolsPanel: NSView, AppearanceRefreshable {
     }
 }
 
+private final class PlaceholderTextView: NSTextView {
+    var placeholderString = ""
+
+    var placeholderColor: NSColor = AppTheme.textSecondary {
+        didSet { needsDisplay = true }
+    }
+
+    override func draw(_ dirtyRect: NSRect) {
+        super.draw(dirtyRect)
+        guard string.isEmpty, !placeholderString.isEmpty else { return }
+
+        let attributes: [NSAttributedString.Key: Any] = [
+            .font: font ?? AppTheme.regularFont(size: 20),
+            .foregroundColor: placeholderColor,
+        ]
+        let rect = firstRect(forCharacterRange: NSRange(location: 0, length: 0), actualRange: nil)
+        (placeholderString as NSString).draw(with: rect, options: .usesLineFragmentOrigin, attributes: attributes)
+    }
+
+    override func becomeFirstResponder() -> Bool {
+        let becameFirstResponder = super.becomeFirstResponder()
+        if becameFirstResponder { needsDisplay = true }
+        return becameFirstResponder
+    }
+
+    override func resignFirstResponder() -> Bool {
+        let resignedFirstResponder = super.resignFirstResponder()
+        if resignedFirstResponder { needsDisplay = true }
+        return resignedFirstResponder
+    }
+}
+
 private final class AddTextPromptView: NSView, AppearanceRefreshable, NSTextViewDelegate {
+    private static let inputFontSize: CGFloat = 20
+    private static let inputTextInset = NSSize(width: 16, height: 14)
+    private static let inputLineFragmentPadding: CGFloat = 5
+
     var onAdd: ((String) -> Void)?
     var onCancel: (() -> Void)?
 
@@ -1095,8 +1131,7 @@ private final class AddTextPromptView: NSView, AppearanceRefreshable, NSTextView
     private let cardView = NSView()
     private let titleLabel = NSTextField(labelWithString: "Add Text")
     private let inputScrollView = NSScrollView()
-    private let inputTextView = NSTextView()
-    private let placeholderLabel = NSTextField(labelWithString: "Enter any text here...")
+    private let inputTextView = PlaceholderTextView()
     private let buttonStack = NSStackView()
     private let addButton = DrawPrintPrintButton(title: "Add", symbolName: "plus", compact: true, firesOnMouseDown: true)
     private let cancelButton = DrawPrintSecondaryButton(title: "Cancel", symbolName: "xmark", compact: true, firesOnMouseDown: true)
@@ -1118,14 +1153,14 @@ private final class AddTextPromptView: NSView, AppearanceRefreshable, NSTextView
         titleLabel.textColor = AppTheme.textPrimary
         inputTextView.backgroundColor = .clear
         inputTextView.textColor = AppTheme.textPrimary
-        placeholderLabel.textColor = AppTheme.textSecondary
+        inputTextView.placeholderColor = AppTheme.textSecondary
         addButton.refreshAppearance()
         cancelButton.refreshAppearance()
     }
 
     func prepareForPresentation() {
         inputTextView.string = ""
-        placeholderLabel.isHidden = false
+        inputTextView.needsDisplay = true
     }
 
     func focusInput() {
@@ -1155,17 +1190,16 @@ private final class AddTextPromptView: NSView, AppearanceRefreshable, NSTextView
         inputScrollView.borderType = .noBorder
 
         inputTextView.isRichText = false
-        inputTextView.font = AppTheme.regularFont(size: 20)
-        inputTextView.textContainerInset = NSSize(width: 16, height: 14)
+        inputTextView.font = AppTheme.regularFont(size: Self.inputFontSize)
+        inputTextView.textContainerInset = Self.inputTextInset
+        inputTextView.textContainer?.lineFragmentPadding = Self.inputLineFragmentPadding
+        inputTextView.placeholderString = "Enter any text here..."
         inputTextView.isAutomaticQuoteSubstitutionEnabled = true
         inputTextView.isAutomaticDashSubstitutionEnabled = true
         inputTextView.isAutomaticTextReplacementEnabled = true
         inputTextView.delegate = self
         inputScrollView.documentView = inputTextView
 
-        placeholderLabel.font = AppTheme.regularFont(size: 18)
-        placeholderLabel.translatesAutoresizingMaskIntoConstraints = false
-
         buttonStack.orientation = .horizontal
         buttonStack.spacing = 12
         buttonStack.distribution = .fillEqually
@@ -1182,7 +1216,6 @@ private final class AddTextPromptView: NSView, AppearanceRefreshable, NSTextView
         addSubview(cardView)
         cardView.addSubview(titleLabel)
         cardView.addSubview(inputScrollView)
-        cardView.addSubview(placeholderLabel)
         cardView.addSubview(buttonStack)
         buttonStack.addArrangedSubview(addButton)
         buttonStack.addArrangedSubview(cancelButton)
@@ -1210,9 +1243,6 @@ private final class AddTextPromptView: NSView, AppearanceRefreshable, NSTextView
             inputScrollView.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 14),
             inputScrollView.bottomAnchor.constraint(equalTo: buttonStack.topAnchor, constant: -16),
 
-            placeholderLabel.leadingAnchor.constraint(equalTo: inputScrollView.leadingAnchor, constant: 6),
-            placeholderLabel.topAnchor.constraint(equalTo: inputScrollView.topAnchor, constant: 10),
-
             buttonStack.leadingAnchor.constraint(equalTo: cardView.leadingAnchor, constant: 24),
             buttonStack.trailingAnchor.constraint(equalTo: cardView.trailingAnchor, constant: -24),
             buttonStack.bottomAnchor.constraint(equalTo: cardView.bottomAnchor, constant: -20),
@@ -1232,7 +1262,7 @@ private final class AddTextPromptView: NSView, AppearanceRefreshable, NSTextView
     }
 
     func textDidChange(_ notification: Notification) {
-        placeholderLabel.isHidden = !inputTextView.string.isEmpty
+        inputTextView.needsDisplay = true
     }
 }