[UI] add line wrapping functionality

This commit is contained in:
Nicolas Guillot
2025-10-02 12:05:37 +02:00
parent 11f57f7c6e
commit c7198ad9af
4 changed files with 567 additions and 39 deletions

130
README.md
View File

@@ -114,18 +114,34 @@ struct MathView: UIViewRepresentable {
var fontSize: CGFloat = 30
var labelMode: MTMathUILabelMode = .text
var insets: MTEdgeInsets = MTEdgeInsets()
func makeUIView(context: Context) -> MTMathUILabel {
let view = MTMathUILabel()
view.setContentHuggingPriority(.required, for: .vertical)
view.setContentCompressionResistancePriority(.required, for: .vertical)
return view
}
func updateUIView(_ view: MTMathUILabel, context: Context) {
view.latex = equation
view.font = MTFontManager().font(withName: font.rawValue, size: fontSize)
let font = MTFontManager().font(withName: font.rawValue, size: fontSize)
font?.fallbackFont = UIFont.systemFont(ofSize: fontSize)
view.font = font
view.textAlignment = textAlignment
view.labelMode = labelMode
view.textColor = MTColor(Color.primary)
view.contentInsets = insets
view.invalidateIntrinsicContentSize()
}
func sizeThatFits(_ proposal: ProposedViewSize, uiView: MTMathUILabel, context: Context) -> CGSize? {
// Enable line wrapping by passing proposed width to the label
if let width = proposal.width, width.isFinite, width > 0 {
uiView.preferredMaxLayoutWidth = width
let size = uiView.sizeThatFits(CGSize(width: width, height: .greatestFiniteMagnitude))
return size
}
return nil
}
}
```
@@ -143,23 +159,127 @@ struct MathView: NSViewRepresentable {
var fontSize: CGFloat = 30
var labelMode: MTMathUILabelMode = .text
var insets: MTEdgeInsets = MTEdgeInsets()
func makeNSView(context: Context) -> MTMathUILabel {
let view = MTMathUILabel()
view.setContentHuggingPriority(.required, for: .vertical)
view.setContentCompressionResistancePriority(.required, for: .vertical)
return view
}
func updateNSView(_ view: MTMathUILabel, context: Context) {
view.latex = equation
view.font = MTFontManager().font(withName: font.rawValue, size: fontSize)
let font = MTFontManager().font(withName: font.rawValue, size: fontSize)
font?.fallbackFont = NSFont.systemFont(ofSize: fontSize)
view.font = font
view.textAlignment = textAlignment
view.labelMode = labelMode
view.textColor = MTColor(Color.primary)
view.contentInsets = insets
view.invalidateIntrinsicContentSize()
}
func sizeThatFits(_ proposal: ProposedViewSize, nsView: MTMathUILabel, context: Context) -> CGSize? {
// Enable line wrapping by passing proposed width to the label
if let width = proposal.width, width.isFinite, width > 0 {
nsView.preferredMaxLayoutWidth = width
let size = nsView.sizeThatFits(CGSize(width: width, height: .greatestFiniteMagnitude))
return size
}
return nil
}
}
```
### Automatic Line Wrapping
`SwiftMath` supports automatic line wrapping for text and simple math expressions. When the content exceeds the available width, it will wrap at word boundaries to fit within the constrained space.
#### Using Line Wrapping with UIKit/AppKit
For direct `MTMathUILabel` usage, set the `preferredMaxLayoutWidth` property:
```swift
let label = MTMathUILabel()
label.latex = "\\(\\text{Remember the conversion: 1 km equals 1000 meters.}\\)"
label.font = MTFontManager.fontManager.defaultFont
label.labelMode = .text
// Enable line wrapping by setting a maximum width
label.preferredMaxLayoutWidth = 300
```
You can also use `sizeThatFits` to calculate the size with a width constraint:
```swift
let constrainedSize = label.sizeThatFits(CGSize(width: 300, height: .greatestFiniteMagnitude))
```
#### Using Line Wrapping with SwiftUI
The `MathView` examples above include `sizeThatFits()` which automatically enables line wrapping when SwiftUI proposes a width constraint. No additional configuration is needed:
```swift
VStack(alignment: .leading, spacing: 8) {
MathView(
equation: "\\(\\text{Remember the conversion: 1 km equals 1000 meters.}\\)",
fontSize: 17,
labelMode: .text
)
}
.frame(maxWidth: 300) // The text will wrap to fit within 300pt
```
#### Line Wrapping Behavior
- **Works for**: Text content (`\text{...}`), mixed text with simple math, and simple equations
- **Breaks at**: Word boundaries (spaces)
- **Preserves**: Complex math layout (fractions, superscripts, matrices remain on single lines)
- **Respects**: Unicode text including CJK characters with proper word boundaries
#### Examples
**Simple text wrapping:**
```swift
// Long text will wrap to multiple lines
label.latex = "\\(\\text{The quadratic formula is used to solve equations of the form } ax^2 + bx + c = 0\\)"
label.preferredMaxLayoutWidth = 250
```
**Simple equation with operators:**
```swift
// Long equations can break between operators if too long
label.latex = "\\(5 + 10 + 15 + 20 + 25 + 30\\)"
label.preferredMaxLayoutWidth = 150
// Will wrap: "5 + 10 + 15 + 20 +"
// "25 + 30"
```
**Mixed text and math:**
```swift
// Text wraps but math expressions stay intact
label.latex = "\\(\\text{Result: } 5 \\times 1000 = 5000 \\text{ meters}\\)"
label.preferredMaxLayoutWidth = 200
// Will wrap at spaces between text and operators
```
**Multiple lines in SwiftUI:**
```swift
ScrollView {
VStack(alignment: .leading, spacing: 12) {
ForEach(steps) { step in
MathView(
equation: step.description,
fontSize: 17,
labelMode: .text
)
}
}
.padding()
}
// Each MathView will automatically wrap based on available width
```
### Included Features
This is a list of formula types that the library currently supports: