1
1
mirror of https://github.com/qvacua/vimr.git synced 2024-10-28 03:38:54 +03:00

Use CoreText's font metric

This commit is contained in:
Tae Won Ha 2016-06-19 13:39:20 +02:00
parent bbc0096019
commit d5420f4b78
No known key found for this signature in database
GPG Key ID: E40743465B5B8B44
4 changed files with 54 additions and 33 deletions

View File

@ -157,6 +157,7 @@ class Grid: CustomStringConvertible {
}
private func clearRegion(region: Region) {
// FIXME: sometimes clearRegion gets called without first resizing the Grid. Should we handle this?
guard self.hasData else {
return
}

View File

@ -48,7 +48,8 @@ public class NeoVimView: NSView {
didSet {
self.drawer.font = self.font
self.cellSize = self.drawer.cellSize
self.lineSpace = self.drawer.lineSpace
self.descent = self.drawer.descent
self.leading = self.drawer.leading
// FIXME: resize and redraw
}
@ -58,21 +59,23 @@ public class NeoVimView: NSView {
private let drawer: TextDrawer
private var cellSize = CGSize.zero
private var lineSpace = CGFloat(0)
private var descent = CGFloat(0)
private var leading = CGFloat(0)
private let grid = Grid()
init(frame rect: NSRect = CGRect.zero, xpc: NeoVimXpc) {
self.xpc = xpc
self.font = NSFont(name: "Menlo", size: 13)!
self.font = NSFont(name: "Menlo", size: 16)!
self.drawer = TextDrawer(font: font)
super.init(frame: rect)
self.wantsLayer = true
self.cellSize = self.drawer.cellSize
self.lineSpace = self.drawer.lineSpace
self.descent = self.drawer.descent
self.leading = self.drawer.leading
}
override public func keyDown(theEvent: NSEvent) {
@ -100,7 +103,7 @@ public class NeoVimView: NSView {
self.drawBackground(positions: positions, background: rowFrag.attrs.background)
let string = self.grid.cells[rowFrag.row][rowFrag.range].reduce("") { $0 + $1.string }
let glyphPositions = positions.map { CGPoint(x: $0.x, y: $0.y + self.lineSpace) }
let glyphPositions = positions.map { CGPoint(x: $0.x, y: $0.y + self.descent + self.leading) }
self.drawer.drawString(string,
positions: UnsafeMutablePointer(glyphPositions), positionsCount: positions.count,
highlightAttrs: rowFrag.attrs,
@ -120,23 +123,26 @@ public class NeoVimView: NSView {
private func rowRunIntersecting(rects rects: [CGRect]) -> [RowRun] {
return rects
.map { rect -> Region in
// Get all Regions that intersects with the given rects. There can be overlaps between the Regions, but for the
// time being we ignore them; probably not necessary to optimize them away.
let rowStart = Int(floor((self.frame.height - (rect.origin.y + rect.size.height)) / self.cellSize.height))
let rowEnd = Int(ceil((self.frame.height - rect.origin.y) / self.cellSize.height)) - 1
let columnStart = Int(floor(rect.origin.x / self.cellSize.width))
let columnEnd = Int(ceil((rect.origin.x + rect.size.width) / self.cellSize.width)) - 1
return Region(top: rowStart, bottom: rowEnd, left: columnStart, right: columnEnd)
} // There can be overlaps between the Regions, but for the time being we ignore them.
}
.map { region -> [RowRun] in
return (region.rowRange)
// Map Regions to RowRuns for drawing.
return region.rowRange
// Map each row in a Region to RowRuns
.map { row -> [RowRun] in
let range = region.columnRange
let columns = region.columnRange
let rowCells = self.grid.cells[row]
let startIndex = range.startIndex
let startIndex = columns.startIndex
var result = [
RowRun(row: row, range: startIndex...startIndex, attrs: rowCells[startIndex].attrs)
]
range.forEach { idx in
var result = [ RowRun(row: row, range: startIndex...startIndex, attrs: rowCells[startIndex].attrs) ]
columns.forEach { idx in
if rowCells[idx].attrs == result.last!.attrs {
let last = result.popLast()!
result.append(RowRun(row: row, range: last.range.startIndex...idx, attrs: last.attrs))
@ -145,11 +151,11 @@ public class NeoVimView: NSView {
}
}
return result
} // -> [[RowRun]]
.flatMap { $0 } // -> [RowRun]
} // -> [[RowRun]]
.flatMap { $0 } // -> [RowRun]
return result // All RowRuns for a row in a Region.
} // All RowRuns for all rows in a Region grouped by row.
.flatMap { $0 } // Flattened RowRuns for a Region.
} // All RowRuns for all Regions grouped by Region.
.flatMap { $0 } // Flattened RowRuns for all Regions.
}
private func positionOnView(row: Int, column: Int) -> CGPoint {

View File

@ -11,7 +11,8 @@
@interface TextDrawer : NSObject
@property (nonatomic, nonnull, retain) NSFont *font;
@property (nonatomic, readonly) CGFloat lineSpace;
@property (nonatomic, readonly) CGFloat leading;
@property (nonatomic, readonly) CGFloat descent;
@property (nonatomic, readonly) CGSize cellSize;
- (instancetype _Nonnull)initWithFont:(NSFont *_Nonnull)font;

View File

@ -19,8 +19,8 @@
NSLayoutManager *_layoutManager;
NSFont *_font;
CGFloat _fontDescent;
CGFloat _lineGap;
CGFloat _underlinePosition;
CGFloat _underlineThickness;
NSMutableArray *_fontLookupCache;
NSMutableDictionary *_fontTraitCache;
@ -30,13 +30,23 @@
[_font autorelease];
_font = [font retain];
// cf. https://developer.apple.com/library/mac/documentation/TextFonts/Conceptual/CocoaTextArchitecture/FontHandling/FontHandling.html
CGFloat ascent = CTFontGetAscent((CTFontRef) _font);
CGFloat descent = CTFontGetDescent((CTFontRef) _font);
CGFloat leading = CTFontGetLeading((CTFontRef) _font);
CGFloat underlinePosition = CTFontGetUnderlinePosition((CTFontRef) _font);
CGFloat underlineThickness = CTFontGetUnderlineThickness((CTFontRef) _font);
_cellSize = CGSizeMake(
round([@"m" sizeWithAttributes:@{ NSFontAttributeName : _font }].width),
[_layoutManager defaultLineHeightForFont:_font] + _lineSpace
ceil(ascent + descent + leading)
);
// https://developer.apple.com/library/mac/documentation/TextFonts/Conceptual/CocoaTextArchitecture/FontHandling/FontHandling.html
_lineGap = _cellSize.height - _font.ascender - _font.descender;
_fontDescent = CTFontGetDescent((CTFontRef) _font);
_leading = leading;
_descent = descent;
_underlinePosition = underlinePosition; // This seems to take the thickness into account
// TODO: Maybe we should use 0.5 or 1 as minimum thickness for Retina and non-Retina, respectively.
_underlineThickness = underlineThickness;
}
- (instancetype)initWithFont:(NSFont *_Nonnull)font {
@ -49,7 +59,6 @@
_fontLookupCache = [[NSMutableArray alloc] init];
_fontTraitCache = [[NSMutableDictionary alloc] init];
_lineSpace = 4;
self.font = font;
return self;
@ -79,11 +88,7 @@
CGContextSaveGState(context);
if (attrs.fontTrait & FontTraitUnderline) {
CGRect rect = {
{positions[0].x, positions[0].y - 1},
{positions[0].x + positions[positionsCount - 1].x + _cellSize.width, 1}
};
[self drawUnderline:rect color:attrs.special context:context];
[self drawUnderline:positions count:positionsCount color:attrs.special context:context];
}
[self drawString:string positions:positions
@ -93,8 +98,16 @@
CGContextRestoreGState(context);
}
- (void)drawUnderline:(CGRect)rect color:(unsigned int)color context:(CGContextRef _Nonnull)context {
- (void)drawUnderline:(const CGPoint *_Nonnull)positions
count:(NSInteger)count
color:(unsigned int)color
context:(CGContextRef _Nonnull)context
{
CGContextSetRGBFillColor(context, RED(color), GREEN(color), BLUE(color), ALPHA(color));
CGRect rect = {
{positions[0].x, positions[0].y + _underlinePosition},
{positions[0].x + positions[count - 1].x + _cellSize.width, _underlineThickness}
};
CGContextFillRect(context, rect);
}
@ -115,7 +128,7 @@
unichars = unibuffer;
}
CGGlyph *glyphs = malloc(unilength * sizeof(UniChar));
CGGlyph *glyphs = malloc(unilength * sizeof(CGGlyph));
CTFontRef fontWithTraits = [self fontWithTrait:fontTrait];
CGContextSetRGBFillColor(context, RED(foreground), GREEN(foreground), BLUE(foreground), 1.0);