diff --git a/Packages/vChewing_SwiftExtension/Sources/SwiftExtension/SwiftUIExtension.swift b/Packages/vChewing_SwiftExtension/Sources/SwiftExtension/SwiftUIExtension.swift index fa3ab1fc..f1b04f55 100644 --- a/Packages/vChewing_SwiftExtension/Sources/SwiftExtension/SwiftUIExtension.swift +++ b/Packages/vChewing_SwiftExtension/Sources/SwiftExtension/SwiftUIExtension.swift @@ -80,3 +80,83 @@ public struct VisualEffectView: NSViewRepresentable { visualEffectView.blendingMode = blendingMode } } + +// MARK: - TextEditor for macOS 10.15 Catalina + +// Ref: https://stackoverflow.com/a/63761738/4162914 + +@available(macOS 10.15, *) +/// A much faster alternative than Apple official TextEditor. +public struct TextEditorEX: NSViewRepresentable { + @Binding var text: String + + public init(text: Binding) { + _text = text + } + + public func makeNSView(context: Context) -> NSScrollView { + context.coordinator.createTextViewStack() + } + + public func updateNSView(_ nsView: NSScrollView, context _: Context) { + if let textArea = nsView.documentView as? NSTextView, textArea.string != self.text { + textArea.string = text + } + } + + public func makeCoordinator() -> Coordinator { + Coordinator(text: $text) + } + + public class Coordinator: NSObject, NSTextViewDelegate { + public var text: Binding + + public init(text: Binding) { + self.text = text + } + + public func textView(_ textView: NSTextView, shouldChangeTextIn range: NSRange, replacementString text: String?) + -> Bool + { + defer { + self.text.wrappedValue = (textView.string as NSString).replacingCharacters(in: range, with: text!) + } + return true + } + + fileprivate lazy var textStorage = NSTextStorage() + fileprivate lazy var layoutManager = NSLayoutManager() + fileprivate lazy var textContainer = NSTextContainer() + fileprivate lazy var textView: NSTextView = { + let result = NSTextView(frame: CGRect(), textContainer: textContainer) + result.font = NSFont.systemFont(ofSize: 13, weight: .regular) + result.allowsUndo = true + return result + }() + + fileprivate lazy var scrollview = NSScrollView() + + public func createTextViewStack() -> NSScrollView { + let contentSize = scrollview.contentSize + + textContainer.containerSize = CGSize(width: contentSize.width, height: CGFloat.greatestFiniteMagnitude) + textContainer.widthTracksTextView = true + + textView.minSize = CGSize(width: 0, height: 0) + textView.maxSize = CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude) + textView.isVerticallyResizable = true + textView.frame = CGRect(x: 0, y: 0, width: contentSize.width, height: contentSize.height) + textView.autoresizingMask = [.width] + textView.delegate = self + + scrollview.borderType = .noBorder + scrollview.hasVerticalScroller = true + scrollview.documentView = textView + + textStorage.addLayoutManager(layoutManager) + layoutManager.addTextContainer(textContainer) + + return scrollview + } + } +}