gpt4 book ai didi

swift - 无论我做什么,NSLayoutManager 都会隐藏换行符

转载 作者:可可西里 更新时间:2023-11-01 00:58:26 32 4
gpt4 key购买 nike

我试图在我的 NSTextView 子类中显示不可见字符,例如换行符。覆盖 NSLayoutManager 的 drawGlyph 方法这样的常用方法是一个坏主意,因为它太慢并且不能正确处理多页布局。

我想做的是重写 NSLayoutManager 的 setGlyph 方法,这样它将用“¶”字形替换不可见的“\n”字形,用“∙”替换“”。

它适用于 ""空格字形,但对换行符没有影响。

public override func setGlyphs(_ glyphs: UnsafePointer<CGGlyph>, properties props: UnsafePointer<NSGlyphProperty>, characterIndexes charIndexes: UnsafePointer<Int>, font aFont: Font, forGlyphRange glyphRange: NSRange) {
var substring = (self.currentTextStorage.string as NSString).substring(with: glyphRange)

// replace invisible characters with visible
if PreferencesManager.shared.shouldShowInvisibles == true {
substring = substring.replacingOccurrences(of: " ", with: "\u{00B7}")
substring = substring.replacingOccurrences(of: "\n", with: "u{00B6}")
}

// create a CFString
let stringRef = substring as CFString
let count = CFStringGetLength(stringRef)

// convert processed string to the C-pointer
let cfRange = CFRangeMake(0, count)
let fontRef = CTFontCreateWithName(aFont.fontName as CFString?, aFont.pointSize, nil)
let characters = UnsafeMutablePointer<UniChar>.allocate(capacity: MemoryLayout<UniChar>.size * count)
CFStringGetCharacters(stringRef, cfRange, characters)

// get glyphs for the pointer of characters
let glyphsRef = UnsafeMutablePointer<CGGlyph>.allocate(capacity: MemoryLayout<CGGlyph>.size * count)
CTFontGetGlyphsForCharacters(fontRef, characters, glyphsRef, count)

// set those glyphs
super.setGlyphs(glyphsRef, properties:props, characterIndexes: charIndexes, font: aFont, forGlyphRange: glyphRange)
}

然后我想出了一个主意:看起来 NSTypesetter 标记了新的行字符范围,就像它根本不应该处理的那样。所以我继承了 NSTypesetter 并覆盖了一个方法:

override func setNotShownAttribute(_ flag: Bool, forGlyphRange glyphRange: NSRange) {
let theFlag = PreferencesManager.shared.shouldShowInvisibles == true ? false : true
super.setNotShownAttribute(theFlag, forGlyphRange: glyphRange)
}

但它不起作用。无论我创建什么字形,NSLayoutManager 仍然不会为换行符生成字形。

我做错了什么?

最佳答案

正如我发现的那样,NSTypesetter 的 setNotShownAttribute: 类的默认实现不会更改其字形存储中已经生成的字形。因此,调用 super 不会产生任何效果。我只需要在调用 super 之前手动替换字形。

因此,显示不可见字符的最有效实现(缩放 View 时您会看到差异)是这样的:

这种方法的局限性:如果您的应用必须在 TextView 中使用多种字体,那么这种方法可能不是一个好主意,因为那些显示的不可见字符的字体会有所不同出色地。这不是您想要实现的目标。

  1. 继承 NSLayoutManager 并覆盖 setGlyphs 以显示空格字符:

    public override func setGlyphs(_ glyphs: UnsafePointer<CGGlyph>, properties props: UnsafePointer<NSGlyphProperty>, characterIndexes charIndexes: UnsafePointer<Int>, font aFont: Font, forGlyphRange glyphRange: NSRange) {
    var substring = (self.currentTextStorage.string as NSString).substring(with: glyphRange)

    // replace invisible characters with visible
    if PreferencesManager.shared.shouldShowInvisibles == true {
    substring = substring.replacingOccurrences(of: " ", with: "\u{00B7}")
    }

    // create a CFString
    let stringRef = substring as CFString
    let count = CFStringGetLength(stringRef)

    // convert processed string to the C-pointer
    let cfRange = CFRangeMake(0, count)
    let fontRef = CTFontCreateWithName(aFont.fontName as CFString?, aFont.pointSize, nil)
    let characters = UnsafeMutablePointer<UniChar>.allocate(capacity: MemoryLayout<UniChar>.size * count)
    CFStringGetCharacters(stringRef, cfRange, characters)

    // get glyphs for the pointer of characters
    let glyphsRef = UnsafeMutablePointer<CGGlyph>.allocate(capacity: MemoryLayout<CGGlyph>.size * count)
    CTFontGetGlyphsForCharacters(fontRef, characters, glyphsRef, count)

    // set those glyphs
    super.setGlyphs(glyphsRef, properties:props, characterIndexes: charIndexes, font: aFont, forGlyphRange: glyphRange)
    }
  2. 子类 NSATSTypesetter 并将其分配给您的 NSLayoutManager 子类。子类将显示新行字符并确保每个不可见字符都将以不同的颜色绘制:

    class CustomTypesetter: NSATSTypesetter {

    override func setNotShownAttribute(_ flag: Bool, forGlyphRange glyphRange: NSRange) {
    var theFlag = flag

    if PreferencesManager.shared.shouldShowInvisibles == true {
    theFlag = false

    // add new line glyphs into the glyph storage
    var newLineGlyph = yourFont.glyph(withName: "paragraph")
    self.substituteGlyphs(in: glyphRange, withGlyphs: &newLineGlyph)

    // draw new line char with different color
    self.layoutManager?.addTemporaryAttribute(NSForegroundColorAttributeName, value: NSColor.invisibleTextColor, forCharacterRange: glyphRange)
    }

    super.setNotShownAttribute(theFlag, forGlyphRange: glyphRange)
    }

    /// Currently hadn't found any faster way to draw space glyphs with different color
    override func setParagraphGlyphRange(_ paragraphRange: NSRange, separatorGlyphRange paragraphSeparatorRange: NSRange) {
    super.setParagraphGlyphRange(paragraphRange, separatorGlyphRange: paragraphSeparatorRange)

    guard PreferencesManager.shared.shouldShowInvisibles == true else { return }

    if let substring = (self.layoutManager?.textStorage?.string as NSString?)?.substring(with: paragraphRange) {
    let expression = try? NSRegularExpression.init(pattern: "\\s", options: NSRegularExpression.Options.useUnicodeWordBoundaries)
    let sunstringRange = NSRange(location: 0, length: substring.characters.count)

    if let matches = expression?.matches(in: substring, options: NSRegularExpression.MatchingOptions.withoutAnchoringBounds, range: sunstringRange) {
    for match in matches {
    let globalSubRange = NSRange(location: paragraphRange.location + match.range.location, length: 1)
    self.layoutManager?.addTemporaryAttribute(NSForegroundColorAttributeName, value: Color.invisibleText, forCharacterRange: globalSubRange)
    }
    }
    }
    }
    }
  3. 要显示/隐藏不可见字符,只需调用:

    let storageRange = NSRange(location: 0, length: currentTextStorage.length)
    layoutManager.invalidateGlyphs(forCharacterRange: storageRange, changeInLength: 0, actualCharacterRange: nil)
    layoutManager.ensureGlyphs(forGlyphRange: storageRange)

关于swift - 无论我做什么,NSLayoutManager 都会隐藏换行符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39545718/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com