NSTextView能否实现多插入点?自定义多选择移动行为方案
自定义NSTextView多选择/多插入点行为
我尝试修改NSTextView的多选择行为,具体是重写moveLeft:方法,让它不会将所有选区折叠到第一个位置,而是保留每个选区的起始位置。最初的实现代码如下:
@objc override func moveLeft(_ sender: Any?) { if selectedRanges.count < 2 { super.moveLeft(sender) return } let ranges = selectedRanges.map { NSValue(range: NSMakeRange($0.rangeValue.location, 0)) } setSelectedRanges(ranges, affinity: .downstream, stillSelecting: false) }
但修改后仍有其他场景会触发默认的选区行为。请问:长度为0的选区仅支持单选择场景吗?有没有无需自行实现完整文本系统的解决方案?
编辑:查阅官方文档后发现相关规定:
数组中的对象是已排序、不重叠、不连续的,且(除单个选区的情况外)必须是非零长度的
我这里提供一个临时解决方案供参考:
class MultiCaretView: NSTextView { // 追踪多个选区,包括零长度的情况 private var caretRanges: [NSValue]? = nil var multipleCarets: Bool { caretRanges != nil } var multipleSelections: Bool { selectedRanges.count > 1 } // 停止处理多插入点的操作 @objc override func moveDown(_ sender: Any?) { caretRanges = nil super.moveDown(sender) } @objc override func moveUp(_ sender: Any?) { caretRanges = nil super.moveUp(sender) } // 处理多插入点向右移动 @objc override func moveRight(_ sender: Any?) { guard multipleCarets || multipleSelections else { return super.moveRight(sender) } let selection = caretRanges ?? selectedRanges func mvRight(_ range: NSRange) -> NSRange { return if range.length == 0 { NSMakeRange(range.location + 1, 0) } else { NSMakeRange(range.location + range.length, 0) } } caretRanges = selection.map { NSValue(range: mvRight($0.rangeValue)) } setSelectedRanges(caretRanges!, affinity: .downstream, stillSelecting: false) needsDisplay = true } @objc override func moveRightAndModifySelection(_ sender: Any?) { guard multipleCarets || multipleSelections else { return super.moveRightAndModifySelection(sender) } let selection = caretRanges ?? selectedRanges func mvRightAndModifySelection(_ range: NSRange) -> NSRange { NSMakeRange(range.location, range.length + 1) } caretRanges = selection.map { NSValue(range: mvRightAndModifySelection($0.rangeValue)) } setSelectedRanges(caretRanges!, affinity: .downstream, stillSelecting: false) } // 其他方向的操作可依此类推... override func draw(_ dirtyRect: NSRect) { super.draw(dirtyRect) // 强制绘制自定义多插入点(无闪烁效果) guard let caretRanges else { return } guard let layoutManager else { return } guard let textContainer else { return } for range in caretRanges { if range.rangeValue.length == 0 { let glyphRange = layoutManager.glyphRange( forCharacterRange: range.rangeValue, actualCharacterRange: nil ) let rect = layoutManager.boundingRect( forGlyphRange: glyphRange, in: textContainer ) let caret = NSBezierPath(rect: NSInsetRect(rect, -1, 0)) insertionPointColor.setFill() caret.fill() } } } }
多插入点应用场景示例
比如可以通过少量按键快速修改YAML格式:
原始内容:
yellow: '#b58900' orange: '#cb4b16' red: '#dc322f'
修改后内容:
yellow: &yellow '#b58900' orange: &orange '#cb4b16' red: &red '#dc322f'
内容的提问来源于stack exchange,提问作者user1007047
相关产品推荐
相关产品推荐

