Alexander Arin: 1 widgets, widgets/material: add RadioButton & RadioButtonsGroup 6 files changed, 198 insertions(+), 73 deletions(-)
Copy & paste the following snippet into your terminal to import this patchset into git:
curl -s https://lists.sr.ht/~eliasnaur/gio/patches/8986/mbox | git am -3Learn more about email & git
Signed-off-by: Alexander Arin <fralx@yandex.ru> --- example/kitchen/kitchen.go | 22 ++++++++-- widget/enum.go | 51 ++++++++++++++++++++++ widget/material/checkbox.go | 79 ++++++---------------------------- widget/material/chekable.go | 65 ++++++++++++++++++++++++++++ widget/material/material.go | 12 ++++-- widget/material/radiobutton.go | 42 ++++++++++++++++++ 6 files changed, 198 insertions(+), 73 deletions(-) create mode 100644 widget/enum.go create mode 100644 widget/material/chekable.go create mode 100644 widget/material/radiobutton.go diff --git a/example/kitchen/kitchen.go b/example/kitchen/kitchen.go index e390761..486924e 100644 --- a/example/kitchen/kitchen.go +++ b/example/kitchen/kitchen.go @@ -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]) diff --git a/widget/enum.go b/widget/enum.go new file mode 100644 index 0000000..63e35cb --- /dev/null +++ b/widget/enum.go @@ -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 +} diff --git a/widget/material/checkbox.go b/widget/material/checkbox.go index 1648891..4d1537d 100644 --- a/widget/material/checkbox.go +++ b/widget/material/checkbox.go @@ -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) } diff --git a/widget/material/chekable.go b/widget/material/chekable.go new file mode 100644 index 0000000..278b8d3 --- /dev/null +++ b/widget/material/chekable.go @@ -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) +} diff --git a/widget/material/material.go b/widget/material/material.go index d5b70db..1b73af0 100644 --- a/widget/material/material.go +++ b/widget/material/material.go @@ -26,8 +26,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 { @@ -40,8 +42,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 } diff --git a/widget/material/radiobutton.go b/widget/material/radiobutton.go new file mode 100644 index 0000000..9a9a008 --- /dev/null +++ b/widget/material/radiobutton.go @@ -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) +} -- 2.18.0.windows.1