NSLayoutManager 字形生成和插入符号位置

NSLayoutManager glyph generation and caret position

我有一个文本编辑器应用程序,它使用 Markdown 样式的格式。当 caret/selection 不在相应行时,格式化字符被隐藏。

我成功地使用 NSLayoutManagerDelegate 中的 shouldGenerateGlyphs: 方法来操纵 NSTextView 上绘制的内容。我使用的是纯委托,没有对布局管理器本身进行子类化。

但是,我无法理解如何在字形重新生成后正确定位插入符号。选择更改后,我将调用一个方法(此处称为 hideAndShowMarkup),该方法为当前编辑的行和之前选择的行重新生成字形。因为在屏幕上添加了一些字形,所以插入符号呈现错误。

-(void)hideAndShowMarkup {
    [self.layoutManager invalidateGlyphsForCharacterRange:currentLine.range changeInLength:0 actualCharacterRange:nil];
    [self.layoutManager invalidateGlyphsForCharacterRange:prevLine.range changeInLength:0 actualCharacterRange:nil];
    
    [self.layoutManager ensureGlyphsForCharacterRange:currentLine.range];
    [self.layoutManager ensureGlyphsForCharacterRange:prevLine.range];

    [self updateInsertionPointStateAndRestartTimer:YES];
}

updateInsertionPoint... 在这里不起作用,因为确保字形似乎 运行 异步。我也试过在 didCompleteLayoutForTextContainer: 委托方法中调用它,但没有效果。

有没有办法检测字形实际绘制的时间,并确保之后插入点的位置?

NSLayoutManager 的文档出奇地少,要了解其内部工作原理,需要查看随机 public 存储库中的实现。

要更改范围内的字形并使插入符位置正确,您首先需要使两个更改范围的字形无效,然后使范围内的 layout 无效插入符号即将位于。

然后,在确保有当前图形上下文后,可以同步重绘字形。之后更新插入点就正常了

在下面的示例中,我有两个对象,lineprevLine,它们包含插入符号现在所在的行和一个插入符号移开的范围。

-(void)hideAndShowMarkup {
    [self.layoutManager invalidateGlyphsForCharacterRange:line.range changeInLength:0 actualCharacterRange:nil];
    [self.layoutManager invalidateGlyphsForCharacterRange:prevLine.range changeInLength:0 actualCharacterRange:nil];
    [self.layoutManager invalidateLayoutForCharacterRange:line.range actualCharacterRange:nil];
        
    if (NSGraphicsContext.currentContext) {
        [self.layoutManager drawGlyphsForGlyphRange:line.range atPoint:self.frame.origin];
        [self.layoutManager drawGlyphsForGlyphRange:prevLine.range atPoint:self.frame.origin];
    }
    
    [self updateInsertionPointStateAndRestartTimer:NO];
}