As I said, from scratch. So, everything, including the underlying text storage (an ASCII piece table, because it is very simple and suits my use case well).
Layout and colouring / syntax highlighting is based on glyph runs. Font and glyph metrics are retrieved using CoreText on macOS and CanvasFontFace (Win2D) on Windows.
Rendering of the layed out glyphs uses CTFontDrawGlyphs on macOS, and Win2D's DrawGlyphRun on Windows.
Here's the entire macOS renderer:
import CoreGraphics
import CoreText
import CompositorCore
func render(run: CodeEditor.GlyphRun, into context: CGContext, fontManager: MacFontManager, metrics: GlyphMetrics, documentBounds: Rect) {
guard let ctFont = fontManager.coreTextFont(fontHandle: run.fontHandle) else {
return
}
if let backgroundColor = run.backgroundColor {
let runOrigin = Point(run.origins[0])
let runRect = Rect(x: runOrigin.x,
y: runOrigin.y - metrics.ascent,
width: Float(run.length) * metrics.advance,
height: metrics.lineHeight)
.flippedVertically(in: documentBounds)
context.setFillColor(backgroundColor.cgColor)
context.fill(runRect.platformRect)
}
let origins = (0..<run.length).map { index in
CGPoint(Point(run.origins[index]).flippedVertically(in: documentBounds))
}
context.setFillColor(run.color.cgColor)
origins.withUnsafeBufferPointer { buffer in
guard let baseAddress = buffer.baseAddress else {
return
}
CTFontDrawGlyphs(ctFont, run.glyphs, baseAddress, run.length, context)
}
}
and this is the entire renderer on Windows:
import UWP
import Win2D
import WindowsFoundation
import CompositorCore
import WinSupport
func render(run: CodeEditor.GlyphRun, into drawingSession: CanvasDrawingSession, font: CodeEditor.Font, fontManager: WindowsFontManager) {
guard let windowsFont = fontManager.font(fontHandle: run.fontHandle) else {
return
}
if let backgroundColor = run.backgroundColor {
let backgroundRect = WindowsFoundation.Rect(x: run.origins[0].x,
y: run.origins[0].y - font.metrics.ascent,
width: Float(run.length) * font.metrics.advance,
height: font.metrics.lineHeight)
try? drawingSession.fillRectangle(backgroundRect, backgroundColor.uwpColor)
}
let origin = Vector2(x: run.origins[0].x, y: run.origins[0].y)
let glyphs: [CanvasGlyph] = (0..<run.length).map { index in
let glyphIndexInFont = run.glyphs[index]
let advance = index < run.length - 1 ? run.origins[index + 1].x - run.origins[index].x : 0.1
let ascenderOffset: Float = 0.0 // run.origins[index].y - run.origins[0].y //: 0
return CanvasGlyph(index: glyphIndexInFont, advance: advance, advanceOffset: 0, ascenderOffset: ascenderOffset)
}
let brush = CanvasSolidColorBrush(drawingSession.device, run.color.uwpColor)
do {
try drawingSession.drawGlyphRun(
origin,
windowsFont.fontFace,
font.size,
glyphs,
false,
0,
brush)
} catch let error {
logDebug("\(error)")
}
}
Text selection is also entirely custom and based on querying the layout engine for glyph hits.