Skip to content

Commit a0015f0

Browse files
committed
Update Cursor Positions in TextView.layout
1 parent 7d63a64 commit a0015f0

File tree

2 files changed

+59
-50
lines changed

2 files changed

+59
-50
lines changed

Sources/CodeEditTextView/TextSelectionManager/TextSelectionManager.swift

Lines changed: 58 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -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() {

Sources/CodeEditTextView/TextView/TextView+Layout.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ extension TextView {
1111
override public func layout() {
1212
super.layout()
1313
layoutManager.layoutLines()
14+
selectionManager.updateSelectionViews(skipTimerReset: true)
1415
}
1516

1617
open override class var isCompatibleWithResponsiveScrolling: Bool {

0 commit comments

Comments
 (0)