Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -136,72 +136,80 @@ public class TextSelectionManager: NSObject {

// MARK: - Selection Views

/// Update all selection cursors. Placing them in the correct position for each text selection and reseting the
/// blink timer.
func updateSelectionViews(force: Bool = false) {
/// Update all selection cursors. Placing them in the correct position for each text selection and
/// optionally reseting the blink timer.
func updateSelectionViews(force: Bool = false, skipTimerReset: Bool = false) {
guard textView?.isFirstResponder ?? false else { return }
var didUpdate: Bool = false

for textSelection in textSelections {
if textSelection.range.isEmpty {
guard let cursorRect = layoutManager?.rectForOffset(textSelection.range.location) else {
continue
}

var doesViewNeedReposition: Bool

// If using the system cursor, macOS will change the origin and height by about 0.5, so we do an
// approximate equals in that case to avoid extra updates.
if useSystemCursor, #available(macOS 14.0, *) {
doesViewNeedReposition = !textSelection.boundingRect.origin.approxEqual(cursorRect.origin)
|| !textSelection.boundingRect.height.approxEqual(layoutManager?.estimateLineHeight() ?? 0)
} else {
doesViewNeedReposition = textSelection.boundingRect.origin != cursorRect.origin
|| textSelection.boundingRect.height != layoutManager?.estimateLineHeight() ?? 0
}

if textSelection.view == nil || doesViewNeedReposition {
let cursorView: NSView
didUpdate = didUpdate || repositionCursorSelection(textSelection: textSelection)
} else if !textSelection.range.isEmpty && textSelection.view != nil {
textSelection.view?.removeFromSuperview()
textSelection.view = nil
didUpdate = true
}
}

if let existingCursorView = textSelection.view {
cursorView = existingCursorView
} else {
textSelection.view?.removeFromSuperview()
textSelection.view = nil
if didUpdate || force {
delegate?.setNeedsDisplay()
if !skipTimerReset {
cursorTimer.resetTimer()
resetSystemCursorTimers()
}
}
}

if useSystemCursor, #available(macOS 14.0, *) {
let systemCursorView = NSTextInsertionIndicator(frame: .zero)
cursorView = systemCursorView
systemCursorView.displayMode = .automatic
} else {
let internalCursorView = CursorView(color: insertionPointColor)
cursorView = internalCursorView
cursorTimer.register(internalCursorView)
}
private func repositionCursorSelection(textSelection: TextSelection) -> Bool {
guard let cursorRect = layoutManager?.rectForOffset(textSelection.range.location) else {
return false
}

textView?.addSubview(cursorView, positioned: .above, relativeTo: nil)
}
var doesViewNeedReposition: Bool

cursorView.frame.origin = cursorRect.origin
cursorView.frame.size.height = cursorRect.height
// If using the system cursor, macOS will change the origin and height by about 0.5, so we do an
// approximate equals in that case to avoid extra updates.
if useSystemCursor, #available(macOS 14.0, *) {
doesViewNeedReposition = !textSelection.boundingRect.origin.approxEqual(cursorRect.origin)
|| !textSelection.boundingRect.height.approxEqual(layoutManager?.estimateLineHeight() ?? 0)
} else {
doesViewNeedReposition = textSelection.boundingRect.origin != cursorRect.origin
|| textSelection.boundingRect.height != layoutManager?.estimateLineHeight() ?? 0
}

textSelection.view = cursorView
textSelection.boundingRect = cursorView.frame
if textSelection.view == nil || doesViewNeedReposition {
let cursorView: NSView

didUpdate = true
}
} else if !textSelection.range.isEmpty && textSelection.view != nil {
if let existingCursorView = textSelection.view {
cursorView = existingCursorView
} else {
textSelection.view?.removeFromSuperview()
textSelection.view = nil
didUpdate = true

if useSystemCursor, #available(macOS 14.0, *) {
let systemCursorView = NSTextInsertionIndicator(frame: .zero)
cursorView = systemCursorView
systemCursorView.displayMode = .automatic
} else {
let internalCursorView = CursorView(color: insertionPointColor)
cursorView = internalCursorView
cursorTimer.register(internalCursorView)
}

textView?.addSubview(cursorView, positioned: .above, relativeTo: nil)
}
}

if didUpdate || force {
delegate?.setNeedsDisplay()
cursorTimer.resetTimer()
resetSystemCursorTimers()
cursorView.frame.origin = cursorRect.origin
cursorView.frame.size.height = cursorRect.height

textSelection.view = cursorView
textSelection.boundingRect = cursorView.frame

return true
}

return false
}

private func resetSystemCursorTimers() {
Expand Down
1 change: 1 addition & 0 deletions Sources/CodeEditTextView/TextView/TextView+Layout.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ extension TextView {
override public func layout() {
super.layout()
layoutManager.layoutLines()
selectionManager.updateSelectionViews(skipTimerReset: true)
}

open override class var isCompatibleWithResponsiveScrolling: Bool {
Expand Down