Pad/internal/ui/shaper.go
Greg Pomerantz 93a7a0a0b2 fix: remove duplicate scale storage, read from State via ScaleProvider interface
- Remove Renderer.scale field and SetScale method
- Add ScaleProvider interface to avoid editor/ui import cycle
- Add Scale() method to State, rename Scale field to scale
- Renderer reads scale through r.scale.Scale()
- Fix icons.go blank import preventing embed.FS usage
- Update main.go to pass State as ScaleProvider to Renderer
2026-05-17 10:59:29 -04:00

94 lines
2.2 KiB
Go

package ui
import (
"image/color"
"gioui.org/f32"
"gioui.org/layout"
"gioui.org/op"
"gioui.org/op/clip"
"gioui.org/op/paint"
"gioui.org/text"
"gioui.org/unit"
"golang.org/x/image/math/fixed"
)
// ShapedText holds the result from ShapeText: width for positioning,
// plus the path data ready to draw. Must be drawn before the next ShapeText call.
type ShapedText struct {
Width int // total width in pixels
PathSpec clip.PathSpec // glyph path data
Bitmaps op.CallOp // bitmap glyphs (may be empty)
FirstX fixed.Int26_6 // first glyph X offset
FirstY int32 // first glyph Y offset
}
// ShapeText layouts text and returns width + path data for drawing.
// The returned ShapedText must be drawn before the next ShapeText call.
func (r *Renderer) ShapeText(gtx layout.Context, str string, size unit.Sp) ShapedText {
r.shp.LayoutString(text.Parameters{
PxPerEm: fixed.I(gtx.Sp(size)),
MinWidth: 0,
MaxWidth: 1000,
MaxLines: 1,
}, str)
// Collect glyphs and measure width
var glyphs []text.Glyph
var cumWidth fixed.Int26_6
var firstX fixed.Int26_6
var firstY int32
for {
g, ok := r.shp.NextGlyph()
if !ok {
break
}
if len(glyphs) == 0 {
firstX = g.X
firstY = g.Y
}
cumWidth += g.Advance
glyphs = append(glyphs, g)
}
// capture path spec
var spec clip.PathSpec
var bitmaps op.CallOp
spec = r.shp.Shape(glyphs)
bitmaps = r.shp.Bitmaps(glyphs)
return ShapedText{
Width: int(cumWidth >> 6),
PathSpec: spec,
Bitmaps: bitmaps,
FirstX: firstX,
FirstY: firstY,
}
}
// DrawShapedText draws a ShapedText at the given position.
func (r *Renderer) DrawShapedText(gtx layout.Context, st ShapedText, x, y Dp, col color.NRGBA) {
xPx := r.toPx(x)
yPx := r.toPx(y)
m := op.Record(gtx.Ops)
offX := float32(xPx) + float32(st.FirstX)/64.0
offY := float32(yPx) + float32(st.FirstY)
t := op.Affine(f32.Affine2D{}.Offset(f32.Pt(offX, offY))).Push(gtx.Ops)
outline := clip.Outline{Path: st.PathSpec}.Op().Push(gtx.Ops)
paint.ColorOp{Color: col}.Add(gtx.Ops)
paint.PaintOp{}.Add(gtx.Ops)
outline.Pop()
if st.Bitmaps != (op.CallOp{}) {
st.Bitmaps.Add(gtx.Ops)
}
t.Pop()
call := m.Stop()
call.Add(gtx.Ops)
}