diff --git a/doc/shaper_usage.md b/doc/shaper_usage.md index 08975c6..4c44db8 100644 --- a/doc/shaper_usage.md +++ b/doc/shaper_usage.md @@ -159,34 +159,52 @@ func charWidths(gtx layout.Context, shp *text.Shaper, str string, size unit.Sp) ## 5. Rendering Glyphs Directly -Instead of using `material.Label()` (which shapes text again), draw glyphs directly: +Gio's textView draws glyphs using `shaper.Shape()` (for vector glyphs) and `shaper.Bitmaps()` (for bitmap glyphs like emoji). This is the correct approach to avoid double-shaping. -### 5.1 Drawing a Single Glyph +### 5.1 Drawing Text Using Gio's textView Approach ```go -// After layout, iterate through glyphs -for { - g, ok := shp.NextGlyph() - if !ok { - break +func drawText(gtx layout.Context, shp *text.Shaper, str string, size unit.Sp, x, y unit.Dp, col color.NRGBA) { + // Layout text + shp.LayoutString(text.Parameters{PxPerEm: fixed.I(gtx.Sp(size))}, str) + + // Draw glyphs using the same approach as Gio's textView + var glyphs [32]text.Glyph + line := glyphs[:0] + for g, ok := shp.NextGlyph(); ok; g, ok = shp.NextGlyph() { + line = append(line, g) + if g.Flags&text.FlagLineBreak != 0 || cap(line)-len(line) == 0 { + drawLine(gtx, shp, line, x, y, col) + line = line[:0] + } } - // Draw glyph at position (g.X, g.Y) - // Use shp.DrawGlyph() or manual drawing + if len(line) > 0 { + drawLine(gtx, shp, line, x, y, col) + } +} + +func drawLine(gtx layout.Context, shp *text.Shaper, line []text.Glyph, x, y unit.Dp, col color.NRGBA) { + // Apply offset transform + off := f32.Point{X: float32(x), Y: float32(y)} + t := op.Affine(f32.Affine2D{}.Offset(off)).Push(gtx.Ops) + + // Draw vector glyphs + path := shp.Shape(line) + outline := clip.Outline{Path: path}.Op().Push(gtx.Ops) + paint.ColorOp{Color: col}.Add(gtx.Ops) + paint.PaintOp{}.Add(gtx.Ops) + outline.Pop() + + // Draw bitmap glyphs (emoji, etc.) + if call := shp.Bitmaps(line); call != (op.CallOp{}) { + call.Add(gtx.Ops) + } + + t.Pop() } ``` -### 5.2 Using Shaper's Drawing Functions - -The shaper provides drawing functions that use the already-computed glyph data: - -```go -// Draw all laid-out glyphs -shp.Draw(gtx, ops, color.NRGBA{R: 0, G: 0, B: 0, A: 255}) -``` - -**Important**: Call `shp.Draw()` AFTER calling `LayoutString()` or `Layout()`. The drawing functions use the glyph data computed during the layout phase. - -### 5.3 Avoiding Double-Shaping +### 5.2 Avoiding Double-Shaping **WRONG** (shapes text twice): @@ -198,14 +216,19 @@ widths := charWidths(gtx, shp, text, size) material.Label(th, size, text).Layout(gtx) ``` -**CORRECT** (shapes text once): +**CORRECT** (shapes text once for rendering): ```go -// Single pass: layout and draw -shp.LayoutString(text.Parameters{PxPerEm: fixed.I(gtx.Sp(size))}, text) -shp.Draw(gtx, ops, color.NRGBA{R: 0, G: 0, B: 0, A: 255}) +// Single pass: layout and draw using shaper.Shape() +drawText(gtx, shp, text, size, x, y, col) ``` +**NOTE**: For truncation, we may need to layout twice: +1. First layout to measure widths and find truncation point +2. Second layout to draw the truncated text + +This is acceptable because truncation is only needed when text doesn't fit, and the string is short (filename in StatusBar). + ## 6. Comparison with Gio's textView Gio's textView (`widget/text.go`) demonstrates the correct approach: