package ui import ( "strconv" "strings" "opslag.de/schobers/geom" ) type Paragraph struct { Label width float32 lines CachedValue } func BuildParagraph(text string, fn func(*Paragraph)) *Paragraph { var p = &Paragraph{} p.Text = text if fn != nil { fn(p) } return p } func fastFormatFloat32(f float32) string { return strconv.FormatFloat(float64(f), 'b', 32, 32) } func (p *Paragraph) desiredSize(ctx Context) interface{} { font := p.ActualFont(ctx) pad := p.ActualTextPadding(ctx) lines := p.splitInLines(ctx, p.width-pad.Left-pad.Right) return geom.PtF32(p.width, float32(len(lines))*font.Height()+pad.Top+pad.Bottom) } func (p *Paragraph) hashTextArranged(ctx Context) string { return p.FontName(ctx) + p.Text + fastFormatFloat32(p.Bounds().Dx()) } func (p *Paragraph) hashTextDesired(ctx Context) string { return p.FontName(ctx) + p.Text + fastFormatFloat32(p.width) } func (p *Paragraph) splitInLines(ctx Context, width float32) []string { font := p.ActualFont(ctx) var lines []string for _, line := range strings.Split(p.Text, "\n") { if len(line) == 0 { lines = append(lines, line) continue } for len(line) > 0 { clipped := fitTextWord(font, line, width) lines = append(lines, clipped) line = strings.TrimLeft(line[len(clipped):], " ") } } return lines } func (p *Paragraph) updateLines(ctx Context) interface{} { pad := p.ActualTextPadding(ctx) return p.splitInLines(ctx, p.Bounds().Dx()-pad.Left-pad.Right) } func (p *Paragraph) DesiredSize(ctx Context, size geom.PointF32) geom.PointF32 { // stores the given width, is used when calculating the new desired size (and thus used in the hash method as well) p.width = size.X return p.desired.GetContext(ctx, p.desiredSize, p.hashTextDesired).(geom.PointF32) } func (p *Paragraph) Render(ctx Context) { p.RenderBackground(ctx) pad := p.ActualTextPadding(ctx) width := p.Bounds().Dx() - pad.Left - pad.Right lines := p.lines.GetContext(ctx, p.updateLines, p.hashTextArranged).([]string) fontColor := p.TextColor(ctx) fontName := p.FontName(ctx) fontHeight := ctx.Fonts().Font(fontName).Height() bounds := pad.InsetRect(p.bounds) left := bounds.Min.X switch p.TextAlignment { case AlignRight: left = bounds.Max.X case AlignCenter: left += .5 * width } offset := bounds.Min.Y for _, line := range lines { ctx.Fonts().TextAlign(fontName, geom.PtF32(left, offset), fontColor, line, p.TextAlignment) offset += fontHeight } }