Implement Math view
This commit is contained in:
@@ -13,7 +13,7 @@ extension Math {
|
||||
var localTextColor: CGColor?
|
||||
var localBackgroundColor: CGColor?
|
||||
|
||||
func bounds() -> CGRect {
|
||||
var bounds: CGRect {
|
||||
CGRect(x: position.x, y: position.y - descent, width: width, height: ascent + descent)
|
||||
}
|
||||
}
|
||||
|
||||
118
Sources/SwiftUIMath/Internal/Display/DisplayProvider.swift
Normal file
118
Sources/SwiftUIMath/Internal/Display/DisplayProvider.swift
Normal file
@@ -0,0 +1,118 @@
|
||||
import Foundation
|
||||
|
||||
extension Math {
|
||||
final class DisplayProvider: Sendable {
|
||||
private struct Cache {
|
||||
struct Key: Hashable {
|
||||
let latex: String
|
||||
let font: Font
|
||||
let style: TypesettingStyle
|
||||
let proposedWidth: CGFloat
|
||||
}
|
||||
|
||||
let atomList = NSCache<NSString, AtomList>()
|
||||
let displayNode = NSCache<KeyBox<Key>, DisplayNode>()
|
||||
}
|
||||
|
||||
static let shared = DisplayProvider()
|
||||
|
||||
private let cache = ReadWriteLockIsolated<Cache>(Cache())
|
||||
|
||||
func sizeThatFits(
|
||||
proposedWidth width: CGFloat,
|
||||
latex: String,
|
||||
font: Font,
|
||||
style: TypesettingStyle
|
||||
) -> CGSize {
|
||||
display(
|
||||
for: latex,
|
||||
font: font,
|
||||
style: style,
|
||||
proposedWidth: width
|
||||
)?.bounds.size ?? .zero
|
||||
}
|
||||
|
||||
func display(
|
||||
for latex: String,
|
||||
font: Font,
|
||||
style: TypesettingStyle,
|
||||
proposedWidth: CGFloat
|
||||
) -> DisplayNode? {
|
||||
cache.withValue { cache in
|
||||
let roundedWidth = proposedWidth.halfPointRounded()
|
||||
let key = KeyBox(
|
||||
Cache.Key(
|
||||
latex: latex,
|
||||
font: font,
|
||||
style: style,
|
||||
proposedWidth: roundedWidth
|
||||
)
|
||||
)
|
||||
|
||||
if let displayNode = cache.displayNode.object(forKey: key) {
|
||||
return displayNode
|
||||
}
|
||||
|
||||
guard
|
||||
let atomList = atomList(for: latex, cache: &cache),
|
||||
let displayNode = Typesetter.createLineForMathList(
|
||||
atomList,
|
||||
font: .init(font: font),
|
||||
style: .init(style),
|
||||
maxWidth: roundedWidth
|
||||
)
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
cache.displayNode.setObject(displayNode, forKey: key)
|
||||
|
||||
if displayNode.width != roundedWidth {
|
||||
// Cache the measured width to avoid a miss between layout and draw passes
|
||||
let secondaryKey = KeyBox(
|
||||
Cache.Key(
|
||||
latex: latex,
|
||||
font: font,
|
||||
style: style,
|
||||
proposedWidth: displayNode.width.halfPointRounded()
|
||||
)
|
||||
)
|
||||
cache.displayNode.setObject(displayNode, forKey: secondaryKey)
|
||||
}
|
||||
|
||||
return displayNode
|
||||
}
|
||||
}
|
||||
|
||||
private func atomList(for latex: String, cache: inout Cache) -> AtomList? {
|
||||
if let atomList = cache.atomList.object(forKey: latex as NSString) {
|
||||
return atomList
|
||||
}
|
||||
|
||||
guard let atomList = Parser.build(fromString: latex) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
cache.atomList.setObject(atomList, forKey: latex as NSString)
|
||||
return atomList
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Math.Style.Level {
|
||||
fileprivate init(_ typesettingStyle: Math.TypesettingStyle) {
|
||||
switch typesettingStyle {
|
||||
case .display:
|
||||
self = .display
|
||||
case .text:
|
||||
self = .text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension CGFloat {
|
||||
fileprivate func halfPointRounded() -> CGFloat {
|
||||
guard self > 0 else { return 0 }
|
||||
return (self * 2).rounded() / 2
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ extension GraphicsContext {
|
||||
|
||||
context.translateBy(x: 0, y: size.height)
|
||||
context.scaleBy(x: 1, y: -1)
|
||||
context.translateBy(x: 0, y: displayNode.descent)
|
||||
|
||||
let foregroundColor = foregroundColor.resolve(in: environment).cgColor
|
||||
|
||||
|
||||
@@ -1793,6 +1793,10 @@ extension Math {
|
||||
currentLine.addAttribute(
|
||||
kCTFontAttributeName as NSAttributedString.Key, value: styleFont.ctFont as Any,
|
||||
range: NSMakeRange(0, currentLine.length))
|
||||
currentLine.addAttribute(
|
||||
NSAttributedString.Key(kCTForegroundColorFromContextAttributeName as String),
|
||||
value: true,
|
||||
range: NSMakeRange(0, currentLine.length))
|
||||
/*assert(currentLineIndexRange.length == numCodePoints(currentLine.string),
|
||||
"The length of the current line: %@ does not match the length of the range (%d, %d)",
|
||||
currentLine, currentLineIndexRange.location, currentLineIndexRange.length);*/
|
||||
@@ -2410,6 +2414,10 @@ extension Math {
|
||||
line.addAttribute(
|
||||
kCTFontAttributeName as NSAttributedString.Key, value: styleFont.ctFont,
|
||||
range: NSMakeRange(0, line.length))
|
||||
line.addAttribute(
|
||||
NSAttributedString.Key(kCTForegroundColorFromContextAttributeName as String),
|
||||
value: true,
|
||||
range: NSMakeRange(0, line.length))
|
||||
let attributedString = line.copy() as! NSAttributedString
|
||||
let ctLine = CTLineCreateWithAttributedString(attributedString)
|
||||
let displayAtom = DisplayTextRun(
|
||||
|
||||
Reference in New Issue
Block a user