浏览代码

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 月之前
父节点
当前提交
99c6a3ec51
共有 1 个文件被更改,包括 44 次插入14 次删除
  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
     }
 }