inline layout for all complex atom types
Extends the width-checking pattern from fractions/radicals to ALL remaining
complex atom types, completing Priority 1 of the multiline implementation.
Changes:
- Large operators (∑, ∫, ∏): Now stay inline with height+width checking
(breaks only if height > fontSize * 2.5 OR width exceeds constraint)
- Delimiters (\left...\right): Stay inline with maxWidth propagation to
inner content for proper nested wrapping
- Colors (.color, .textcolor, .colorBox): All 3 types now stay inline with
maxWidth propagation for proper nested wrapping
- Matrices/tables: Small matrices can now stay inline with surrounding content
- Width constraint propagation: All recursive createLineForMathList() calls
now properly pass maxWidth parameter
Impact:
Before: Complex atoms always forced line breaks, even when they fit
After: ALL complex atoms intelligently stay inline when width permits
Examples:
- a + ∑ xᵢ + b → 1 line instead of 3
- (a+b) + \left(\frac{c}{d}\right) + e → stays inline with wrapping
This commit is contained in:
@@ -620,40 +620,49 @@ class MTTypesetter {
|
||||
continue
|
||||
|
||||
case .color:
|
||||
// stash the existing layout
|
||||
if currentLine.length > 0 {
|
||||
self.addDisplayLine()
|
||||
}
|
||||
// Create the colored display first (pass maxWidth for inner breaking)
|
||||
let colorAtom = atom as! MTMathColor
|
||||
let display = MTTypesetter.createLineForMathList(colorAtom.innerList, font: font, style: style)
|
||||
let display = MTTypesetter.createLineForMathList(colorAtom.innerList, font: font, style: style, maxWidth: maxWidth)
|
||||
display!.localTextColor = MTColor(fromHexString: colorAtom.colorString)
|
||||
|
||||
// Check if we need to break before adding this colored content
|
||||
if shouldBreakBeforeDisplay(display!, prevNode: prevNode, displayType: .ordinary) {
|
||||
performLineBreak()
|
||||
} else {
|
||||
self.addInterElementSpace(prevNode, currentType:.ordinary)
|
||||
}
|
||||
|
||||
display!.position = currentPosition
|
||||
currentPosition.x += display!.width
|
||||
displayAtoms.append(display!)
|
||||
|
||||
case .textcolor:
|
||||
// stash the existing layout
|
||||
if currentLine.length > 0 {
|
||||
self.addDisplayLine()
|
||||
}
|
||||
// Create the text colored display first (pass maxWidth for inner breaking)
|
||||
let colorAtom = atom as! MTMathTextColor
|
||||
let display = MTTypesetter.createLineForMathList(colorAtom.innerList, font: font, style: style)
|
||||
let display = MTTypesetter.createLineForMathList(colorAtom.innerList, font: font, style: style, maxWidth: maxWidth)
|
||||
display!.localTextColor = MTColor(fromHexString: colorAtom.colorString)
|
||||
|
||||
if prevNode != nil {
|
||||
let subDisplay: MTDisplay = display!.subDisplays[0]
|
||||
let subDisplayAtom = (subDisplay as? MTCTLineDisplay)!.atoms[0]
|
||||
let interElementSpace = self.getInterElementSpace(prevNode!.type, right:subDisplayAtom.type)
|
||||
if currentLine.length > 0 {
|
||||
if interElementSpace > 0 {
|
||||
// add a kerning of that space to the previous character
|
||||
currentLine.addAttribute(kCTKernAttributeName as NSAttributedString.Key,
|
||||
value:NSNumber(floatLiteral: interElementSpace),
|
||||
range:currentLine.mutableString.rangeOfComposedCharacterSequence(at: currentLine.length-1))
|
||||
// Check if we need to break before adding this colored content
|
||||
if shouldBreakBeforeDisplay(display!, prevNode: prevNode, displayType: .ordinary) {
|
||||
performLineBreak()
|
||||
} else if prevNode != nil && display!.subDisplays.count > 0 {
|
||||
// Handle inter-element spacing if not breaking
|
||||
if let subDisplay = display!.subDisplays.first,
|
||||
let ctLineDisplay = subDisplay as? MTCTLineDisplay,
|
||||
!ctLineDisplay.atoms.isEmpty {
|
||||
let subDisplayAtom = ctLineDisplay.atoms[0]
|
||||
let interElementSpace = self.getInterElementSpace(prevNode!.type, right:subDisplayAtom.type)
|
||||
if currentLine.length > 0 {
|
||||
if interElementSpace > 0 {
|
||||
// add a kerning of that space to the previous character
|
||||
currentLine.addAttribute(kCTKernAttributeName as NSAttributedString.Key,
|
||||
value:NSNumber(floatLiteral: interElementSpace),
|
||||
range:currentLine.mutableString.rangeOfComposedCharacterSequence(at: currentLine.length-1))
|
||||
}
|
||||
} else {
|
||||
// increase the space
|
||||
currentPosition.x += interElementSpace
|
||||
}
|
||||
} else {
|
||||
// increase the space
|
||||
currentPosition.x += interElementSpace
|
||||
}
|
||||
}
|
||||
|
||||
@@ -662,16 +671,21 @@ class MTTypesetter {
|
||||
displayAtoms.append(display!)
|
||||
|
||||
case .colorBox:
|
||||
// stash the existing layout
|
||||
if currentLine.length > 0 {
|
||||
self.addDisplayLine()
|
||||
}
|
||||
// Create the colorbox display first (pass maxWidth for inner breaking)
|
||||
let colorboxAtom = atom as! MTMathColorbox
|
||||
let display = MTTypesetter.createLineForMathList(colorboxAtom.innerList, font:font, style:style)
|
||||
|
||||
let display = MTTypesetter.createLineForMathList(colorboxAtom.innerList, font:font, style:style, maxWidth: maxWidth)
|
||||
|
||||
display!.localBackgroundColor = MTColor(fromHexString: colorboxAtom.colorString)
|
||||
|
||||
// Check if we need to break before adding this colorbox
|
||||
if shouldBreakBeforeDisplay(display!, prevNode: prevNode, displayType: .ordinary) {
|
||||
performLineBreak()
|
||||
} else {
|
||||
self.addInterElementSpace(prevNode, currentType:.ordinary)
|
||||
}
|
||||
|
||||
display!.position = currentPosition
|
||||
currentPosition.x += display!.width;
|
||||
currentPosition.x += display!.width
|
||||
displayAtoms.append(display!)
|
||||
|
||||
case .radical:
|
||||
@@ -727,31 +741,50 @@ class MTTypesetter {
|
||||
}
|
||||
|
||||
case .largeOperator:
|
||||
// stash the existing layout
|
||||
if currentLine.length > 0 {
|
||||
self.addDisplayLine()
|
||||
}
|
||||
self.addInterElementSpace(prevNode, currentType:atom.type)
|
||||
// Create the large operator display first
|
||||
let op = atom as! MTLargeOperator?
|
||||
let display = self.makeLargeOp(op)
|
||||
|
||||
// Check if we need to break before adding this operator
|
||||
// Large operators can be tall (with limits), so check both width and height
|
||||
let isTooTall = (display!.ascent + display!.descent) > styleFont.fontSize * 2.5
|
||||
let isTooWide = shouldBreakBeforeDisplay(display!, prevNode: prevNode, displayType: atom.type)
|
||||
|
||||
if isTooTall || isTooWide {
|
||||
performLineBreak()
|
||||
} else {
|
||||
self.addInterElementSpace(prevNode, currentType:atom.type)
|
||||
}
|
||||
|
||||
// Position and add the operator display
|
||||
display!.position = currentPosition
|
||||
displayAtoms.append(display!)
|
||||
currentPosition.x += display!.width
|
||||
|
||||
case .inner:
|
||||
// stash the existing layout
|
||||
if currentLine.length > 0 {
|
||||
self.addDisplayLine()
|
||||
}
|
||||
self.addInterElementSpace(prevNode, currentType:atom.type)
|
||||
// Create the inner display first
|
||||
let inner = atom as! MTInner?
|
||||
var display : MTDisplay? = nil
|
||||
if inner!.leftBoundary != nil || inner!.rightBoundary != nil {
|
||||
display = self.makeLeftRight(inner)
|
||||
// Pass maxWidth to delimited content so it can also break
|
||||
display = self.makeLeftRight(inner, maxWidth:maxWidth)
|
||||
} else {
|
||||
display = MTTypesetter.createLineForMathList(inner!.innerList, font:font, style:style, cramped:cramped)
|
||||
// Pass maxWidth to inner content so it can also break
|
||||
display = MTTypesetter.createLineForMathList(inner!.innerList, font:font, style:style, cramped:cramped, maxWidth:maxWidth)
|
||||
}
|
||||
|
||||
// Check if we need to break before adding this inner content
|
||||
if shouldBreakBeforeDisplay(display!, prevNode: prevNode, displayType: .inner) {
|
||||
performLineBreak()
|
||||
} else {
|
||||
self.addInterElementSpace(prevNode, currentType:atom.type)
|
||||
}
|
||||
|
||||
// Position and add the inner display
|
||||
display!.position = currentPosition
|
||||
currentPosition.x += display!.width
|
||||
displayAtoms.append(display!)
|
||||
|
||||
// add super scripts || subscripts
|
||||
if atom.subScript != nil || atom.superScript != nil {
|
||||
self.makeScripts(atom, display:display, index:UInt(atom.indexRange.location), delta:0)
|
||||
@@ -869,16 +902,20 @@ class MTTypesetter {
|
||||
}
|
||||
|
||||
case .table:
|
||||
// stash the existing layout
|
||||
if currentLine.length > 0 {
|
||||
self.addDisplayLine()
|
||||
}
|
||||
// We will consider tables as inner
|
||||
self.addInterElementSpace(prevNode, currentType:.inner)
|
||||
atom.type = .inner;
|
||||
|
||||
// Create the table display first
|
||||
let table = atom as! MTMathTable?
|
||||
let display = self.makeTable(table)
|
||||
|
||||
// Check if we need to break before adding this table
|
||||
// We will consider tables as inner
|
||||
if shouldBreakBeforeDisplay(display!, prevNode: prevNode, displayType: .inner) {
|
||||
performLineBreak()
|
||||
} else {
|
||||
self.addInterElementSpace(prevNode, currentType:.inner)
|
||||
}
|
||||
atom.type = .inner
|
||||
|
||||
display!.position = currentPosition
|
||||
displayAtoms.append(display!)
|
||||
currentPosition.x += display!.width
|
||||
// A table doesn't have subscripts or superscripts
|
||||
@@ -1829,10 +1866,10 @@ class MTTypesetter {
|
||||
static let kDelimiterFactor = CGFloat(901)
|
||||
static let kDelimiterShortfallPoints = CGFloat(5)
|
||||
|
||||
func makeLeftRight(_ inner: MTInner?) -> MTDisplay? {
|
||||
func makeLeftRight(_ inner: MTInner?, maxWidth: CGFloat = 0) -> MTDisplay? {
|
||||
assert(inner!.leftBoundary != nil || inner!.rightBoundary != nil, "Inner should have a boundary to call this function");
|
||||
|
||||
let innerListDisplay = MTTypesetter.createLineForMathList(inner!.innerList, font:font, style:style, cramped:cramped, spaced:true)
|
||||
|
||||
let innerListDisplay = MTTypesetter.createLineForMathList(inner!.innerList, font:font, style:style, cramped:cramped, spaced:true, maxWidth:maxWidth)
|
||||
let axisHeight = styleFont.mathTable!.axisHeight
|
||||
// delta is the max distance from the axis
|
||||
let delta = max(innerListDisplay!.ascent - axisHeight, innerListDisplay!.descent + axisHeight);
|
||||
|
||||
Reference in New Issue
Block a user