@@ -23,7 +23,7 @@ public class TextSelectionManager: NSObject {
23
23
24
24
public class TextSelection : Hashable , Equatable {
25
25
public var range : NSRange
26
- weak var view : CursorView ?
26
+ weak var view : NSView ?
27
27
var boundingRect : CGRect = . zero
28
28
var suggestedXPos : CGFloat ?
29
29
/// The position this selection should 'rotate' around when modifying selections.
@@ -71,12 +71,17 @@ public class TextSelectionManager: NSObject {
71
71
72
72
public var insertionPointColor : NSColor = NSColor . labelColor {
73
73
didSet {
74
- textSelections. forEach { $0. view? . color = insertionPointColor }
74
+ textSelections. compactMap ( { $0. view as? CursorView } ) . forEach { $0 . color = insertionPointColor }
75
75
}
76
76
}
77
77
public var highlightSelectedLine : Bool = true
78
78
public var selectedLineBackgroundColor : NSColor = NSColor . selectedTextBackgroundColor. withSystemEffect ( . disabled)
79
79
public var selectionBackgroundColor : NSColor = NSColor . selectedTextBackgroundColor
80
+ public var useSystemCursor : Bool = false {
81
+ didSet {
82
+ updateSelectionViews ( )
83
+ }
84
+ }
80
85
81
86
internal( set) public var textSelections : [ TextSelection ] = [ ]
82
87
weak var layoutManager : TextLayoutManager ?
@@ -89,7 +94,8 @@ public class TextSelectionManager: NSObject {
89
94
layoutManager: TextLayoutManager ,
90
95
textStorage: NSTextStorage ,
91
96
textView: TextView ? ,
92
- delegate: TextSelectionManagerDelegate ?
97
+ delegate: TextSelectionManagerDelegate ? ,
98
+ useSystemCursor: Bool = false
93
99
) {
94
100
self . layoutManager = layoutManager
95
101
self . textStorage = textStorage
@@ -168,21 +174,47 @@ public class TextSelectionManager: NSObject {
168
174
for textSelection in textSelections {
169
175
if textSelection. range. isEmpty {
170
176
let cursorOrigin = ( layoutManager? . rectForOffset ( textSelection. range. location) ?? . zero) . origin
171
- if textSelection. view == nil
172
- || textSelection. boundingRect. origin != cursorOrigin
173
- || textSelection. boundingRect. height != layoutManager? . estimateLineHeight ( ) ?? 0 {
174
- textSelection. view? . removeFromSuperview ( )
175
- textSelection. view = nil
176
177
177
- let cursorView = CursorView ( color: insertionPointColor)
178
+ var doesViewNeedReposition : Bool
179
+
180
+ // If using the system cursor, macOS will change the origin and height by about 0.5, so we do an
181
+ // approximate equals in that case to avoid extra updates.
182
+ if useSystemCursor, #available( macOS 14 . 0 , * ) {
183
+ doesViewNeedReposition = !textSelection. boundingRect. origin. approxEqual ( cursorOrigin)
184
+ || !textSelection. boundingRect. height. approxEqual ( layoutManager? . estimateLineHeight ( ) ?? 0 )
185
+ } else {
186
+ doesViewNeedReposition = textSelection. boundingRect. origin != cursorOrigin
187
+ || textSelection. boundingRect. height != layoutManager? . estimateLineHeight ( ) ?? 0
188
+ }
189
+
190
+ if textSelection. view == nil || doesViewNeedReposition {
191
+ let cursorView : NSView
192
+
193
+ if let existingCursorView = textSelection. view {
194
+ cursorView = existingCursorView
195
+ } else {
196
+ textSelection. view? . removeFromSuperview ( )
197
+ textSelection. view = nil
198
+
199
+ if useSystemCursor, #available( macOS 14 . 0 , * ) {
200
+ let systemCursorView = NSTextInsertionIndicator ( frame: . zero)
201
+ cursorView = systemCursorView
202
+ systemCursorView. displayMode = . automatic
203
+ } else {
204
+ let internalCursorView = CursorView ( color: insertionPointColor)
205
+ cursorView = internalCursorView
206
+ cursorTimer. register ( internalCursorView)
207
+ }
208
+
209
+ textView? . addSubview ( cursorView)
210
+ }
211
+
178
212
cursorView. frame. origin = cursorOrigin
179
213
cursorView. frame. size. height = layoutManager? . estimateLineHeight ( ) ?? 0
180
- textView ? . addSubview ( cursorView )
214
+
181
215
textSelection. view = cursorView
182
216
textSelection. boundingRect = cursorView. frame
183
217
184
- cursorTimer. register ( cursorView)
185
-
186
218
didUpdate = true
187
219
}
188
220
} else if !textSelection. range. isEmpty && textSelection. view != nil {
0 commit comments