~eliasnaur/gio

2451750782b86e020be67be715178c7a163e1d3f — Elias Naur 6 months ago 3af01a3
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>
M widget/material/button.go => widget/material/button.go +20 -13
@@ 26,6 26,7 @@ type ButtonStyle struct {
	Background   color.RGBA
	CornerRadius unit.Value
	Inset        layout.Inset
	Button       *widget.Clickable
	shaper       text.Shaper
}



@@ 33,6 34,7 @@ type ButtonLayoutStyle struct {
	Background   color.RGBA
	CornerRadius unit.Value
	Inset        layout.Inset
	Button       *widget.Clickable
}

type IconButtonStyle struct {


@@ 41,11 43,12 @@ type IconButtonStyle struct {
	Color color.RGBA
	Icon  *widget.Icon
	// Size is the icon size.
	Size  unit.Value
	Inset layout.Inset
	Size   unit.Value
	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{
		Text:         txt,
		Color:        rgb(0xffffff),


@@ 56,25 59,28 @@ func Button(th *Theme, txt string) ButtonStyle {
			Top: unit.Dp(10), Bottom: unit.Dp(10),
			Left: unit.Dp(12), Right: unit.Dp(12),
		},
		Button: button,
		shaper: th.Shaper,
	}
}

func ButtonLayout(th *Theme) ButtonLayoutStyle {
func ButtonLayout(th *Theme, button *widget.Clickable) ButtonLayoutStyle {
	return ButtonLayoutStyle{
		Button:       button,
		Background:   th.Color.Primary,
		CornerRadius: unit.Dp(4),
		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{
		Background: th.Color.Primary,
		Color:      th.Color.InvText,
		Icon:       icon,
		Size:       unit.Dp(24),
		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{
		Background:   b.Background,
		CornerRadius: b.CornerRadius,
		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)
		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
	return layout.Stack{Alignment: layout.Center}.Layout(gtx,
		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,
			}.Op(gtx.Ops).Add(gtx.Ops)
			dims := fill(gtx, b.Background)
			for _, c := range button.History() {
			for _, c := range b.Button.History() {
				drawInk(gtx, c)
			}
			return dims


@@ 134,11 141,11 @@ func (b ButtonLayoutStyle) Layout(gtx layout.Context, button *widget.Clickable, 
				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,
		layout.Expanded(func(gtx layout.Context) layout.Dimensions {
			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,
			}.Op(gtx.Ops).Add(gtx.Ops)
			dims := fill(gtx, b.Background)
			for _, c := range button.History() {
			for _, c := range b.Button.History() {
				drawInk(gtx, c)
			}
			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 {
			pointer.Ellipse(image.Rectangle{Max: gtx.Constraints.Min}).Add(gtx.Ops)
			return button.Layout(gtx)
			return b.Button.Layout(gtx)
		}),
	)
}

M widget/material/checkbox.go => widget/material/checkbox.go +8 -6
@@ 10,11 10,13 @@ import (

type CheckBoxStyle struct {
	checkable
	CheckBox *widget.Bool
}

func CheckBox(th *Theme, label string) CheckBoxStyle {
func CheckBox(th *Theme, checkBox *widget.Bool, label string) CheckBoxStyle {
	return CheckBoxStyle{
		checkable{
		CheckBox: checkBox,
		checkable: checkable{
			Label:              label,
			Color:              th.Color.Text,
			IconColor:          th.Color.Primary,


@@ 28,9 30,9 @@ func CheckBox(th *Theme, label string) CheckBoxStyle {
}

// Layout updates the checkBox and displays it.
func (c CheckBoxStyle) Layout(gtx layout.Context, checkBox *widget.Bool) layout.Dimensions {
	checkBox.Update(gtx)
	dims := c.layout(gtx, checkBox.Value)
	checkBox.Layout(gtx)
func (c CheckBoxStyle) Layout(gtx layout.Context) layout.Dimensions {
	c.CheckBox.Update(gtx)
	dims := c.layout(gtx, c.CheckBox.Value)
	c.CheckBox.Layout(gtx)
	return dims
}

M widget/material/editor.go => widget/material/editor.go +9 -7
@@ 22,12 22,14 @@ type EditorStyle struct {
	Hint string
	// HintColor is the color of hint text.
	HintColor color.RGBA
	Editor    *widget.Editor

	shaper text.Shaper
}

func Editor(th *Theme, hint string) EditorStyle {
func Editor(th *Theme, editor *widget.Editor, hint string) EditorStyle {
	return EditorStyle{
		Editor:    editor,
		TextSize:  th.TextSize,
		Color:     th.Color.Text,
		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
	stack.Push(gtx.Ops)
	var macro op.MacroOp
	macro.Record(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)
	macro.Stop()
	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 {
		gtx.Constraints.Min.Y = h
	}
	dims = editor.Layout(gtx, e.shaper, e.Font, e.TextSize)
	if editor.Len() > 0 {
	dims = e.Editor.Layout(gtx, e.shaper, e.Font, e.TextSize)
	if e.Editor.Len() > 0 {
		paint.ColorOp{Color: e.Color}.Add(gtx.Ops)
		editor.PaintText(gtx)
		e.Editor.PaintText(gtx)
	} else {
		macro.Add()
	}
	paint.ColorOp{Color: e.Color}.Add(gtx.Ops)
	editor.PaintCaret(gtx)
	e.Editor.PaintCaret(gtx)
	stack.Pop()
	return dims
}

M widget/material/progressbar.go => widget/material/progressbar.go +7 -4
@@ 14,16 14,18 @@ import (
)

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{
		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 {
		maxHeight := unit.Dp(4)
		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}
	}

	progress := p.Progress
	if progress > 100 {
		progress = 100
	} else if progress < 0 {

M widget/material/radiobutton.go => widget/material/radiobutton.go +8 -6
@@ 10,13 10,15 @@ import (

type RadioButtonStyle struct {
	checkable
	Key string
	Key   string
	Group *widget.Enum
}

// RadioButton returns a RadioButton with a label. The key specifies
// 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{
		Group: group,
		checkable: checkable{
			Label: label,



@@ 33,9 35,9 @@ func RadioButton(th *Theme, key, label string) RadioButtonStyle {
}

// Layout updates enum and displays the radio button.
func (r RadioButtonStyle) Layout(gtx layout.Context, enum *widget.Enum) layout.Dimensions {
	enum.Update(gtx)
	dims := r.layout(gtx, enum.Value == r.Key)
	enum.Layout(gtx, r.Key)
func (r RadioButtonStyle) Layout(gtx layout.Context) layout.Dimensions {
	r.Group.Update(gtx)
	dims := r.layout(gtx, r.Group.Value == r.Key)
	r.Group.Layout(gtx, r.Key)
	return dims
}

M widget/material/switch.go => widget/material/switch.go +10 -8
@@ 17,18 17,20 @@ import (
)

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{
		Color: th.Color.Primary,
		Switch: swtch,
		Color:  th.Color.Primary,
	}
}

// Layout updates the checkBox and displays it.
func (s SwitchStyle) Layout(gtx layout.Context, swtch *widget.Bool) layout.Dimensions {
	swtch.Update(gtx)
func (s SwitchStyle) Layout(gtx layout.Context) layout.Dimensions {
	s.Switch.Update(gtx)

	trackWidth := gtx.Px(unit.Dp(36))
	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.
	stack.Push(gtx.Ops)
	col := rgb(0xffffff)
	if swtch.Value {
	if s.Switch.Value {
		off := trackWidth - thumbSize
		op.TransformOp{}.Offset(f32.Point{X: float32(off)}).Add(gtx.Ops)
		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,
	}.Op(gtx.Ops).Add(gtx.Ops)
	drawInk(gtx, swtch.Last)
	drawInk(gtx, s.Switch.Last)
	stack.Pop()

	// Set up click area.


@@ 109,7 111,7 @@ func (s SwitchStyle) Layout(gtx layout.Context, swtch *widget.Bool) layout.Dimen
			X: clickSize, Y: clickSize,
		},
	}).Add(gtx.Ops)
	swtch.Layout(gtx)
	s.Switch.Layout(gtx)
	stack.Pop()

	return layout.Dimensions{