Символьный индекс левого / правого символа в биди-тексте в TextKit
Я работаю с подклассом NSTextView и хотел бы знать индекс символа рядом с текущей точкой вставки. Точнее, я хочу получить их индекс символов для перемещения курсора с помощью клавиши со стрелкой, но без использования NSResponder. moveLeft(_:)
или же moveRight(_:)
семьи.
Это легко с помощью обычного горизонтального однонаправленного текста, потому что index-1
левый и index+1
является правильным (если вы забудете о том, что буквы состоят из нескольких символов). Однако при использовании двухстороннего текста, в котором смешаны слева направо и справа налево, порядок визуальных символов, отображаемый в представлении, не совпадает с фактическим индексом символов.
Реализация по умолчанию NSTextView, кажется, выполняет биди-текст как-то. Поэтому я хотел бы получить то же поведение перемещения курсора NSTextView, но я не смог найти подходящий публичный API для него.
До сих пор я нашел метод NSLayoutManager getGlyphs(in:glyphs:properties:characterIndexes:bidiLevels:)
это кажется правильным методом, чтобы получить биди условие. Поэтому я написал метод расширения, подобный следующему. Но тогда, даже если бы я мог получить состояние RTL определенного местоположения, я не уверен, что индекс символа следующего, когда индекс находится на границе букв RTL и LTR.
extension NSLayoutManager {
func isRTL(at index: Int) -> Bool {
let glyphIndex = self.glyphIndexForCharacter(at: index)
guard glyphIndex > 0 else { return false }
var bidiLevels: [UInt8] = [0]
self.getGlyphs(in: NSRange((glyphIndex-1)..<glyphIndex), glyphs: nil, properties: nil, characterIndexes: nil, bidiLevels: &bidiLevels)
return (bidiLevels[0] % 2 == 1)
}
}
Другой подход, который я сейчас пробую, это получить его из координаты вида, как показано ниже. Но затем метод пропускает символы нулевой ширины.
extension NSTextView {
func leftIndex(of index: Int) -> Int {
guard
let layoutManager = self.layoutManager,
let textContainer = self.textContainer
else { assertionFailure(); return index }
let glyphIndex = layoutManager.glyphIndexForCharacter(at: index)
let rect = layoutManager.boundingRect(forGlyphRange: NSRange(glyphIndex..<(glyphIndex+1)), in: textContainer)
.offset(by: self.textContainerOrigin)
let point = NSPoint(x: rect.minX - 1, y: rect.midY)
return self.characterIndexForInsertion(at: point)
}
}
У кого-нибудь есть идея выполнить мое требование?