@@ -136,72 +136,80 @@ public class TextSelectionManager: NSObject {
136136
137137 // MARK: - Selection Views
138138
139- /// Update all selection cursors. Placing them in the correct position for each text selection and reseting the
140- /// blink timer.
141- func updateSelectionViews( force: Bool = false ) {
139+ /// Update all selection cursors. Placing them in the correct position for each text selection and
140+ /// optionally reseting the blink timer.
141+ func updateSelectionViews( force: Bool = false , skipTimerReset : Bool = false ) {
142142 guard textView? . isFirstResponder ?? false else { return }
143143 var didUpdate : Bool = false
144144
145145 for textSelection in textSelections {
146146 if textSelection. range. isEmpty {
147- guard let cursorRect = layoutManager? . rectForOffset ( textSelection. range. location) else {
148- continue
149- }
150-
151- var doesViewNeedReposition : Bool
152-
153- // If using the system cursor, macOS will change the origin and height by about 0.5, so we do an
154- // approximate equals in that case to avoid extra updates.
155- if useSystemCursor, #available( macOS 14 . 0 , * ) {
156- doesViewNeedReposition = !textSelection. boundingRect. origin. approxEqual ( cursorRect. origin)
157- || !textSelection. boundingRect. height. approxEqual ( layoutManager? . estimateLineHeight ( ) ?? 0 )
158- } else {
159- doesViewNeedReposition = textSelection. boundingRect. origin != cursorRect. origin
160- || textSelection. boundingRect. height != layoutManager? . estimateLineHeight ( ) ?? 0
161- }
162-
163- if textSelection. view == nil || doesViewNeedReposition {
164- let cursorView : NSView
147+ didUpdate = didUpdate || repositionCursorSelection ( textSelection: textSelection)
148+ } else if !textSelection. range. isEmpty && textSelection. view != nil {
149+ textSelection. view? . removeFromSuperview ( )
150+ textSelection. view = nil
151+ didUpdate = true
152+ }
153+ }
165154
166- if let existingCursorView = textSelection. view {
167- cursorView = existingCursorView
168- } else {
169- textSelection. view? . removeFromSuperview ( )
170- textSelection. view = nil
155+ if didUpdate || force {
156+ delegate? . setNeedsDisplay ( )
157+ if !skipTimerReset {
158+ cursorTimer. resetTimer ( )
159+ resetSystemCursorTimers ( )
160+ }
161+ }
162+ }
171163
172- if useSystemCursor, #available( macOS 14 . 0 , * ) {
173- let systemCursorView = NSTextInsertionIndicator ( frame: . zero)
174- cursorView = systemCursorView
175- systemCursorView. displayMode = . automatic
176- } else {
177- let internalCursorView = CursorView ( color: insertionPointColor)
178- cursorView = internalCursorView
179- cursorTimer. register ( internalCursorView)
180- }
164+ private func repositionCursorSelection( textSelection: TextSelection ) -> Bool {
165+ guard let cursorRect = layoutManager? . rectForOffset ( textSelection. range. location) else {
166+ return false
167+ }
181168
182- textView? . addSubview ( cursorView, positioned: . above, relativeTo: nil )
183- }
169+ var doesViewNeedReposition : Bool
184170
185- cursorView. frame. origin = cursorRect. origin
186- cursorView. frame. size. height = cursorRect. height
171+ // If using the system cursor, macOS will change the origin and height by about 0.5, so we do an
172+ // approximate equals in that case to avoid extra updates.
173+ if useSystemCursor, #available( macOS 14 . 0 , * ) {
174+ doesViewNeedReposition = !textSelection. boundingRect. origin. approxEqual ( cursorRect. origin)
175+ || !textSelection. boundingRect. height. approxEqual ( layoutManager? . estimateLineHeight ( ) ?? 0 )
176+ } else {
177+ doesViewNeedReposition = textSelection. boundingRect. origin != cursorRect. origin
178+ || textSelection. boundingRect. height != layoutManager? . estimateLineHeight ( ) ?? 0
179+ }
187180
188- textSelection. view = cursorView
189- textSelection . boundingRect = cursorView. frame
181+ if textSelection. view == nil || doesViewNeedReposition {
182+ let cursorView : NSView
190183
191- didUpdate = true
192- }
193- } else if !textSelection . range . isEmpty && textSelection . view != nil {
184+ if let existingCursorView = textSelection . view {
185+ cursorView = existingCursorView
186+ } else {
194187 textSelection. view? . removeFromSuperview ( )
195188 textSelection. view = nil
196- didUpdate = true
189+
190+ if useSystemCursor, #available( macOS 14 . 0 , * ) {
191+ let systemCursorView = NSTextInsertionIndicator ( frame: . zero)
192+ cursorView = systemCursorView
193+ systemCursorView. displayMode = . automatic
194+ } else {
195+ let internalCursorView = CursorView ( color: insertionPointColor)
196+ cursorView = internalCursorView
197+ cursorTimer. register ( internalCursorView)
198+ }
199+
200+ textView? . addSubview ( cursorView, positioned: . above, relativeTo: nil )
197201 }
198- }
199202
200- if didUpdate || force {
201- delegate? . setNeedsDisplay ( )
202- cursorTimer. resetTimer ( )
203- resetSystemCursorTimers ( )
203+ cursorView. frame. origin = cursorRect. origin
204+ cursorView. frame. size. height = cursorRect. height
205+
206+ textSelection. view = cursorView
207+ textSelection. boundingRect = cursorView. frame
208+
209+ return true
204210 }
211+
212+ return false
205213 }
206214
207215 private func resetSystemCursorTimers( ) {
0 commit comments