mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
widget{,/material}: [API] split interactive and non-interactive text widgets
This commit separates the types for interactive and non-interactive text within package widget. widget.Selectable is used for all interactive text. widget.Label is used for all non-interactive text. There is no longer a field on widget.Label to provide it with a Selectable. If you want selectable text and are not relying upon the material pacakge API, you need to create widget.Selectables instead of widget.Labels. The material package's LabelStyle API is unchanged. Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
This commit is contained in:
+4
-21
@@ -16,7 +16,8 @@ import (
|
||||
"golang.org/x/image/math/fixed"
|
||||
)
|
||||
|
||||
// Label is a widget for laying out and drawing text.
|
||||
// Label is a widget for laying out and drawing text. Labels are always
|
||||
// non-interactive text. They cannot be selected or copied.
|
||||
type Label struct {
|
||||
// Alignment specifies the text alignment.
|
||||
Alignment text.Alignment
|
||||
@@ -25,28 +26,10 @@ type Label struct {
|
||||
// Truncator is the text that will be shown at the end of the final
|
||||
// line if MaxLines is exceeded. Defaults to "…" if empty.
|
||||
Truncator string
|
||||
// Selectable optionally provides text selection state. If nil,
|
||||
// text will not be selectable.
|
||||
Selectable *Selectable
|
||||
}
|
||||
|
||||
// Layout the label with the given shaper, font, size, text, and materials. If the Selectable field is
|
||||
// populated, the label will support text selection. Otherwise, it will be non-interactive. The textMaterial
|
||||
// and selectionMaterial op.CallOps are responsible for setting the painting material for the text glyphs
|
||||
// and the text selection rectangles, respectively.
|
||||
func (l Label) Layout(gtx layout.Context, lt *text.Shaper, font text.Font, size unit.Sp, txt string, textMaterial, selectionMaterial op.CallOp) layout.Dimensions {
|
||||
if l.Selectable == nil {
|
||||
return l.layout(gtx, lt, font, size, txt, textMaterial, selectionMaterial)
|
||||
}
|
||||
l.Selectable.text.Alignment = l.Alignment
|
||||
l.Selectable.text.MaxLines = l.MaxLines
|
||||
l.Selectable.text.Truncator = l.Truncator
|
||||
l.Selectable.SetText(txt)
|
||||
return l.Selectable.Layout(gtx, lt, font, size, textMaterial, selectionMaterial)
|
||||
}
|
||||
|
||||
// layout the text as non-interactive.
|
||||
func (l Label) layout(gtx layout.Context, lt *text.Shaper, font text.Font, size unit.Sp, txt string, textMaterial, selectionMaterial op.CallOp) layout.Dimensions {
|
||||
// Layout the label with the given shaper, font, size, text, and material.
|
||||
func (l Label) Layout(gtx layout.Context, lt *text.Shaper, font text.Font, size unit.Sp, txt string, textMaterial op.CallOp) layout.Dimensions {
|
||||
cs := gtx.Constraints
|
||||
textSize := fixed.I(gtx.Sp(size))
|
||||
lt.LayoutString(text.Parameters{
|
||||
|
||||
@@ -119,7 +119,7 @@ func (b ButtonStyle) Layout(gtx layout.Context) layout.Dimensions {
|
||||
return b.Inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||
colMacro := op.Record(gtx.Ops)
|
||||
paint.ColorOp{Color: b.Color}.Add(gtx.Ops)
|
||||
return widget.Label{Alignment: text.Middle}.Layout(gtx, b.shaper, b.Font, b.TextSize, b.Text, colMacro.Stop(), op.CallOp{})
|
||||
return widget.Label{Alignment: text.Middle}.Layout(gtx, b.shaper, b.Font, b.TextSize, b.Text, colMacro.Stop())
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ func (c *checkable) layout(gtx layout.Context, checked, hovered bool) layout.Dim
|
||||
return layout.UniformInset(2).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||
colMacro := op.Record(gtx.Ops)
|
||||
paint.ColorOp{Color: c.Color}.Add(gtx.Ops)
|
||||
return widget.Label{}.Layout(gtx, c.shaper, c.Font, c.TextSize, c.Label, colMacro.Stop(), op.CallOp{})
|
||||
return widget.Label{}.Layout(gtx, c.shaper, c.Font, c.TextSize, c.Label, colMacro.Stop())
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -61,7 +61,7 @@ func (e EditorStyle) Layout(gtx layout.Context) layout.Dimensions {
|
||||
|
||||
macro := op.Record(gtx.Ops)
|
||||
tl := widget.Label{Alignment: e.Editor.Alignment, MaxLines: maxlines}
|
||||
dims := tl.Layout(gtx, e.shaper, e.Font, e.TextSize, e.Hint, hintColor, selectionColor)
|
||||
dims := tl.Layout(gtx, e.shaper, e.Font, e.TextSize, e.Hint, hintColor)
|
||||
call := macro.Stop()
|
||||
|
||||
if w := dims.Size.X; gtx.Constraints.Min.X < w {
|
||||
|
||||
@@ -14,6 +14,9 @@ import (
|
||||
"gioui.org/widget"
|
||||
)
|
||||
|
||||
// LabelStyle configures the presentation of text. If the State field is set, the
|
||||
// label will be laid out as interactive (able to be selected and copied). Otherwise,
|
||||
// the label will be non-interactive.
|
||||
type LabelStyle struct {
|
||||
// Face defines the text style.
|
||||
Font text.Font
|
||||
@@ -109,6 +112,12 @@ func (l LabelStyle) Layout(gtx layout.Context) layout.Dimensions {
|
||||
paint.ColorOp{Color: l.SelectionColor}.Add(gtx.Ops)
|
||||
selectColor := selectColorMacro.Stop()
|
||||
|
||||
tl := widget.Label{Alignment: l.Alignment, MaxLines: l.MaxLines, Selectable: l.State}
|
||||
return tl.Layout(gtx, l.shaper, l.Font, l.TextSize, l.Text, textColor, selectColor)
|
||||
if l.State != nil {
|
||||
if l.State.Text() != l.Text {
|
||||
l.State.SetText(l.Text)
|
||||
}
|
||||
return l.State.Layout(gtx, l.shaper, l.Font, l.TextSize, textColor, selectColor)
|
||||
}
|
||||
tl := widget.Label{Alignment: l.Alignment, MaxLines: l.MaxLines}
|
||||
return tl.Layout(gtx, l.shaper, l.Font, l.TextSize, l.Text, textColor)
|
||||
}
|
||||
|
||||
@@ -46,9 +46,8 @@ func TestSelectableMove(t *testing.T) {
|
||||
gtx.Queue = newQueue(key.FocusEvent{Focus: true})
|
||||
s := new(Selectable)
|
||||
|
||||
Label{
|
||||
Selectable: s,
|
||||
}.Layout(gtx, cache, text.Font{}, fontSize, str, op.CallOp{}, op.CallOp{})
|
||||
s.SetText(str)
|
||||
s.Layout(gtx, cache, text.Font{}, fontSize, op.CallOp{}, op.CallOp{})
|
||||
|
||||
testKey := func(keyName string) {
|
||||
// Select 345
|
||||
@@ -62,9 +61,8 @@ func TestSelectableMove(t *testing.T) {
|
||||
|
||||
// Press the key
|
||||
gtx.Queue = newQueue(key.Event{State: key.Press, Name: keyName})
|
||||
Label{
|
||||
Selectable: s,
|
||||
}.Layout(gtx, cache, font, fontSize, str, op.CallOp{}, op.CallOp{})
|
||||
s.SetText(str)
|
||||
s.Layout(gtx, cache, font, fontSize, op.CallOp{}, op.CallOp{})
|
||||
|
||||
if expected, got := "", s.SelectedText(); expected != got {
|
||||
t.Errorf("KeyName %s, expected %q, got %q", keyName, expected, got)
|
||||
@@ -102,12 +100,10 @@ func TestSelectableConfigurations(t *testing.T) {
|
||||
gtx.Constraints.Min = gtx.Constraints.Max
|
||||
}
|
||||
s := new(Selectable)
|
||||
label := Label{
|
||||
Alignment: alignment,
|
||||
Selectable: s,
|
||||
}
|
||||
interactiveDims := label.Layout(gtx, cache, font, fontSize, sentence, op.CallOp{}, op.CallOp{})
|
||||
staticDims := label.Layout(gtx, cache, font, fontSize, sentence, op.CallOp{}, op.CallOp{})
|
||||
s.text.Alignment = alignment
|
||||
s.SetText(sentence)
|
||||
interactiveDims := s.Layout(gtx, cache, font, fontSize, op.CallOp{}, op.CallOp{})
|
||||
staticDims := Label{Alignment: alignment}.Layout(gtx, cache, font, fontSize, sentence, op.CallOp{})
|
||||
|
||||
if interactiveDims != staticDims {
|
||||
t.Errorf("expected consistent dimensions, static returned %#+v, interactive returned %#+v", staticDims, interactiveDims)
|
||||
|
||||
@@ -97,7 +97,7 @@ func BenchmarkLabelStatic(b *testing.B) {
|
||||
l := Label{}
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
l.Layout(gtx, cache, font, fontSize, runesStr, op.CallOp{}, op.CallOp{})
|
||||
l.Layout(gtx, cache, font, fontSize, runesStr, op.CallOp{})
|
||||
if render {
|
||||
win.Frame(gtx.Ops)
|
||||
}
|
||||
@@ -132,7 +132,7 @@ func BenchmarkLabelDynamic(b *testing.B) {
|
||||
a := rand.Intn(len(runes))
|
||||
b := rand.Intn(len(runes))
|
||||
runes[a], runes[b] = runes[b], runes[a]
|
||||
l.Layout(gtx, cache, font, fontSize, string(runes), op.CallOp{}, op.CallOp{})
|
||||
l.Layout(gtx, cache, font, fontSize, string(runes), op.CallOp{})
|
||||
if render {
|
||||
win.Frame(gtx.Ops)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user