diff --git a/Sources/CodeEditTextView/TextSelectionManager/TextSelectionManager.swift b/Sources/CodeEditTextView/TextSelectionManager/TextSelectionManager.swift index c26b0e20..5310ce19 100644 --- a/Sources/CodeEditTextView/TextSelectionManager/TextSelectionManager.swift +++ b/Sources/CodeEditTextView/TextSelectionManager/TextSelectionManager.swift @@ -23,7 +23,7 @@ public class TextSelectionManager: NSObject { public class TextSelection: Hashable, Equatable { public var range: NSRange - weak var view: CursorView? + weak var view: NSView? var boundingRect: CGRect = .zero var suggestedXPos: CGFloat? /// The position this selection should 'rotate' around when modifying selections. @@ -71,12 +71,17 @@ public class TextSelectionManager: NSObject { public var insertionPointColor: NSColor = NSColor.labelColor { didSet { - textSelections.forEach { $0.view?.color = insertionPointColor } + textSelections.compactMap({ $0.view as? CursorView }).forEach { $0.color = insertionPointColor } } } public var highlightSelectedLine: Bool = true public var selectedLineBackgroundColor: NSColor = NSColor.selectedTextBackgroundColor.withSystemEffect(.disabled) public var selectionBackgroundColor: NSColor = NSColor.selectedTextBackgroundColor + public var useSystemCursor: Bool = false { + didSet { + updateSelectionViews() + } + } internal(set) public var textSelections: [TextSelection] = [] weak var layoutManager: TextLayoutManager? @@ -174,15 +179,26 @@ public class TextSelectionManager: NSObject { textSelection.view?.removeFromSuperview() textSelection.view = nil - let cursorView = CursorView(color: insertionPointColor) + let cursorView: NSView + + if useSystemCursor, #available(macOS 14.0, *) { + let systemCursorView = NSTextInsertionIndicator(frame: .zero) + cursorView = systemCursorView + systemCursorView.displayMode = .visible + } else { + let internalCursorView = CursorView(color: insertionPointColor) + cursorView = internalCursorView + cursorTimer.register(internalCursorView) + } + cursorView.frame.origin = cursorOrigin cursorView.frame.size.height = layoutManager?.estimateLineHeight() ?? 0 + textView?.addSubview(cursorView) + textSelection.view = cursorView textSelection.boundingRect = cursorView.frame - cursorTimer.register(cursorView) - didUpdate = true } } else if !textSelection.range.isEmpty && textSelection.view != nil { diff --git a/Sources/CodeEditTextView/TextView/TextView+ReplaceCharacters.swift b/Sources/CodeEditTextView/TextView/TextView+ReplaceCharacters.swift index 64eab098..202b75a6 100644 --- a/Sources/CodeEditTextView/TextView/TextView+ReplaceCharacters.swift +++ b/Sources/CodeEditTextView/TextView/TextView+ReplaceCharacters.swift @@ -20,7 +20,9 @@ extension TextView { NotificationCenter.default.post(name: Self.textWillChangeNotification, object: self) layoutManager.beginTransaction() textStorage.beginEditing() - // Can't insert an ssempty string into an empty range. One must be not empty + _undoManager?.beginGrouping() + + // Can't insert an empty string into an empty range. One must be not empty for range in ranges.sorted(by: { $0.location > $1.location }) where (delegate?.textView(self, shouldReplaceContentsIn: range, with: string) ?? true) && (!range.isEmpty || !string.isEmpty) { @@ -38,6 +40,8 @@ extension TextView { delegate?.textView(self, didReplaceContentsIn: range, with: string) } + + _undoManager?.endGrouping() layoutManager.endTransaction() textStorage.endEditing() selectionManager.notifyAfterEdit() diff --git a/Sources/CodeEditTextView/TextView/TextView.swift b/Sources/CodeEditTextView/TextView/TextView.swift index 90285fd3..871c134a 100644 --- a/Sources/CodeEditTextView/TextView/TextView.swift +++ b/Sources/CodeEditTextView/TextView/TextView.swift @@ -177,6 +177,22 @@ public class TextView: NSView, NSTextContent { layoutManager.lineBreakStrategy = newValue } } + + /// Determines if the text view uses the macOS system cursor or a ``CursorView`` for cursors. + /// + /// - Important: Only available after macOS 14. + public var useSystemCursor: Bool { + get { + selectionManager?.useSystemCursor ?? false + } + set { + guard #available(macOS 14, *) else { + logger.warning("useSystemCursor only available after macOS 14.") + return + } + selectionManager?.useSystemCursor = newValue + } + } open var contentType: NSTextContentType? @@ -203,7 +219,7 @@ public class TextView: NSView, NSTextContent { (" " as NSString).size(withAttributes: [.font: font]).width } - var _undoManager: CEUndoManager? + internal(set) public var _undoManager: CEUndoManager? @objc dynamic open var allowsUndo: Bool var scrollView: NSScrollView? {