From ddf770b9d533655b24e7f7fd2a2cb66e4bc7a4c4 Mon Sep 17 00:00:00 2001 From: Chris Waldon Date: Tue, 18 Jul 2023 16:26:45 -0400 Subject: [PATCH] widget{,/material}: surface line height manipulation This commit surfaces fields to manipulate the line height of all label and editor types. It's unfortunate how this spreads through the API, but I don't see a good way to eliminate that right now. Signed-off-by: Chris Waldon --- widget/editor.go | 8 ++++++++ widget/label.go | 27 ++++++++++++++++++--------- widget/material/editor.go | 19 ++++++++++++++++--- widget/material/label.go | 18 ++++++++++++++---- widget/selectable.go | 14 +++++++++++--- widget/text.go | 14 ++++++++++++++ 6 files changed, 81 insertions(+), 19 deletions(-) diff --git a/widget/editor.go b/widget/editor.go index 69820e8d..19601082 100644 --- a/widget/editor.go +++ b/widget/editor.go @@ -35,6 +35,12 @@ type Editor struct { text textView // Alignment controls the alignment of text within the editor. Alignment text.Alignment + // LineHeight determines the gap between baselines of text. If zero, a sensible + // default will be used. + LineHeight unit.Sp + // LineHeightScale is multiplied by LineHeight to determine the final gap + // between baselines. If zero, a sensible default will be used. + LineHeightScale float32 // SingleLine force the text to stay on a single line. // SingleLine also sets the scrolling direction to // horizontal. @@ -504,6 +510,8 @@ func (e *Editor) initBuffer() { e.text.SetSource(e.buffer) } e.text.Alignment = e.Alignment + e.text.LineHeight = e.LineHeight + e.text.LineHeightScale = e.LineHeightScale e.text.SingleLine = e.SingleLine e.text.Mask = e.Mask e.text.WrapPolicy = e.WrapPolicy diff --git a/widget/label.go b/widget/label.go index f4e2279d..58a01d70 100644 --- a/widget/label.go +++ b/widget/label.go @@ -30,6 +30,12 @@ type Label struct { Truncator string // WrapPolicy configures how displayed text will be broken into lines. WrapPolicy text.WrapPolicy + // LineHeight controls the distance between the baselines of lines of text. + // If zero, a sensible default will be used. + LineHeight unit.Sp + // LineHeightScale applies a scaling factor to the LineHeight. If zero, a + // sensible default will be used. + LineHeightScale float32 } // Layout the label with the given shaper, font, size, text, and material. @@ -49,16 +55,19 @@ type TextInfo struct { func (l Label) LayoutDetailed(gtx layout.Context, lt *text.Shaper, font font.Font, size unit.Sp, txt string, textMaterial op.CallOp) (layout.Dimensions, TextInfo) { cs := gtx.Constraints textSize := fixed.I(gtx.Sp(size)) + lineHeight := fixed.I(gtx.Sp(l.LineHeight)) lt.LayoutString(text.Parameters{ - Font: font, - PxPerEm: textSize, - MaxLines: l.MaxLines, - Truncator: l.Truncator, - Alignment: l.Alignment, - WrapPolicy: l.WrapPolicy, - MaxWidth: cs.Max.X, - MinWidth: cs.Min.X, - Locale: gtx.Locale, + Font: font, + PxPerEm: textSize, + MaxLines: l.MaxLines, + Truncator: l.Truncator, + Alignment: l.Alignment, + WrapPolicy: l.WrapPolicy, + MaxWidth: cs.Max.X, + MinWidth: cs.Min.X, + Locale: gtx.Locale, + LineHeight: lineHeight, + LineHeightScale: l.LineHeightScale, }, txt) m := op.Record(gtx.Ops) viewport := image.Rectangle{Max: cs.Max} diff --git a/widget/material/editor.go b/widget/material/editor.go index 0b4850de..db3c7935 100644 --- a/widget/material/editor.go +++ b/widget/material/editor.go @@ -16,8 +16,14 @@ import ( ) type EditorStyle struct { - Font font.Font - TextSize unit.Sp + Font font.Font + // LineHeight controls the distance between the baselines of lines of text. + // If zero, a sensible default will be used. + LineHeight unit.Sp + // LineHeightScale applies a scaling factor to the LineHeight. If zero, a + // sensible default will be used. + LineHeightScale float32 + TextSize unit.Sp // Color is the text color. Color color.NRGBA // Hint contains the text displayed when the editor is empty. @@ -64,7 +70,12 @@ func (e EditorStyle) Layout(gtx layout.Context) layout.Dimensions { } macro := op.Record(gtx.Ops) - tl := widget.Label{Alignment: e.Editor.Alignment, MaxLines: maxlines} + tl := widget.Label{ + Alignment: e.Editor.Alignment, + MaxLines: maxlines, + LineHeight: e.LineHeight, + LineHeightScale: e.LineHeightScale, + } dims := tl.Layout(gtx, e.shaper, e.Font, e.TextSize, e.Hint, hintColor) call := macro.Stop() @@ -74,6 +85,8 @@ func (e EditorStyle) Layout(gtx layout.Context) layout.Dimensions { if h := dims.Size.Y; gtx.Constraints.Min.Y < h { gtx.Constraints.Min.Y = h } + e.Editor.LineHeight = e.LineHeight + e.Editor.LineHeightScale = e.LineHeightScale dims = e.Editor.Layout(gtx, e.shaper, e.Font, e.TextSize, textColor, selectionColor) if e.Editor.Len() == 0 { call.Add(gtx.Ops) diff --git a/widget/material/label.go b/widget/material/label.go index 2eb295e6..11cbecd1 100644 --- a/widget/material/label.go +++ b/widget/material/label.go @@ -38,6 +38,12 @@ type LabelStyle struct { Text string // TextSize determines the size of the text glyphs. TextSize unit.Sp + // LineHeight controls the distance between the baselines of lines of text. + // If zero, a sensible default will be used. + LineHeight unit.Sp + // LineHeightScale applies a scaling factor to the LineHeight. If zero, a + // sensible default will be used. + LineHeightScale float32 // Shaper is the text shaper used to display this labe. This field is automatically // set using by all constructor functions. If constructing a LabelStyle literal, you @@ -132,13 +138,17 @@ func (l LabelStyle) Layout(gtx layout.Context) layout.Dimensions { l.State.MaxLines = l.MaxLines l.State.Truncator = l.Truncator l.State.WrapPolicy = l.WrapPolicy + l.State.LineHeight = l.LineHeight + l.State.LineHeightScale = l.LineHeightScale return l.State.Layout(gtx, l.Shaper, l.Font, l.TextSize, textColor, selectColor) } tl := widget.Label{ - Alignment: l.Alignment, - MaxLines: l.MaxLines, - Truncator: l.Truncator, - WrapPolicy: l.WrapPolicy, + Alignment: l.Alignment, + MaxLines: l.MaxLines, + Truncator: l.Truncator, + WrapPolicy: l.WrapPolicy, + LineHeight: l.LineHeight, + LineHeightScale: l.LineHeightScale, } return tl.Layout(gtx, l.Shaper, l.Font, l.TextSize, l.Text, textColor) } diff --git a/widget/selectable.go b/widget/selectable.go index aa4c9da5..90385dc7 100644 --- a/widget/selectable.go +++ b/widget/selectable.go @@ -59,9 +59,15 @@ type Selectable struct { // if text was cut off. Defaults to "…" if left empty. Truncator string // WrapPolicy configures how displayed text will be broken into lines. - WrapPolicy text.WrapPolicy - initialized bool - source stringSource + WrapPolicy text.WrapPolicy + // LineHeight controls the distance between the baselines of lines of text. + // If zero, a sensible default will be used. + LineHeight unit.Sp + // LineHeightScale applies a scaling factor to the LineHeight. If zero, a + // sensible default will be used. + LineHeightScale float32 + initialized bool + source stringSource // scratch is a buffer reused to efficiently read text out of the // textView. scratch []byte @@ -181,6 +187,8 @@ func (l *Selectable) Truncated() bool { // paint material for the text and selection rectangles, respectively. func (l *Selectable) Layout(gtx layout.Context, lt *text.Shaper, font font.Font, size unit.Sp, textMaterial, selectionMaterial op.CallOp) layout.Dimensions { l.initialize() + l.text.LineHeight = l.LineHeight + l.text.LineHeightScale = l.LineHeightScale l.text.Alignment = l.Alignment l.text.MaxLines = l.MaxLines l.text.Truncator = l.Truncator diff --git a/widget/text.go b/widget/text.go index 852182dd..d1a843d2 100644 --- a/widget/text.go +++ b/widget/text.go @@ -44,6 +44,12 @@ type textSource interface { // be scrolled, and for configuring and drawing text selection boxes. type textView struct { Alignment text.Alignment + // LineHeight controls the distance between the baselines of lines of text. + // If zero, a sensible default will be used. + LineHeight unit.Sp + // LineHeightScale applies a scaling factor to the LineHeight. If zero, a + // sensible default will be used. + LineHeightScale float32 // SingleLine forces the text to stay on a single line. // SingleLine also sets the scrolling direction to // horizontal. @@ -273,6 +279,14 @@ func (e *textView) Update(gtx layout.Context, lt *text.Shaper, font font.Font, s e.params.WrapPolicy = e.WrapPolicy e.invalidate() } + if lh := fixed.I(gtx.Sp(e.LineHeight)); lh != e.params.LineHeight { + e.params.LineHeight = lh + e.invalidate() + } + if e.LineHeightScale != e.params.LineHeightScale { + e.params.LineHeightScale = e.LineHeightScale + e.invalidate() + } e.makeValid() if eventHandling != nil {