mirror of
https://github.com/coteditor/CotEditor.git
synced 2024-09-11 11:25:57 +03:00
Optimize invisible glyph generation
This commit is contained in:
parent
4a13ec7418
commit
e4834ee48a
@ -5,6 +5,11 @@ Change Log
|
||||
3.8.10 (unreleased)
|
||||
--------------------------
|
||||
|
||||
### Improvements
|
||||
|
||||
- Optimize invisible character drawing.
|
||||
|
||||
|
||||
### Fixes
|
||||
|
||||
- Fix an issue where the Unicode code point field in the document inspector displayed always “not selected.”
|
||||
|
@ -8,7 +8,7 @@
|
||||
//
|
||||
// ---------------------------------------------------------------------------
|
||||
//
|
||||
// © 2015-2019 1024jp
|
||||
// © 2015-2020 1024jp
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -91,8 +91,8 @@ final class FindPanelLayoutManager: NSLayoutManager {
|
||||
let fullwidthSpace = NSAttributedString(string: Invisible.fullwidthSpace.usedSymbol, attributes: fullwidthAttributes)
|
||||
|
||||
// draw invisibles glyph by glyph
|
||||
for glyphIndex in glyphsToShow.location..<glyphsToShow.upperBound {
|
||||
let charIndex = self.characterIndexForGlyph(at: glyphIndex)
|
||||
let characterRange = self.characterRange(forGlyphRange: glyphsToShow, actualGlyphRange: nil)
|
||||
for charIndex in characterRange.lowerBound..<characterRange.upperBound {
|
||||
let codeUnit = (string as NSString).character(at: charIndex)
|
||||
let invisible = Invisible(codeUnit: codeUnit)
|
||||
|
||||
@ -114,16 +114,15 @@ final class FindPanelLayoutManager: NSLayoutManager {
|
||||
guard showsFullwidthSpace else { continue }
|
||||
glyphString = fullwidthSpace
|
||||
|
||||
default:
|
||||
case .otherControl:
|
||||
guard showsOtherInvisibles else { continue }
|
||||
guard
|
||||
self.propertyForGlyph(at: glyphIndex) == .controlCharacter,
|
||||
self.textStorage?.attribute(.glyphInfo, at: charIndex, effectiveRange: nil) == nil
|
||||
else { continue }
|
||||
|
||||
let replaceFont = NSFont(named: .lucidaGrande, size: font.pointSize) ?? NSFont.systemFont(ofSize: font.pointSize)
|
||||
let charRange = self.characterRange(forGlyphRange: NSRange(location: glyphIndex, length: 1), actualGlyphRange: nil)
|
||||
let baseString = (string as NSString).substring(with: charRange)
|
||||
let controlRange = NSRange(location: charIndex, length: 1)
|
||||
let baseString = (string as NSString).substring(with: controlRange)
|
||||
|
||||
guard let glyphInfo = NSGlyphInfo(glyphName: "replacement", for: replaceFont, baseString: baseString) else { continue }
|
||||
|
||||
@ -132,11 +131,15 @@ final class FindPanelLayoutManager: NSLayoutManager {
|
||||
// (2015-09 by 1024jp)
|
||||
self.textStorage?.addAttributes([.glyphInfo: glyphInfo,
|
||||
.font: replaceFont,
|
||||
.foregroundColor: color], range: charRange)
|
||||
.foregroundColor: color], range: controlRange)
|
||||
continue
|
||||
|
||||
case .none:
|
||||
continue
|
||||
}
|
||||
|
||||
// calculate position to draw glyph
|
||||
let glyphIndex = self.glyphIndexForCharacter(at: charIndex)
|
||||
let lineOrigin = self.lineFragmentRect(forGlyphAt: glyphIndex, effectiveRange: nil, withoutAdditionalLayout: true).origin
|
||||
let glyphLocation = self.location(forGlyphAt: glyphIndex)
|
||||
let point = lineOrigin.offset(by: origin).offsetBy(dx: glyphLocation.x)
|
||||
|
@ -8,7 +8,7 @@
|
||||
//
|
||||
// ---------------------------------------------------------------------------
|
||||
//
|
||||
// © 2014-2018 1024jp
|
||||
// © 2014-2020 1024jp
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -31,7 +31,7 @@ enum Invisible {
|
||||
case tab
|
||||
case newLine
|
||||
case fullwidthSpace
|
||||
case replacement
|
||||
case otherControl
|
||||
|
||||
|
||||
var candidates: [String] {
|
||||
@ -45,7 +45,7 @@ enum Invisible {
|
||||
return ["¶", "↩", "↵", "⏎"]
|
||||
case .fullwidthSpace:
|
||||
return ["□", "⊠", "■", "•"]
|
||||
case .replacement:
|
||||
case .otherControl:
|
||||
return ["<EFBFBD>"]
|
||||
}
|
||||
}
|
||||
@ -82,8 +82,11 @@ extension Invisible {
|
||||
self = .newLine
|
||||
case 0x3000: // IDEOGRAPHIC SPACE a.k.a. full-width space (JP)
|
||||
self = .fullwidthSpace
|
||||
case 0x0000...0x001F, 0x0080...0x009F, 0x200B: // C0, C1, ZERO WIDTH SPACE
|
||||
// -> NSGlyphGenerator generates NSControlGlyph for all characters
|
||||
// in the Unicode General Category C* and U+200B (ZERO WIDTH SPACE).
|
||||
self = .otherControl
|
||||
default:
|
||||
// `.replacement` cannot be determined only with code unit
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@ -114,7 +117,7 @@ extension Invisible {
|
||||
case .tab: return .invisibleTab
|
||||
case .newLine: return .invisibleNewLine
|
||||
case .fullwidthSpace: return .invisibleFullwidthSpace
|
||||
case .replacement: return nil
|
||||
case .otherControl: return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,7 @@ final class LayoutManager: NSLayoutManager, ValidationIgnorable {
|
||||
self.spaceWidth = textFont.spaceWidth
|
||||
|
||||
self.invisibleLines = self.generateInvisibleLines()
|
||||
self.replacementGlyphWidth = self.invisibleLines.replacement.bounds().width
|
||||
self.replacementGlyphWidth = self.invisibleLines.otherControl.bounds().width
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,7 +106,7 @@ final class LayoutManager: NSLayoutManager, ValidationIgnorable {
|
||||
let tab: CTLine
|
||||
let newLine: CTLine
|
||||
let fullwidthSpace: CTLine
|
||||
let replacement: CTLine
|
||||
let otherControl: CTLine
|
||||
}
|
||||
|
||||
|
||||
@ -215,8 +215,8 @@ final class LayoutManager: NSLayoutManager, ValidationIgnorable {
|
||||
}
|
||||
|
||||
// draw invisibles glyph by glyph
|
||||
for glyphIndex in glyphsToShow.location..<glyphsToShow.upperBound {
|
||||
let charIndex = self.characterIndexForGlyph(at: glyphIndex)
|
||||
let characterRange = self.characterRange(forGlyphRange: glyphsToShow, actualGlyphRange: nil)
|
||||
for charIndex in characterRange.lowerBound..<characterRange.upperBound {
|
||||
let codeUnit = string.character(at: charIndex)
|
||||
let invisible = Invisible(codeUnit: codeUnit)
|
||||
|
||||
@ -238,13 +238,16 @@ final class LayoutManager: NSLayoutManager, ValidationIgnorable {
|
||||
guard self.showsFullwidthSpace else { continue }
|
||||
line = self.invisibleLines.fullwidthSpace
|
||||
|
||||
default:
|
||||
case .otherControl:
|
||||
guard self.showsOtherInvisibles else { continue }
|
||||
guard self.propertyForGlyph(at: glyphIndex) == .controlCharacter else { continue }
|
||||
line = self.invisibleLines.replacement
|
||||
line = self.invisibleLines.otherControl
|
||||
|
||||
case .none:
|
||||
continue
|
||||
}
|
||||
|
||||
// calculate position to draw glyph
|
||||
let glyphIndex = self.glyphIndexForCharacter(at: charIndex)
|
||||
let lineOrigin = self.lineFragmentRect(forGlyphAt: glyphIndex, effectiveRange: nil, withoutAdditionalLayout: true).origin
|
||||
let glyphLocation = self.location(forGlyphAt: glyphIndex)
|
||||
var point = lineOrigin.offset(by: origin).offsetBy(dx: glyphLocation.x,
|
||||
@ -341,7 +344,7 @@ final class LayoutManager: NSLayoutManager, ValidationIgnorable {
|
||||
tab: self.invisibleLine(.tab, font: font),
|
||||
newLine: self.invisibleLine(.newLine, font: font),
|
||||
fullwidthSpace: self.invisibleLine(.fullwidthSpace, font: fullWidthFont),
|
||||
replacement: self.invisibleLine(.replacement, font: textFont))
|
||||
otherControl: self.invisibleLine(.otherControl, font: textFont))
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user