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 <christopher.waldon.dev@gmail.com>
This commit is contained in:
Chris Waldon
2023-07-18 16:26:45 -04:00
committed by Elias Naur
parent acab582487
commit ddf770b9d5
6 changed files with 81 additions and 19 deletions
+8
View File
@@ -35,6 +35,12 @@ type Editor struct {
text textView text textView
// Alignment controls the alignment of text within the editor. // Alignment controls the alignment of text within the editor.
Alignment text.Alignment 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 force the text to stay on a single line.
// SingleLine also sets the scrolling direction to // SingleLine also sets the scrolling direction to
// horizontal. // horizontal.
@@ -504,6 +510,8 @@ func (e *Editor) initBuffer() {
e.text.SetSource(e.buffer) e.text.SetSource(e.buffer)
} }
e.text.Alignment = e.Alignment e.text.Alignment = e.Alignment
e.text.LineHeight = e.LineHeight
e.text.LineHeightScale = e.LineHeightScale
e.text.SingleLine = e.SingleLine e.text.SingleLine = e.SingleLine
e.text.Mask = e.Mask e.text.Mask = e.Mask
e.text.WrapPolicy = e.WrapPolicy e.text.WrapPolicy = e.WrapPolicy
+18 -9
View File
@@ -30,6 +30,12 @@ type Label struct {
Truncator string Truncator string
// WrapPolicy configures how displayed text will be broken into lines. // WrapPolicy configures how displayed text will be broken into lines.
WrapPolicy text.WrapPolicy 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. // 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) { 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 cs := gtx.Constraints
textSize := fixed.I(gtx.Sp(size)) textSize := fixed.I(gtx.Sp(size))
lineHeight := fixed.I(gtx.Sp(l.LineHeight))
lt.LayoutString(text.Parameters{ lt.LayoutString(text.Parameters{
Font: font, Font: font,
PxPerEm: textSize, PxPerEm: textSize,
MaxLines: l.MaxLines, MaxLines: l.MaxLines,
Truncator: l.Truncator, Truncator: l.Truncator,
Alignment: l.Alignment, Alignment: l.Alignment,
WrapPolicy: l.WrapPolicy, WrapPolicy: l.WrapPolicy,
MaxWidth: cs.Max.X, MaxWidth: cs.Max.X,
MinWidth: cs.Min.X, MinWidth: cs.Min.X,
Locale: gtx.Locale, Locale: gtx.Locale,
LineHeight: lineHeight,
LineHeightScale: l.LineHeightScale,
}, txt) }, txt)
m := op.Record(gtx.Ops) m := op.Record(gtx.Ops)
viewport := image.Rectangle{Max: cs.Max} viewport := image.Rectangle{Max: cs.Max}
+16 -3
View File
@@ -16,8 +16,14 @@ import (
) )
type EditorStyle struct { type EditorStyle struct {
Font font.Font Font font.Font
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
TextSize unit.Sp
// Color is the text color. // Color is the text color.
Color color.NRGBA Color color.NRGBA
// Hint contains the text displayed when the editor is empty. // 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) 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) dims := tl.Layout(gtx, e.shaper, e.Font, e.TextSize, e.Hint, hintColor)
call := macro.Stop() 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 { if h := dims.Size.Y; gtx.Constraints.Min.Y < h {
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) dims = e.Editor.Layout(gtx, e.shaper, e.Font, e.TextSize, textColor, selectionColor)
if e.Editor.Len() == 0 { if e.Editor.Len() == 0 {
call.Add(gtx.Ops) call.Add(gtx.Ops)
+14 -4
View File
@@ -38,6 +38,12 @@ type LabelStyle struct {
Text string Text string
// TextSize determines the size of the text glyphs. // TextSize determines the size of the text glyphs.
TextSize unit.Sp 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 // 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 // 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.MaxLines = l.MaxLines
l.State.Truncator = l.Truncator l.State.Truncator = l.Truncator
l.State.WrapPolicy = l.WrapPolicy 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) return l.State.Layout(gtx, l.Shaper, l.Font, l.TextSize, textColor, selectColor)
} }
tl := widget.Label{ tl := widget.Label{
Alignment: l.Alignment, Alignment: l.Alignment,
MaxLines: l.MaxLines, MaxLines: l.MaxLines,
Truncator: l.Truncator, Truncator: l.Truncator,
WrapPolicy: l.WrapPolicy, WrapPolicy: l.WrapPolicy,
LineHeight: l.LineHeight,
LineHeightScale: l.LineHeightScale,
} }
return tl.Layout(gtx, l.Shaper, l.Font, l.TextSize, l.Text, textColor) return tl.Layout(gtx, l.Shaper, l.Font, l.TextSize, l.Text, textColor)
} }
+11 -3
View File
@@ -59,9 +59,15 @@ type Selectable struct {
// if text was cut off. Defaults to "…" if left empty. // if text was cut off. Defaults to "…" if left empty.
Truncator string Truncator string
// WrapPolicy configures how displayed text will be broken into lines. // WrapPolicy configures how displayed text will be broken into lines.
WrapPolicy text.WrapPolicy WrapPolicy text.WrapPolicy
initialized bool // LineHeight controls the distance between the baselines of lines of text.
source stringSource // 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 // scratch is a buffer reused to efficiently read text out of the
// textView. // textView.
scratch []byte scratch []byte
@@ -181,6 +187,8 @@ func (l *Selectable) Truncated() bool {
// paint material for the text and selection rectangles, respectively. // 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 { 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.initialize()
l.text.LineHeight = l.LineHeight
l.text.LineHeightScale = l.LineHeightScale
l.text.Alignment = l.Alignment l.text.Alignment = l.Alignment
l.text.MaxLines = l.MaxLines l.text.MaxLines = l.MaxLines
l.text.Truncator = l.Truncator l.text.Truncator = l.Truncator
+14
View File
@@ -44,6 +44,12 @@ type textSource interface {
// be scrolled, and for configuring and drawing text selection boxes. // be scrolled, and for configuring and drawing text selection boxes.
type textView struct { type textView struct {
Alignment text.Alignment 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 forces the text to stay on a single line.
// SingleLine also sets the scrolling direction to // SingleLine also sets the scrolling direction to
// horizontal. // 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.params.WrapPolicy = e.WrapPolicy
e.invalidate() 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() e.makeValid()
if eventHandling != nil { if eventHandling != nil {