widget/material: move widget state object from Layout methods to constructors

Instead of, say,

	var th *material.Theme
	var btn *widget.Clickable

	material.Button(th, "Click me").Layout(gtx, btn)

move the widget state objects to the constructor:

	material.Button(th, btn, "Click me").Layout(gtx)

The advatage is that several widgets can now be used without
wrapping them in function literals. For example,

	layout.Inset{}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
		material.Button(th, "Click me").Layout(gtx, btn)
	})

collapses to just

	layout.Inset{}.Layout(gtx, material.Button(th, btn, "Click me").Layout)

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2020-05-23 20:09:08 +02:00
parent 3af01a3f43
commit 2451750782
6 changed files with 62 additions and 44 deletions
+20 -13
View File
@@ -26,6 +26,7 @@ type ButtonStyle struct {
Background color.RGBA Background color.RGBA
CornerRadius unit.Value CornerRadius unit.Value
Inset layout.Inset Inset layout.Inset
Button *widget.Clickable
shaper text.Shaper shaper text.Shaper
} }
@@ -33,6 +34,7 @@ type ButtonLayoutStyle struct {
Background color.RGBA Background color.RGBA
CornerRadius unit.Value CornerRadius unit.Value
Inset layout.Inset Inset layout.Inset
Button *widget.Clickable
} }
type IconButtonStyle struct { type IconButtonStyle struct {
@@ -41,11 +43,12 @@ type IconButtonStyle struct {
Color color.RGBA Color color.RGBA
Icon *widget.Icon Icon *widget.Icon
// Size is the icon size. // Size is the icon size.
Size unit.Value Size unit.Value
Inset layout.Inset Inset layout.Inset
Button *widget.Clickable
} }
func Button(th *Theme, txt string) ButtonStyle { func Button(th *Theme, button *widget.Clickable, txt string) ButtonStyle {
return ButtonStyle{ return ButtonStyle{
Text: txt, Text: txt,
Color: rgb(0xffffff), Color: rgb(0xffffff),
@@ -56,25 +59,28 @@ func Button(th *Theme, txt string) ButtonStyle {
Top: unit.Dp(10), Bottom: unit.Dp(10), Top: unit.Dp(10), Bottom: unit.Dp(10),
Left: unit.Dp(12), Right: unit.Dp(12), Left: unit.Dp(12), Right: unit.Dp(12),
}, },
Button: button,
shaper: th.Shaper, shaper: th.Shaper,
} }
} }
func ButtonLayout(th *Theme) ButtonLayoutStyle { func ButtonLayout(th *Theme, button *widget.Clickable) ButtonLayoutStyle {
return ButtonLayoutStyle{ return ButtonLayoutStyle{
Button: button,
Background: th.Color.Primary, Background: th.Color.Primary,
CornerRadius: unit.Dp(4), CornerRadius: unit.Dp(4),
Inset: layout.UniformInset(unit.Dp(12)), Inset: layout.UniformInset(unit.Dp(12)),
} }
} }
func IconButton(th *Theme, icon *widget.Icon) IconButtonStyle { func IconButton(th *Theme, button *widget.Clickable, icon *widget.Icon) IconButtonStyle {
return IconButtonStyle{ return IconButtonStyle{
Background: th.Color.Primary, Background: th.Color.Primary,
Color: th.Color.InvText, Color: th.Color.InvText,
Icon: icon, Icon: icon,
Size: unit.Dp(24), Size: unit.Dp(24),
Inset: layout.UniformInset(unit.Dp(12)), Inset: layout.UniformInset(unit.Dp(12)),
Button: button,
} }
} }
@@ -99,18 +105,19 @@ func Clickable(gtx layout.Context, button *widget.Clickable, w layout.Widget) la
) )
} }
func (b ButtonStyle) Layout(gtx layout.Context, button *widget.Clickable) layout.Dimensions { func (b ButtonStyle) Layout(gtx layout.Context) layout.Dimensions {
return ButtonLayoutStyle{ return ButtonLayoutStyle{
Background: b.Background, Background: b.Background,
CornerRadius: b.CornerRadius, CornerRadius: b.CornerRadius,
Inset: b.Inset, Inset: b.Inset,
}.Layout(gtx, button, func(gtx layout.Context) layout.Dimensions { Button: b.Button,
}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
paint.ColorOp{Color: b.Color}.Add(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) return widget.Label{Alignment: text.Middle}.Layout(gtx, b.shaper, b.Font, b.TextSize, b.Text)
}) })
} }
func (b ButtonLayoutStyle) Layout(gtx layout.Context, button *widget.Clickable, w layout.Widget) layout.Dimensions { func (b ButtonLayoutStyle) Layout(gtx layout.Context, w layout.Widget) layout.Dimensions {
min := gtx.Constraints.Min min := gtx.Constraints.Min
return layout.Stack{Alignment: layout.Center}.Layout(gtx, return layout.Stack{Alignment: layout.Center}.Layout(gtx,
layout.Expanded(func(gtx layout.Context) layout.Dimensions { layout.Expanded(func(gtx layout.Context) layout.Dimensions {
@@ -123,7 +130,7 @@ func (b ButtonLayoutStyle) Layout(gtx layout.Context, button *widget.Clickable,
NE: rr, NW: rr, SE: rr, SW: rr, NE: rr, NW: rr, SE: rr, SW: rr,
}.Op(gtx.Ops).Add(gtx.Ops) }.Op(gtx.Ops).Add(gtx.Ops)
dims := fill(gtx, b.Background) dims := fill(gtx, b.Background)
for _, c := range button.History() { for _, c := range b.Button.History() {
drawInk(gtx, c) drawInk(gtx, c)
} }
return dims return dims
@@ -134,11 +141,11 @@ func (b ButtonLayoutStyle) Layout(gtx layout.Context, button *widget.Clickable,
return b.Inset.Layout(gtx, w) return b.Inset.Layout(gtx, w)
}) })
}), }),
layout.Expanded(button.Layout), layout.Expanded(b.Button.Layout),
) )
} }
func (b IconButtonStyle) Layout(gtx layout.Context, button *widget.Clickable) layout.Dimensions { func (b IconButtonStyle) Layout(gtx layout.Context) layout.Dimensions {
return layout.Stack{Alignment: layout.Center}.Layout(gtx, return layout.Stack{Alignment: layout.Center}.Layout(gtx,
layout.Expanded(func(gtx layout.Context) layout.Dimensions { layout.Expanded(func(gtx layout.Context) layout.Dimensions {
size := gtx.Constraints.Min.X size := gtx.Constraints.Min.X
@@ -149,7 +156,7 @@ func (b IconButtonStyle) Layout(gtx layout.Context, button *widget.Clickable) la
NE: rr, NW: rr, SE: rr, SW: rr, NE: rr, NW: rr, SE: rr, SW: rr,
}.Op(gtx.Ops).Add(gtx.Ops) }.Op(gtx.Ops).Add(gtx.Ops)
dims := fill(gtx, b.Background) dims := fill(gtx, b.Background)
for _, c := range button.History() { for _, c := range b.Button.History() {
drawInk(gtx, c) drawInk(gtx, c)
} }
return dims return dims
@@ -168,7 +175,7 @@ func (b IconButtonStyle) Layout(gtx layout.Context, button *widget.Clickable) la
}), }),
layout.Expanded(func(gtx layout.Context) layout.Dimensions { layout.Expanded(func(gtx layout.Context) layout.Dimensions {
pointer.Ellipse(image.Rectangle{Max: gtx.Constraints.Min}).Add(gtx.Ops) pointer.Ellipse(image.Rectangle{Max: gtx.Constraints.Min}).Add(gtx.Ops)
return button.Layout(gtx) return b.Button.Layout(gtx)
}), }),
) )
} }
+8 -6
View File
@@ -10,11 +10,13 @@ import (
type CheckBoxStyle struct { type CheckBoxStyle struct {
checkable checkable
CheckBox *widget.Bool
} }
func CheckBox(th *Theme, label string) CheckBoxStyle { func CheckBox(th *Theme, checkBox *widget.Bool, label string) CheckBoxStyle {
return CheckBoxStyle{ return CheckBoxStyle{
checkable{ CheckBox: checkBox,
checkable: checkable{
Label: label, Label: label,
Color: th.Color.Text, Color: th.Color.Text,
IconColor: th.Color.Primary, IconColor: th.Color.Primary,
@@ -28,9 +30,9 @@ func CheckBox(th *Theme, label string) CheckBoxStyle {
} }
// Layout updates the checkBox and displays it. // Layout updates the checkBox and displays it.
func (c CheckBoxStyle) Layout(gtx layout.Context, checkBox *widget.Bool) layout.Dimensions { func (c CheckBoxStyle) Layout(gtx layout.Context) layout.Dimensions {
checkBox.Update(gtx) c.CheckBox.Update(gtx)
dims := c.layout(gtx, checkBox.Value) dims := c.layout(gtx, c.CheckBox.Value)
checkBox.Layout(gtx) c.CheckBox.Layout(gtx)
return dims return dims
} }
+9 -7
View File
@@ -22,12 +22,14 @@ type EditorStyle struct {
Hint string Hint string
// HintColor is the color of hint text. // HintColor is the color of hint text.
HintColor color.RGBA HintColor color.RGBA
Editor *widget.Editor
shaper text.Shaper shaper text.Shaper
} }
func Editor(th *Theme, hint string) EditorStyle { func Editor(th *Theme, editor *widget.Editor, hint string) EditorStyle {
return EditorStyle{ return EditorStyle{
Editor: editor,
TextSize: th.TextSize, TextSize: th.TextSize,
Color: th.Color.Text, Color: th.Color.Text,
shaper: th.Shaper, shaper: th.Shaper,
@@ -36,13 +38,13 @@ func Editor(th *Theme, hint string) EditorStyle {
} }
} }
func (e EditorStyle) Layout(gtx layout.Context, editor *widget.Editor) layout.Dimensions { func (e EditorStyle) Layout(gtx layout.Context) layout.Dimensions {
var stack op.StackOp var stack op.StackOp
stack.Push(gtx.Ops) stack.Push(gtx.Ops)
var macro op.MacroOp var macro op.MacroOp
macro.Record(gtx.Ops) macro.Record(gtx.Ops)
paint.ColorOp{Color: e.HintColor}.Add(gtx.Ops) paint.ColorOp{Color: e.HintColor}.Add(gtx.Ops)
tl := widget.Label{Alignment: editor.Alignment} tl := widget.Label{Alignment: e.Editor.Alignment}
dims := tl.Layout(gtx, e.shaper, e.Font, e.TextSize, e.Hint) dims := tl.Layout(gtx, e.shaper, e.Font, e.TextSize, e.Hint)
macro.Stop() macro.Stop()
if w := dims.Size.X; gtx.Constraints.Min.X < w { if w := dims.Size.X; gtx.Constraints.Min.X < w {
@@ -51,15 +53,15 @@ func (e EditorStyle) Layout(gtx layout.Context, editor *widget.Editor) layout.Di
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
} }
dims = editor.Layout(gtx, e.shaper, e.Font, e.TextSize) dims = e.Editor.Layout(gtx, e.shaper, e.Font, e.TextSize)
if editor.Len() > 0 { if e.Editor.Len() > 0 {
paint.ColorOp{Color: e.Color}.Add(gtx.Ops) paint.ColorOp{Color: e.Color}.Add(gtx.Ops)
editor.PaintText(gtx) e.Editor.PaintText(gtx)
} else { } else {
macro.Add() macro.Add()
} }
paint.ColorOp{Color: e.Color}.Add(gtx.Ops) paint.ColorOp{Color: e.Color}.Add(gtx.Ops)
editor.PaintCaret(gtx) e.Editor.PaintCaret(gtx)
stack.Pop() stack.Pop()
return dims return dims
} }
+7 -4
View File
@@ -14,16 +14,18 @@ import (
) )
type ProgressBarStyle struct { type ProgressBarStyle struct {
Color color.RGBA Color color.RGBA
Progress int
} }
func ProgressBar(th *Theme) ProgressBarStyle { func ProgressBar(th *Theme, progress int) ProgressBarStyle {
return ProgressBarStyle{ return ProgressBarStyle{
Color: th.Color.Primary, Progress: progress,
Color: th.Color.Primary,
} }
} }
func (p ProgressBarStyle) Layout(gtx layout.Context, progress int) layout.Dimensions { func (p ProgressBarStyle) Layout(gtx layout.Context) layout.Dimensions {
shader := func(width float32, color color.RGBA) layout.Dimensions { shader := func(width float32, color color.RGBA) layout.Dimensions {
maxHeight := unit.Dp(4) maxHeight := unit.Dp(4)
rr := float32(gtx.Px(unit.Dp(2))) rr := float32(gtx.Px(unit.Dp(2)))
@@ -44,6 +46,7 @@ func (p ProgressBarStyle) Layout(gtx layout.Context, progress int) layout.Dimens
return layout.Dimensions{Size: d} return layout.Dimensions{Size: d}
} }
progress := p.Progress
if progress > 100 { if progress > 100 {
progress = 100 progress = 100
} else if progress < 0 { } else if progress < 0 {
+8 -6
View File
@@ -10,13 +10,15 @@ import (
type RadioButtonStyle struct { type RadioButtonStyle struct {
checkable checkable
Key string Key string
Group *widget.Enum
} }
// RadioButton returns a RadioButton with a label. The key specifies // RadioButton returns a RadioButton with a label. The key specifies
// the value for the Enum. // the value for the Enum.
func RadioButton(th *Theme, key, label string) RadioButtonStyle { func RadioButton(th *Theme, group *widget.Enum, key, label string) RadioButtonStyle {
return RadioButtonStyle{ return RadioButtonStyle{
Group: group,
checkable: checkable{ checkable: checkable{
Label: label, Label: label,
@@ -33,9 +35,9 @@ func RadioButton(th *Theme, key, label string) RadioButtonStyle {
} }
// Layout updates enum and displays the radio button. // Layout updates enum and displays the radio button.
func (r RadioButtonStyle) Layout(gtx layout.Context, enum *widget.Enum) layout.Dimensions { func (r RadioButtonStyle) Layout(gtx layout.Context) layout.Dimensions {
enum.Update(gtx) r.Group.Update(gtx)
dims := r.layout(gtx, enum.Value == r.Key) dims := r.layout(gtx, r.Group.Value == r.Key)
enum.Layout(gtx, r.Key) r.Group.Layout(gtx, r.Key)
return dims return dims
} }
+10 -8
View File
@@ -17,18 +17,20 @@ import (
) )
type SwitchStyle struct { type SwitchStyle struct {
Color color.RGBA Color color.RGBA
Switch *widget.Bool
} }
func Switch(th *Theme) SwitchStyle { func Switch(th *Theme, swtch *widget.Bool) SwitchStyle {
return SwitchStyle{ return SwitchStyle{
Color: th.Color.Primary, Switch: swtch,
Color: th.Color.Primary,
} }
} }
// Layout updates the checkBox and displays it. // Layout updates the checkBox and displays it.
func (s SwitchStyle) Layout(gtx layout.Context, swtch *widget.Bool) layout.Dimensions { func (s SwitchStyle) Layout(gtx layout.Context) layout.Dimensions {
swtch.Update(gtx) s.Switch.Update(gtx)
trackWidth := gtx.Px(unit.Dp(36)) trackWidth := gtx.Px(unit.Dp(36))
trackHeight := gtx.Px(unit.Dp(16)) trackHeight := gtx.Px(unit.Dp(16))
@@ -55,7 +57,7 @@ func (s SwitchStyle) Layout(gtx layout.Context, swtch *widget.Bool) layout.Dimen
// Compute thumb offset and color. // Compute thumb offset and color.
stack.Push(gtx.Ops) stack.Push(gtx.Ops)
col := rgb(0xffffff) col := rgb(0xffffff)
if swtch.Value { if s.Switch.Value {
off := trackWidth - thumbSize off := trackWidth - thumbSize
op.TransformOp{}.Offset(f32.Point{X: float32(off)}).Add(gtx.Ops) op.TransformOp{}.Offset(f32.Point{X: float32(off)}).Add(gtx.Ops)
col = s.Color col = s.Color
@@ -93,7 +95,7 @@ func (s SwitchStyle) Layout(gtx layout.Context, swtch *widget.Bool) layout.Dimen
}, },
NE: rr, NW: rr, SE: rr, SW: rr, NE: rr, NW: rr, SE: rr, SW: rr,
}.Op(gtx.Ops).Add(gtx.Ops) }.Op(gtx.Ops).Add(gtx.Ops)
drawInk(gtx, swtch.Last) drawInk(gtx, s.Switch.Last)
stack.Pop() stack.Pop()
// Set up click area. // Set up click area.
@@ -109,7 +111,7 @@ func (s SwitchStyle) Layout(gtx layout.Context, swtch *widget.Bool) layout.Dimen
X: clickSize, Y: clickSize, X: clickSize, Y: clickSize,
}, },
}).Add(gtx.Ops) }).Add(gtx.Ops)
swtch.Layout(gtx) s.Switch.Layout(gtx)
stack.Pop() stack.Pop()
return layout.Dimensions{ return layout.Dimensions{