|
|
@@ -0,0 +1,72 @@
|
|
|
+import AppKit
|
|
|
+import SwiftUI
|
|
|
+
|
|
|
+private final class ThinCaretTextView: NSTextView {
|
|
|
+ private let caretWidth: CGFloat = 1
|
|
|
+
|
|
|
+ override func drawInsertionPoint(in rect: NSRect, color: NSColor, turnedOn flag: Bool) {
|
|
|
+ var caretRect = rect
|
|
|
+ caretRect.size.width = caretWidth
|
|
|
+ caretRect.origin.x += (rect.width - caretWidth) / 2
|
|
|
+ super.drawInsertionPoint(in: caretRect, color: color, turnedOn: flag)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+struct ThinCaretTextEditor: NSViewRepresentable {
|
|
|
+ @Binding var text: String
|
|
|
+
|
|
|
+ func makeCoordinator() -> Coordinator {
|
|
|
+ Coordinator(parent: self)
|
|
|
+ }
|
|
|
+
|
|
|
+ func makeNSView(context: Context) -> NSScrollView {
|
|
|
+ let scrollView = NSScrollView()
|
|
|
+ scrollView.hasVerticalScroller = true
|
|
|
+ scrollView.hasHorizontalScroller = false
|
|
|
+ scrollView.autohidesScrollers = true
|
|
|
+ scrollView.borderType = .noBorder
|
|
|
+ scrollView.drawsBackground = false
|
|
|
+
|
|
|
+ let textView = ThinCaretTextView()
|
|
|
+ textView.isEditable = true
|
|
|
+ textView.isSelectable = true
|
|
|
+ textView.isRichText = false
|
|
|
+ textView.allowsUndo = true
|
|
|
+ textView.drawsBackground = false
|
|
|
+ textView.backgroundColor = .clear
|
|
|
+ textView.textContainer?.lineFragmentPadding = 0
|
|
|
+ textView.textContainerInset = NSSize(width: 0, height: 0)
|
|
|
+ textView.isAutomaticQuoteSubstitutionEnabled = false
|
|
|
+ textView.isAutomaticDashSubstitutionEnabled = false
|
|
|
+ textView.isAutomaticTextReplacementEnabled = false
|
|
|
+ textView.font = NSFont.systemFont(ofSize: 14)
|
|
|
+ textView.textColor = NSColor(AppTheme.textPrimary)
|
|
|
+ textView.delegate = context.coordinator
|
|
|
+ textView.string = text
|
|
|
+
|
|
|
+ scrollView.documentView = textView
|
|
|
+ context.coordinator.textView = textView
|
|
|
+ return scrollView
|
|
|
+ }
|
|
|
+
|
|
|
+ func updateNSView(_ scrollView: NSScrollView, context: Context) {
|
|
|
+ guard let textView = scrollView.documentView as? ThinCaretTextView else { return }
|
|
|
+ if textView.string != text {
|
|
|
+ textView.string = text
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ final class Coordinator: NSObject, NSTextViewDelegate {
|
|
|
+ var parent: ThinCaretTextEditor
|
|
|
+ weak var textView: NSTextView?
|
|
|
+
|
|
|
+ init(parent: ThinCaretTextEditor) {
|
|
|
+ self.parent = parent
|
|
|
+ }
|
|
|
+
|
|
|
+ func textDidChange(_ notification: Notification) {
|
|
|
+ guard let textView = notification.object as? NSTextView else { return }
|
|
|
+ parent.text = textView.string
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|