diff --git a/font/opentype/opentype.go b/font/opentype/opentype.go index e6e0a2d8..e02a89e8 100644 --- a/font/opentype/opentype.go +++ b/font/opentype/opentype.go @@ -85,12 +85,12 @@ func (c *Collection) Font(i int) (*Font, error) { return &Font{font: fnt}, nil } -func (f *Font) Layout(ppem fixed.Int26_6, txt io.Reader, opts text.LayoutOptions) ([]text.Line, error) { +func (f *Font) Layout(ppem fixed.Int26_6, maxWidth int, txt io.Reader) ([]text.Line, error) { glyphs, err := readGlyphs(txt) if err != nil { return nil, err } - return layoutText(&f.buf, ppem, &opentype{Font: f.font, Hinting: font.HintingFull}, glyphs, opts) + return layoutText(&f.buf, ppem, maxWidth, &opentype{Font: f.font, Hinting: font.HintingFull}, glyphs) } func (f *Font) Shape(ppem fixed.Int26_6, str []text.Glyph) op.CallOp { @@ -102,7 +102,7 @@ func (f *Font) Metrics(ppem fixed.Int26_6) font.Metrics { return o.Metrics(&f.buf, ppem) } -func layoutText(sbuf *sfnt.Buffer, ppem fixed.Int26_6, f *opentype, glyphs []text.Glyph, opts text.LayoutOptions) ([]text.Line, error) { +func layoutText(sbuf *sfnt.Buffer, ppem fixed.Int26_6, maxWidth int, f *opentype, glyphs []text.Glyph) ([]text.Line, error) { m := f.Metrics(sbuf, ppem) lineTmpl := text.Line{ Ascent: m.Ascent, @@ -112,7 +112,7 @@ func layoutText(sbuf *sfnt.Buffer, ppem fixed.Int26_6, f *opentype, glyphs []tex Bounds: f.Bounds(sbuf, ppem), } var lines []text.Line - maxDotX := fixed.I(opts.MaxWidth) + maxDotX := fixed.I(maxWidth) type state struct { r rune adv fixed.Int26_6 diff --git a/text/lru.go b/text/lru.go index 3ff9b6d1..85dbf9c3 100644 --- a/text/lru.go +++ b/text/lru.go @@ -30,9 +30,9 @@ type path struct { } type layoutKey struct { - ppem fixed.Int26_6 - str string - opts LayoutOptions + ppem fixed.Int26_6 + maxWidth int + str string } type pathKey struct { diff --git a/text/shaper.go b/text/shaper.go index 3514f94c..99631a2e 100644 --- a/text/shaper.go +++ b/text/shaper.go @@ -9,24 +9,23 @@ import ( "golang.org/x/image/font" "gioui.org/op" - "gioui.org/unit" "golang.org/x/image/math/fixed" ) // Shaper implements layout and shaping of text. type Shaper interface { // Layout a text according to a set of options. - Layout(c unit.Converter, font Font, txt io.Reader, opts LayoutOptions) ([]Line, error) + Layout(font Font, size fixed.Int26_6, maxWidth int, txt io.Reader) ([]Line, error) // Shape a line of text and return a clipping operation for its outline. - Shape(c unit.Converter, font Font, layout []Glyph) op.CallOp + Shape(font Font, size fixed.Int26_6, layout []Glyph) op.CallOp // LayoutString is like Layout, but for strings.. - LayoutString(c unit.Converter, font Font, str string, opts LayoutOptions) []Line + LayoutString(font Font, size fixed.Int26_6, maxWidth int, str string) []Line // ShapeString is like Shape for lines previously laid out by LayoutString. - ShapeString(c unit.Converter, font Font, str string, layout []Glyph) op.CallOp + ShapeString(font Font, size fixed.Int26_6, str string, layout []Glyph) op.CallOp // Metrics returns the font metrics for font. - Metrics(c unit.Converter, font Font) font.Metrics + Metrics(font Font, size fixed.Int26_6) font.Metrics } // FontRegistry implements layout and shaping of text from a set of @@ -53,8 +52,6 @@ func (s *FontRegistry) Register(font Font, tf Face) { s.def = font.Typeface s.faces = make(map[Font]*face) } - // Treat all font sizes equally. - font.Size = unit.Value{} if font.Weight == 0 { font.Weight = Normal } @@ -63,31 +60,29 @@ func (s *FontRegistry) Register(font Font, tf Face) { } } -func (s *FontRegistry) Layout(c unit.Converter, font Font, txt io.Reader, opts LayoutOptions) ([]Line, error) { +func (s *FontRegistry) Layout(font Font, size fixed.Int26_6, maxWidth int, txt io.Reader) ([]Line, error) { tf := s.faceForFont(font) - ppem := fixed.I(c.Px(font.Size)) - return tf.face.Layout(ppem, txt, opts) + return tf.face.Layout(size, maxWidth, txt) } -func (s *FontRegistry) Shape(c unit.Converter, font Font, layout []Glyph) op.CallOp { +func (s *FontRegistry) Shape(font Font, size fixed.Int26_6, layout []Glyph) op.CallOp { tf := s.faceForFont(font) - ppem := fixed.I(c.Px(font.Size)) - return tf.face.Shape(ppem, layout) + return tf.face.Shape(size, layout) } -func (s *FontRegistry) LayoutString(c unit.Converter, font Font, str string, opts LayoutOptions) []Line { +func (s *FontRegistry) LayoutString(font Font, size fixed.Int26_6, maxWidth int, str string) []Line { tf := s.faceForFont(font) - return tf.layout(fixed.I(c.Px(font.Size)), str, opts) + return tf.layout(size, maxWidth, str) } -func (s *FontRegistry) ShapeString(c unit.Converter, font Font, str string, layout []Glyph) op.CallOp { +func (s *FontRegistry) ShapeString(font Font, size fixed.Int26_6, str string, layout []Glyph) op.CallOp { tf := s.faceForFont(font) - return tf.shape(fixed.I(c.Px(font.Size)), str, layout) + return tf.shape(size, str, layout) } -func (s *FontRegistry) Metrics(c unit.Converter, font Font) font.Metrics { +func (s *FontRegistry) Metrics(font Font, size fixed.Int26_6) font.Metrics { tf := s.faceForFont(font) - return tf.metrics(fixed.I(c.Px(font.Size))) + return tf.metrics(size) } func (s *FontRegistry) faceForStyle(font Font) *face { @@ -112,7 +107,6 @@ func (s *FontRegistry) faceForStyle(font Font) *face { } func (s *FontRegistry) faceForFont(font Font) *face { - font.Size = unit.Value{} tf := s.faceForStyle(font) if tf == nil { font.Typeface = s.def @@ -121,19 +115,19 @@ func (s *FontRegistry) faceForFont(font Font) *face { return tf } -func (t *face) layout(ppem fixed.Int26_6, str string, opts LayoutOptions) []Line { +func (t *face) layout(ppem fixed.Int26_6, maxWidth int, str string) []Line { if t == nil { return nil } lk := layoutKey{ - ppem: ppem, - str: str, - opts: opts, + ppem: ppem, + maxWidth: maxWidth, + str: str, } if l, ok := t.layoutCache.Get(lk); ok { return l } - l, _ := t.face.Layout(ppem, strings.NewReader(str), opts) + l, _ := t.face.Layout(ppem, maxWidth, strings.NewReader(str)) t.layoutCache.Put(lk, l) return l } diff --git a/text/text.go b/text/text.go index 2b454670..fdbb25a0 100644 --- a/text/text.go +++ b/text/text.go @@ -6,7 +6,6 @@ import ( "io" "gioui.org/op" - "gioui.org/unit" "golang.org/x/image/font" "golang.org/x/image/math/fixed" ) @@ -32,23 +31,16 @@ type Glyph struct { Advance fixed.Int26_6 } -// LayoutOptions specify the constraints of a text layout. -type LayoutOptions struct { - // MaxWidth is the available width of the layout. - MaxWidth int -} - // Style is the font style. type Style int // Weight is a font weight, in CSS units. type Weight int -// Font specify a particular typeface, style and size. +// Font specify a particular typeface variant, style and weight. type Font struct { Typeface Typeface Variant Variant - Size unit.Value Style Style // Weight is the text weight. If zero, Normal is used instead. Weight Weight @@ -56,7 +48,7 @@ type Font struct { // Face implements text layout and shaping for a particular font. type Face interface { - Layout(ppem fixed.Int26_6, txt io.Reader, opts LayoutOptions) ([]Line, error) + Layout(ppem fixed.Int26_6, maxWidth int, txt io.Reader) ([]Line, error) Shape(ppem fixed.Int26_6, str []Glyph) op.CallOp Metrics(ppem fixed.Int26_6) font.Metrics } diff --git a/widget/editor.go b/widget/editor.go index 341b4cf4..a49a7929 100644 --- a/widget/editor.go +++ b/widget/editor.go @@ -36,6 +36,7 @@ type Editor struct { eventKey int scale int font text.Font + textSize fixed.Int26_6 blinkStart time.Time focused bool rr editBuffer @@ -213,14 +214,16 @@ func (e *Editor) Focus() { } // Layout lays out the editor. -func (e *Editor) Layout(gtx *layout.Context, sh text.Shaper, font text.Font) { +func (e *Editor) Layout(gtx *layout.Context, sh text.Shaper, font text.Font, size unit.Value) { // Flush events from before the previous frame. copy(e.events, e.events[e.prevEvents:]) e.events = e.events[:len(e.events)-e.prevEvents] e.prevEvents = len(e.events) - if e.font != font { + textSize := fixed.I(gtx.Px(size)) + if e.font != font || e.textSize != textSize { e.invalidate() e.font = font + e.textSize = textSize } e.processEvents(gtx) e.layout(gtx, sh) @@ -245,7 +248,7 @@ func (e *Editor) layout(gtx *layout.Context, sh text.Shaper) { } if !e.valid { - e.lines, e.dims = e.layoutText(gtx, sh, e.font) + e.lines, e.dims = e.layoutText(sh) e.valid = true } @@ -277,7 +280,7 @@ func (e *Editor) layout(gtx *layout.Context, sh text.Shaper) { if !ok { break } - path := sh.Shape(gtx, e.font, layout) + path := sh.Shape(e.font, e.textSize, layout) e.shapes = append(e.shapes, line{off, path}) } @@ -430,10 +433,9 @@ func (e *Editor) moveCoord(c unit.Converter, pos image.Point) { e.moveToLine(x, carLine) } -func (e *Editor) layoutText(c unit.Converter, s text.Shaper, font text.Font) ([]text.Line, layout.Dimensions) { +func (e *Editor) layoutText(s text.Shaper) ([]text.Line, layout.Dimensions) { e.rr.Reset() - opts := text.LayoutOptions{MaxWidth: e.maxWidth} - lines, _ := s.Layout(c, font, &e.rr, opts) + lines, _ := s.Layout(e.font, e.textSize, e.maxWidth, &e.rr) dims := linesDimens(lines) for i := 0; i < len(lines)-1; i++ { // To avoid layout flickering while editing, assume a soft newline takes diff --git a/widget/label.go b/widget/label.go index 836d408f..d42538d5 100644 --- a/widget/label.go +++ b/widget/label.go @@ -12,6 +12,7 @@ import ( "gioui.org/op" "gioui.org/op/paint" "gioui.org/text" + "gioui.org/unit" "golang.org/x/image/math/fixed" ) @@ -83,9 +84,10 @@ func (l *lineIterator) Next() (int, int, []text.Glyph, f32.Point, bool) { return 0, 0, nil, f32.Point{}, false } -func (l Label) Layout(gtx *layout.Context, s text.Shaper, font text.Font, txt string) { +func (l Label) Layout(gtx *layout.Context, s text.Shaper, font text.Font, size unit.Value, txt string) { cs := gtx.Constraints - lines := s.LayoutString(gtx, font, txt, text.LayoutOptions{MaxWidth: cs.Width.Max}) + textSize := fixed.I(gtx.Px(size)) + lines := s.LayoutString(font, textSize, cs.Width.Max, txt) if max := l.MaxLines; max > 0 && len(lines) > max { lines = lines[:max] } @@ -109,7 +111,7 @@ func (l Label) Layout(gtx *layout.Context, s text.Shaper, font text.Font, txt st stack.Push(gtx.Ops) op.TransformOp{}.Offset(off).Add(gtx.Ops) str := txt[start:end] - s.ShapeString(gtx, font, str, layout).Add(gtx.Ops) + s.ShapeString(font, textSize, str, layout).Add(gtx.Ops) paint.PaintOp{Rect: lclip}.Add(gtx.Ops) stack.Pop() } diff --git a/widget/material/button.go b/widget/material/button.go index a2c56536..ba9c8e67 100644 --- a/widget/material/button.go +++ b/widget/material/button.go @@ -22,6 +22,7 @@ type Button struct { // Color is the text color. Color color.RGBA Font text.Font + TextSize unit.Value Background color.RGBA CornerRadius unit.Value shaper text.Shaper @@ -40,10 +41,8 @@ func (t *Theme) Button(txt string) Button { Text: txt, Color: rgb(0xffffff), Background: t.Color.Primary, - Font: text.Font{ - Size: t.TextSize.Scale(14.0 / 16.0), - }, - shaper: t.Shaper, + TextSize: t.TextSize.Scale(14.0 / 16.0), + shaper: t.Shaper, } } @@ -83,7 +82,7 @@ func (b Button) Layout(gtx *layout.Context, button *widget.Button) { layout.Center.Layout(gtx, func() { layout.Inset{Top: unit.Dp(10), Bottom: unit.Dp(10), Left: unit.Dp(12), Right: unit.Dp(12)}.Layout(gtx, func() { paint.ColorOp{Color: col}.Add(gtx.Ops) - widget.Label{}.Layout(gtx, b.shaper, b.Font, b.Text) + widget.Label{}.Layout(gtx, b.shaper, b.Font, b.TextSize, b.Text) }) }) pointer.Rect(image.Rectangle{Max: gtx.Dimensions.Size}).Add(gtx.Ops) diff --git a/widget/material/checkable.go b/widget/material/checkable.go index 51309c59..2ee7c7b8 100644 --- a/widget/material/checkable.go +++ b/widget/material/checkable.go @@ -18,6 +18,7 @@ type checkable struct { Label string Color color.RGBA Font text.Font + TextSize unit.Value IconColor color.RGBA Size unit.Value shaper text.Shaper @@ -56,7 +57,7 @@ func (c *checkable) layout(gtx *layout.Context, checked bool) { layout.W.Layout(gtx, func() { layout.UniformInset(unit.Dp(2)).Layout(gtx, func() { paint.ColorOp{Color: c.Color}.Add(gtx.Ops) - widget.Label{}.Layout(gtx, c.shaper, c.Font, c.Label) + widget.Label{}.Layout(gtx, c.shaper, c.Font, c.TextSize, c.Label) }) }) }), diff --git a/widget/material/checkbox.go b/widget/material/checkbox.go index 33014b60..b655d135 100644 --- a/widget/material/checkbox.go +++ b/widget/material/checkbox.go @@ -4,7 +4,6 @@ package material import ( "gioui.org/layout" - "gioui.org/text" "gioui.org/unit" "gioui.org/widget" ) @@ -16,12 +15,10 @@ type CheckBox struct { func (t *Theme) CheckBox(label string) CheckBox { return CheckBox{ checkable{ - Label: label, - Color: t.Color.Text, - IconColor: t.Color.Primary, - Font: text.Font{ - Size: t.TextSize.Scale(14.0 / 16.0), - }, + Label: label, + Color: t.Color.Text, + IconColor: t.Color.Primary, + TextSize: t.TextSize.Scale(14.0 / 16.0), Size: unit.Dp(26), shaper: t.Shaper, checkedStateIcon: t.checkBoxCheckedIcon, diff --git a/widget/material/editor.go b/widget/material/editor.go index 5d12155e..d11ff88a 100644 --- a/widget/material/editor.go +++ b/widget/material/editor.go @@ -9,11 +9,13 @@ import ( "gioui.org/op" "gioui.org/op/paint" "gioui.org/text" + "gioui.org/unit" "gioui.org/widget" ) type Editor struct { - Font text.Font + Font text.Font + TextSize unit.Value // Color is the text color. Color color.RGBA // Hint contains the text displayed when the editor is empty. @@ -26,9 +28,7 @@ type Editor struct { func (t *Theme) Editor(hint string) Editor { return Editor{ - Font: text.Font{ - Size: t.TextSize, - }, + TextSize: t.TextSize, Color: t.Color.Text, shaper: t.Shaper, Hint: hint, @@ -43,7 +43,7 @@ func (e Editor) Layout(gtx *layout.Context, editor *widget.Editor) { macro.Record(gtx.Ops) paint.ColorOp{Color: e.HintColor}.Add(gtx.Ops) tl := widget.Label{Alignment: editor.Alignment} - tl.Layout(gtx, e.shaper, e.Font, e.Hint) + tl.Layout(gtx, e.shaper, e.Font, e.TextSize, e.Hint) macro.Stop() if w := gtx.Dimensions.Size.X; gtx.Constraints.Width.Min < w { gtx.Constraints.Width.Min = w @@ -51,7 +51,7 @@ func (e Editor) Layout(gtx *layout.Context, editor *widget.Editor) { if h := gtx.Dimensions.Size.Y; gtx.Constraints.Height.Min < h { gtx.Constraints.Height.Min = h } - editor.Layout(gtx, e.shaper, e.Font) + editor.Layout(gtx, e.shaper, e.Font, e.TextSize) if editor.Len() > 0 { paint.ColorOp{Color: e.Color}.Add(gtx.Ops) editor.PaintText(gtx) diff --git a/widget/material/label.go b/widget/material/label.go index c3aea991..10d6e5e8 100644 --- a/widget/material/label.go +++ b/widget/material/label.go @@ -22,6 +22,7 @@ type Label struct { // MaxLines limits the number of lines. Zero means no limit. MaxLines int Text string + TextSize unit.Value shaper text.Shaper } @@ -64,17 +65,15 @@ func (t *Theme) Caption(txt string) Label { func (t *Theme) Label(size unit.Value, txt string) Label { return Label{ - Text: txt, - Color: t.Color.Text, - Font: text.Font{ - Size: size, - }, - shaper: t.Shaper, + Text: txt, + Color: t.Color.Text, + TextSize: size, + shaper: t.Shaper, } } func (l Label) Layout(gtx *layout.Context) { paint.ColorOp{Color: l.Color}.Add(gtx.Ops) tl := widget.Label{Alignment: l.Alignment, MaxLines: l.MaxLines} - tl.Layout(gtx, l.shaper, l.Font, l.Text) + tl.Layout(gtx, l.shaper, l.Font, l.TextSize, l.Text) } diff --git a/widget/material/radiobutton.go b/widget/material/radiobutton.go index c690e22e..1152256f 100644 --- a/widget/material/radiobutton.go +++ b/widget/material/radiobutton.go @@ -4,7 +4,6 @@ package material import ( "gioui.org/layout" - "gioui.org/text" "gioui.org/unit" "gioui.org/widget" ) @@ -21,11 +20,9 @@ func (t *Theme) RadioButton(key, label string) RadioButton { checkable: checkable{ Label: label, - Color: t.Color.Text, - IconColor: t.Color.Primary, - Font: text.Font{ - Size: t.TextSize.Scale(14.0 / 16.0), - }, + Color: t.Color.Text, + IconColor: t.Color.Primary, + TextSize: t.TextSize.Scale(14.0 / 16.0), Size: unit.Dp(26), shaper: t.Shaper, checkedStateIcon: t.radioCheckedIcon,