Add Typesetter
This commit is contained in:
@@ -12,7 +12,10 @@ extension Math {
|
|||||||
var lowerLimitGap: CGFloat = 0 { didSet { updateLowerLimitPosition() } }
|
var lowerLimitGap: CGFloat = 0 { didSet { updateLowerLimitPosition() } }
|
||||||
var extraPadding: CGFloat = 0
|
var extraPadding: CGFloat = 0
|
||||||
|
|
||||||
init(nucleus: DisplayNode?, upperLimit: DisplayList?, lowerLimit: DisplayList?, limitShift: CGFloat, extraPadding: CGFloat) {
|
init(
|
||||||
|
nucleus: DisplayNode?, upperLimit: DisplayList?, lowerLimit: DisplayList?,
|
||||||
|
limitShift: CGFloat, extraPadding: CGFloat
|
||||||
|
) {
|
||||||
self.upperLimit = upperLimit
|
self.upperLimit = upperLimit
|
||||||
self.lowerLimit = lowerLimit
|
self.lowerLimit = lowerLimit
|
||||||
self.nucleus = nucleus
|
self.nucleus = nucleus
|
||||||
@@ -29,7 +32,8 @@ extension Math {
|
|||||||
get {
|
get {
|
||||||
guard let nucleus else { return 0 }
|
guard let nucleus else { return 0 }
|
||||||
if let upperLimit {
|
if let upperLimit {
|
||||||
return nucleus.ascent + extraPadding + upperLimit.ascent + upperLimitGap + upperLimit.descent
|
return nucleus.ascent + extraPadding + upperLimit.ascent + upperLimitGap
|
||||||
|
+ upperLimit.descent
|
||||||
}
|
}
|
||||||
return nucleus.ascent
|
return nucleus.ascent
|
||||||
}
|
}
|
||||||
@@ -40,7 +44,8 @@ extension Math {
|
|||||||
get {
|
get {
|
||||||
guard let nucleus else { return 0 }
|
guard let nucleus else { return 0 }
|
||||||
if let lowerLimit {
|
if let lowerLimit {
|
||||||
return nucleus.descent + extraPadding + lowerLimitGap + lowerLimit.descent + lowerLimit.ascent
|
return nucleus.descent + extraPadding + lowerLimitGap + lowerLimit.descent
|
||||||
|
+ lowerLimit.ascent
|
||||||
}
|
}
|
||||||
return nucleus.descent
|
return nucleus.descent
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ extension Math {
|
|||||||
var position: CGPoint = .zero
|
var position: CGPoint = .zero
|
||||||
var range: NSRange = NSRange(location: 0, length: 0)
|
var range: NSRange = NSRange(location: 0, length: 0)
|
||||||
var hasScript: Bool = false
|
var hasScript: Bool = false
|
||||||
|
var textColor: PlatformColor?
|
||||||
|
var localTextColor: PlatformColor?
|
||||||
|
var localBackgroundColor: PlatformColor?
|
||||||
|
|
||||||
func bounds() -> CGRect {
|
func bounds() -> CGRect {
|
||||||
CGRect(x: position.x, y: position.y - descent, width: width, height: ascent + descent)
|
CGRect(x: position.x, y: position.y - descent, width: width, height: ascent + descent)
|
||||||
|
|||||||
@@ -19,6 +19,25 @@ extension Math {
|
|||||||
self.range = range
|
self.range = range
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setDegree(_ degree: DisplayList?, fontMetrics: Math.FontMetrics) {
|
||||||
|
guard let degree else { return }
|
||||||
|
let kernBefore = fontMetrics.radicalKernBeforeDegree
|
||||||
|
let kernAfter = fontMetrics.radicalKernAfterDegree
|
||||||
|
let raise = fontMetrics.radicalDegreeBottomRaisePercent * (ascent - descent)
|
||||||
|
|
||||||
|
self.degree = degree
|
||||||
|
|
||||||
|
var shift = kernBefore + degree.width + kernAfter
|
||||||
|
if shift < 0 {
|
||||||
|
shift = 0
|
||||||
|
}
|
||||||
|
radicalShift = shift
|
||||||
|
|
||||||
|
degree.position = CGPoint(x: position.x + kernBefore, y: position.y + raise)
|
||||||
|
width = shift + (radicalGlyph?.width ?? 0) + (radicand?.width ?? 0)
|
||||||
|
updateRadicandPosition()
|
||||||
|
}
|
||||||
|
|
||||||
override var position: CGPoint {
|
override var position: CGPoint {
|
||||||
didSet { updateRadicandPosition() }
|
didSet { updateRadicandPosition() }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ extension Math {
|
|||||||
var font: Math.Font
|
var font: Math.Font
|
||||||
var atoms: [Math.Atom]
|
var atoms: [Math.Atom]
|
||||||
|
|
||||||
init(text: String, font: Math.Font, position: CGPoint = .zero, range: NSRange, atoms: [Math.Atom]) {
|
init(
|
||||||
|
text: String, font: Math.Font, position: CGPoint = .zero, range: NSRange, atoms: [Math.Atom]
|
||||||
|
) {
|
||||||
self.text = text
|
self.text = text
|
||||||
self.font = font
|
self.font = font
|
||||||
self.atoms = atoms
|
self.atoms = atoms
|
||||||
|
|||||||
2852
Sources/SwiftUIMath/Internal/Display/Typesetter.swift
Normal file
2852
Sources/SwiftUIMath/Internal/Display/Typesetter.swift
Normal file
File diff suppressed because it is too large
Load Diff
@@ -7,3 +7,25 @@ import SwiftUI
|
|||||||
typealias PlatformColor = NSColor
|
typealias PlatformColor = NSColor
|
||||||
typealias PlatformBezierPath = NSBezierPath
|
typealias PlatformBezierPath = NSBezierPath
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
extension PlatformColor {
|
||||||
|
convenience init?(fromHexString hexString: String) {
|
||||||
|
self.init(hexString: hexString)
|
||||||
|
}
|
||||||
|
|
||||||
|
convenience init?(hexString: String) {
|
||||||
|
guard !hexString.isEmpty, hexString.hasPrefix("#") else { return nil }
|
||||||
|
|
||||||
|
var rgbValue = UInt64(0)
|
||||||
|
let scanner = Scanner(string: hexString)
|
||||||
|
scanner.charactersToBeSkipped = CharacterSet(charactersIn: "#")
|
||||||
|
guard scanner.scanHexInt64(&rgbValue) else { return nil }
|
||||||
|
|
||||||
|
self.init(
|
||||||
|
red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0,
|
||||||
|
green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0,
|
||||||
|
blue: CGFloat(rgbValue & 0x0000FF) / 255.0,
|
||||||
|
alpha: 1.0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1189,7 +1189,7 @@ struct ParserTests {
|
|||||||
let str = testCase.0
|
let str = testCase.0
|
||||||
var error: Math.ParserError? = nil
|
var error: Math.ParserError? = nil
|
||||||
let list = Math.Parser.build(fromString: str, error: &error)
|
let list = Math.Parser.build(fromString: str, error: &error)
|
||||||
#expect(list == nil)
|
#expect(list == nil)
|
||||||
#expect(error != nil)
|
#expect(error != nil)
|
||||||
let num = testCase.1
|
let num = testCase.1
|
||||||
#expect(error?.code == num)
|
#expect(error?.code == num)
|
||||||
|
|||||||
Reference in New Issue
Block a user