~eliasnaur/gio

089ae31f0cf8c3bfb2b1edcda8103b6c1f75f4a9 — Alexander Arin 1 year, 8 months ago 82e5101
widgets, widgets/material: add RadioButton & Enum

Signed-off-by: Alexander Arin <fralx@yandex.ru>
M example/kitchen/kitchen.go => example/kitchen/kitchen.go +18 -4
@@ 62,10 62,11 @@ var (
		SingleLine: true,
		Submit:     true,
	}
	button      = new(widget.Button)
	greenButton = new(widget.Button)
	iconButton  = new(widget.Button)
	list        = &layout.List{
	button            = new(widget.Button)
	greenButton       = new(widget.Button)
	iconButton        = new(widget.Button)
	radioButtonsGroup = new(widget.Enum)
	list              = &layout.List{
		Axis: layout.Vertical,
	}
	green    = true


@@ 125,6 126,19 @@ func kitchen(gtx *layout.Context, th *material.Theme) {
		func() {
			th.CheckBox("Checkbox").Layout(gtx, checkbox)
		},
		func() {
			group := layout.Flex{}
			r1 := group.Rigid(gtx, func() {
				th.RadioButton("r1", "RadioButton1").Layout(gtx, radioButtonsGroup)
			})
			r2 := group.Rigid(gtx, func() {
				th.RadioButton("r2", "RadioButton2").Layout(gtx, radioButtonsGroup)
			})
			r3 := group.Rigid(gtx, func() {
				th.RadioButton("r3", "RadioButton3").Layout(gtx, radioButtonsGroup)
			})
			group.Layout(gtx, r1, r2, r3)
		},
	}
	list.Layout(gtx, len(widgets), func(i int) {
		layout.UniformInset(unit.Dp(16)).Layout(gtx, widgets[i])

A widget/enum.go => widget/enum.go +51 -0
@@ 0,0 1,51 @@
package widget

import (
	"gioui.org/gesture"
	"gioui.org/layout"
)

type Enum struct {
	clicks []gesture.Click
	values []string
	value  string
}

func index(vs []string, t string) int {
	for i, v := range vs {
		if v == t {
			return i
		}
	}
	return -1
}

// Value processes events and returns the last selected value, or
// the empty string.
func (e *Enum) Value(gtx *layout.Context) string {
	for i := range e.clicks {
		for _, ev := range e.clicks[i].Events(gtx) {
			switch ev.Type {
			case gesture.TypeClick:
				e.value = e.values[i]
			}
		}
	}
	return e.value
}

// Layout adds the event handler for key.
func (rg *Enum) Layout(gtx *layout.Context, key string) {
	if index(rg.values, key) == -1 {
		rg.values = append(rg.values, key)
		rg.clicks = append(rg.clicks, gesture.Click{})
		rg.clicks[len(rg.clicks)-1].Add(gtx.Ops)
	} else {
		idx := index(rg.values, key)
		rg.clicks[idx].Add(gtx.Ops)
	}
}

func (rg *Enum) SetValue(value string) {
	rg.value = value
}

M widget/material/checkbox.go => widget/material/checkbox.go +14 -65
@@ 4,85 4,34 @@
package material

import (
	"gioui.org/io/pointer"
	"gioui.org/layout"
	"gioui.org/op/paint"
	"gioui.org/text"
	"gioui.org/unit"
	"gioui.org/widget"
	"image"
	"image/color"
)

type CheckBox struct {
	Text string
	// Color is the text color.
	Color              color.RGBA
	Font               text.Font
	IconColor          color.RGBA
	Size               unit.Value
	shaper             *text.Shaper
	checkedStateIcon   *Icon
	uncheckedStateIcon *Icon
	checkable
}

func (t *Theme) CheckBox(txt string) CheckBox {
func (t *Theme) CheckBox(label string) CheckBox {
	return CheckBox{
		Text:      txt,
		Color:     t.Color.Text,
		IconColor: t.Color.Primary,
		Font: text.Font{
			Size: t.TextSize.Scale(14.0 / 16.0),
		checkable{
			Label:     label,
			Color:     t.Color.Text,
			IconColor: t.Color.Primary,
			Font: text.Font{
				Size: t.TextSize.Scale(14.0 / 16.0),
			},
			Size:               unit.Dp(26),
			shaper:             t.Shaper,
			checkedStateIcon:   t.checkBoxCheckedIcon,
			uncheckedStateIcon: t.checkBoxUncheckedIcon,
		},
		Size:               unit.Dp(26),
		shaper:             t.Shaper,
		checkedStateIcon:   t.checkedStateIcon,
		uncheckedStateIcon: t.uncheckedStateIcon,
	}
}

func (c CheckBox) Layout(gtx *layout.Context, checkBox *widget.CheckBox) {

	textColor := c.Color
	iconColor := c.IconColor

	var icon *Icon
	if checkBox.Checked(gtx) {
		icon = c.checkedStateIcon
	} else {
		icon = c.uncheckedStateIcon
	}

	hmin := gtx.Constraints.Width.Min
	vmin := gtx.Constraints.Height.Min

	flex := layout.Flex{Alignment: layout.Middle}

	ico := flex.Rigid(gtx, func() {
		layout.Align(layout.Center).Layout(gtx, func() {
			layout.UniformInset(unit.Dp(2)).Layout(gtx, func() {
				size := gtx.Px(c.Size)
				icon.Color = iconColor
				icon.Layout(gtx, unit.Px(float32(size)))
				gtx.Dimensions = layout.Dimensions{
					Size: image.Point{X: size, Y: size},
				}
			})
		})
	})

	lbl := flex.Rigid(gtx, func() {
		gtx.Constraints.Width.Min = hmin
		gtx.Constraints.Height.Min = vmin
		layout.Align(layout.Start).Layout(gtx, func() {
			layout.UniformInset(unit.Dp(2)).Layout(gtx, func() {
				paint.ColorOp{Color: textColor}.Add(gtx.Ops)
				widget.Label{}.Layout(gtx, c.shaper, c.Font, c.Text)
			})
		})
	})

	flex.Layout(gtx, ico, lbl)
	pointer.RectAreaOp{Rect: image.Rectangle{Max: gtx.Dimensions.Size}}.Add(gtx.Ops)
	c.layout(gtx, checkBox.Checked(gtx))
	checkBox.Layout(gtx)
}

A widget/material/chekable.go => widget/material/chekable.go +65 -0
@@ 0,0 1,65 @@
package material

import (
	"image"
	"image/color"

	"gioui.org/io/pointer"
	"gioui.org/layout"
	"gioui.org/op/paint"
	"gioui.org/text"
	"gioui.org/unit"
	"gioui.org/widget"
)

type checkable struct {
	Label              string
	Color              color.RGBA
	Font               text.Font
	IconColor          color.RGBA
	Size               unit.Value
	shaper             *text.Shaper
	checkedStateIcon   *Icon
	uncheckedStateIcon *Icon
}

func (c *checkable) layout(gtx *layout.Context, checked bool) {

	var icon *Icon
	if checked {
		icon = c.checkedStateIcon
	} else {
		icon = c.uncheckedStateIcon
	}

	hmin := gtx.Constraints.Width.Min
	vmin := gtx.Constraints.Height.Min
	flex := layout.Flex{Alignment: layout.Middle}

	ico := flex.Rigid(gtx, func() {
		layout.Align(layout.Center).Layout(gtx, func() {
			layout.UniformInset(unit.Dp(2)).Layout(gtx, func() {
				size := gtx.Px(c.Size)
				icon.Color = c.IconColor
				icon.Layout(gtx, unit.Px(float32(size)))
				gtx.Dimensions = layout.Dimensions{
					Size: image.Point{X: size, Y: size},
				}
			})
		})
	})

	lbl := flex.Rigid(gtx, func() {
		gtx.Constraints.Width.Min = hmin
		gtx.Constraints.Height.Min = vmin
		layout.Align(layout.Start).Layout(gtx, func() {
			layout.UniformInset(unit.Dp(2)).Layout(gtx, func() {
				paint.ColorOp{Color: c.Color}.Add(gtx.Ops)
				widget.Label{}.Layout(gtx, c.shaper, c.Font, c.Label)
			})
		})
	})

	flex.Layout(gtx, ico, lbl)
	pointer.RectAreaOp{Rect: image.Rectangle{Max: gtx.Dimensions.Size}}.Add(gtx.Ops)
}

M widget/material/material.go => widget/material/material.go +8 -4
@@ 25,8 25,10 @@ type Theme struct {
		InvText color.RGBA
	}
	TextSize           unit.Value
	checkedStateIcon   *Icon
	uncheckedStateIcon *Icon
	checkBoxCheckedIcon   *Icon
	checkBoxUncheckedIcon *Icon
	radioCheckedIcon   *Icon
	radioUncheckedIcon *Icon
}

func NewTheme() *Theme {


@@ 39,8 41,10 @@ func NewTheme() *Theme {
	t.Color.InvText = rgb(0xffffff)
	t.TextSize = unit.Sp(16)

	t.checkedStateIcon = mustIcon(NewIcon(icons.ToggleCheckBox))
	t.uncheckedStateIcon = mustIcon(NewIcon(icons.ToggleCheckBoxOutlineBlank))
	t.checkBoxCheckedIcon = mustIcon(NewIcon(icons.ToggleCheckBox))
	t.checkBoxUncheckedIcon = mustIcon(NewIcon(icons.ToggleCheckBoxOutlineBlank))
	t.radioCheckedIcon = mustIcon(NewIcon(icons.ToggleRadioButtonChecked))
	t.radioUncheckedIcon = mustIcon(NewIcon(icons.ToggleRadioButtonUnchecked))

	return t
}

A widget/material/radiobutton.go => widget/material/radiobutton.go +42 -0
@@ 0,0 1,42 @@
// SPDX-License-Identifier: Unlicense OR MIT

// Package material implements the Material design.
package material

import (
	"gioui.org/layout"
	"gioui.org/text"
	"gioui.org/unit"
	"gioui.org/widget"
)

type RadioButton struct {
	checkable
	Key string
}

// RadioButton returns a RadioButton with a label. The key specifies
// the value for the Enum.
func (t *Theme) RadioButton(key, label string) RadioButton {
	return RadioButton{
		checkable: checkable{
			Label: label,

			Color:     t.Color.Text,
			IconColor: t.Color.Primary,
			Font: text.Font{
				Size: t.TextSize.Scale(14.0 / 16.0),
			},
			Size:               unit.Dp(26),
			shaper:             t.Shaper,
			checkedStateIcon:   t.radioCheckedIcon,
			uncheckedStateIcon: t.radioUncheckedIcon,
		},
		Key: key,
	}
}

func (r RadioButton) Layout(gtx *layout.Context, enum *widget.Enum) {
	r.layout(gtx, enum.Value(gtx) == r.Key)
	enum.Layout(gtx, r.Key)
}