zntg/ui/paragraph.go
Sander Schobers de8ce3e7bc Added text overflow to label.
Generalised text fitting (to width) and implemented binary search.
2021-07-19 17:55:10 +02:00

98 lines
2.4 KiB
Go

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
}
}