Clean up debug statements, restore proper icons, document multi-line text spacing
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
parent
f67cca1f4b
commit
9fd6239129
|
|
@ -476,12 +476,110 @@ Look for:
|
|||
- **Text off-screen**: Wrong offset calculation
|
||||
- **Text clipped**: Clip region doesn't include text bounds
|
||||
|
||||
## 9. Multi-line Text Spacing
|
||||
|
||||
When laying out multiple lines of text at different positions, you need to understand how Gio calculates baseline spacing.
|
||||
|
||||
### 9.1 Gio's Line Height Calculation
|
||||
|
||||
In `gio/text/gotext.go` (`calculateYOffsets` and `LayoutRunes`):
|
||||
|
||||
```go
|
||||
// First line baseline starts at the ascent height
|
||||
currentY := lines[0].ascent.Ceil()
|
||||
|
||||
// Subsequent baselines are spaced by lineHeight
|
||||
for i := range lines {
|
||||
if i > 0 {
|
||||
currentY += lines[i].lineHeight.Round()
|
||||
}
|
||||
lines[i].yOffset = currentY
|
||||
}
|
||||
|
||||
// lineHeight = max(ascent + descent) * LineHeightScale
|
||||
// Default LineHeightScale = 1.2
|
||||
if params.LineHeight != 0 {
|
||||
maxHeight = params.LineHeight
|
||||
}
|
||||
if params.LineHeightScale == 0 {
|
||||
params.LineHeightScale = 1.2
|
||||
}
|
||||
maxHeight = floatToFixed(fixedToFloat(maxHeight) * params.LineHeightScale)
|
||||
```
|
||||
|
||||
**Key points**:
|
||||
- **First line baseline**: `ascent.Ceil()` (not 0)
|
||||
- **Baseline-to-baseline spacing**: `lineHeight * LineHeightScale`
|
||||
- **Default LineHeightScale**: 1.2 (adds 20% extra padding)
|
||||
- **LineHeightScale 1.0**: tightest spacing, no extra padding
|
||||
|
||||
### 9.2 Positioning Multiple Lines
|
||||
|
||||
When drawing multiple lines at different positions (e.g., filename + icons in StatusBar), use the baseline spacing as your guide:
|
||||
|
||||
```go
|
||||
// Line 1: Filename at origin
|
||||
filenameLineY := reg.Y
|
||||
|
||||
// Line 2: Icons — baseline spacing ≈ font size with LineHeightScale=1.0
|
||||
// For 16SP font: baseline-to-baseline ≈ 20DP
|
||||
iconsLineY := filenameLineY + unit.Dp(20)
|
||||
```
|
||||
|
||||
**Why 20DP?** For a 16SP font on a typical display:
|
||||
- `PxPerEm = 16 * 1.25 = 20` device pixels (2x display)
|
||||
- `ascent + descent ≈ PxPerEm = 20`
|
||||
- With `LineHeightScale = 1.0`: baseline spacing = 20DP
|
||||
|
||||
### 9.3 Controlling Spacing
|
||||
|
||||
You have two options to control line spacing:
|
||||
|
||||
**Option A: Set `LineHeightScale` in LayoutString**
|
||||
```go
|
||||
shp.LayoutString(text.Parameters{
|
||||
PxPerEm: fixed.I(gtx.Sp(size)),
|
||||
MinWidth: 0,
|
||||
MaxWidth: availableWidth,
|
||||
MaxLines: 1,
|
||||
LineHeightScale: 1.0, // Tight spacing, no extra padding
|
||||
}, str)
|
||||
```
|
||||
|
||||
**Option B: Set `LineHeight` to a specific value**
|
||||
```go
|
||||
shp.LayoutString(text.Parameters{
|
||||
PxPerEm: fixed.I(gtx.Sp(size)),
|
||||
MinWidth: 0,
|
||||
MaxWidth: availableWidth,
|
||||
MaxLines: 1,
|
||||
LineHeight: fixed.I(gtx.Sp(18)), // Fixed 18SP baseline spacing
|
||||
}, str)
|
||||
```
|
||||
|
||||
### 9.4 Common Mistakes
|
||||
|
||||
**Mistake**: Adding arbitrary spacing between lines without considering baseline positioning.
|
||||
|
||||
**Fix**: Remember that the shaper's Y value is the **baseline**, not the top of the text. The visual top of the text is at `baseline - ascent`. So the visual gap between two lines is:
|
||||
|
||||
```
|
||||
visualGap = (line2Baseline - line1Baseline) - (ascent1 + ascent2)
|
||||
= lineHeight - (ascent1 + ascent2)
|
||||
```
|
||||
|
||||
With `LineHeightScale = 1.0`: `visualGap ≈ 0` (lines touch)
|
||||
With `LineHeightScale = 1.2`: `visualGap ≈ 0.2 * lineHeight` (20% padding)
|
||||
|
||||
## 10. Summary
|
||||
|
||||
- **Always** use `fixed.I(gtx.Sp(size))` for `PxPerEm`
|
||||
- **Always** set `MinWidth`, `MaxWidth`, `MaxLines` in `LayoutString` parameters
|
||||
- **Always** offset by `(x + first.X, y + first.Y)` in `drawLine`
|
||||
- **Always** wrap glyph drawing in `op.Record`/`m.Stop()` for clipping
|
||||
- **Baseline spacing** = `lineHeight * LineHeightScale` (default 1.2)
|
||||
- **For tight multi-line layouts**, use `LineHeightScale: 1.0` or set explicit `LineHeight`
|
||||
- **Visual gap** between lines = `baselineSpacing - (ascent1 + ascent2)`
|
||||
- **Avoid** using `material.Label()` when you need precise glyph positions
|
||||
- **Reuse** glyph data for measurement, hit testing, and rendering
|
||||
- **Follow** Gio's `paintGlyph` as the reference implementation
|
||||
|
|
|
|||
|
|
@ -90,7 +90,6 @@ func (r *Renderer) drawStatusBar(gtx layout.Context, sb StatusBar) {
|
|||
|
||||
// Line 1: Filename (truncated with ellipsis if needed)
|
||||
filenameLineY := reg.Y
|
||||
filenameLineH := unit.Dp(24)
|
||||
|
||||
// Truncate filename only if it doesn't fit
|
||||
displayFilename := sb.Filename
|
||||
|
|
@ -117,8 +116,9 @@ func (r *Renderer) drawStatusBar(gtx layout.Context, sb StatusBar) {
|
|||
// Layout filename, measure widths, and draw in a single pass
|
||||
r.drawTruncatedText(gtx, th.Shaper, displayFilename, r.theme.FontSize, reg.X+unit.Dp(8), filenameLineY, availableWidth, spaceForText, color.NRGBA{R: 0, G: 0, B: 0, A: 255})
|
||||
|
||||
// Line 2: Icons
|
||||
iconsLineY := filenameLineY + filenameLineH
|
||||
// Line 2: Icons — positioned 20DP below filename baseline.
|
||||
// This matches Gio's default line spacing (ascent+descent with LineHeightScale=1.0).
|
||||
iconsLineY := filenameLineY + unit.Dp(20)
|
||||
|
||||
// Left side: Cut, Copy, Paste icons
|
||||
leftX := reg.X + unit.Dp(8)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user