Files
gio-patched/widget/enum.go
T
Elias Naur ed0d5d5767 all: [API] deliver key events to the first matching filter
Replace the key.Filter.Target field with a Focus field that matches only
of the specified tag has focus. This has the advantage of simpler event
delivery and for lower latency in delivering key events to new handlers.

For example, consider a UI where a button is activated by a key press,
which is turn displays a dialog with another button activated by the
same key. This change allows two button press(+releases) in the same frame
to arrive at the intended targets: one key press(+release) for each
button.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
2024-02-05 11:09:36 +00:00

140 lines
2.7 KiB
Go

// SPDX-License-Identifier: Unlicense OR MIT
package widget
import (
"gioui.org/gesture"
"gioui.org/io/event"
"gioui.org/io/key"
"gioui.org/io/pointer"
"gioui.org/io/semantic"
"gioui.org/layout"
"gioui.org/op"
"gioui.org/op/clip"
)
type Enum struct {
Value string
hovered string
hovering bool
focus string
focused bool
keys []*enumKey
}
type enumKey struct {
key string
click gesture.Click
tag struct{}
}
func (e *Enum) index(k string) *enumKey {
for _, v := range e.keys {
if v.key == k {
return v
}
}
return nil
}
// Update the state and report whether Value has changed by user interaction.
func (e *Enum) Update(gtx layout.Context) bool {
if !gtx.Enabled() {
e.focused = false
}
e.hovering = false
changed := false
for _, state := range e.keys {
for {
ev, ok := state.click.Update(gtx.Source)
if !ok {
break
}
switch ev.Kind {
case gesture.KindPress:
if ev.Source == pointer.Mouse {
gtx.Execute(key.FocusCmd{Tag: &state.tag})
}
case gesture.KindClick:
if state.key != e.Value {
e.Value = state.key
changed = true
}
}
}
for {
ev, ok := gtx.Event(
key.FocusFilter{Target: &state.tag},
key.Filter{Focus: &state.tag, Name: key.NameReturn},
key.Filter{Focus: &state.tag, Name: key.NameSpace},
)
if !ok {
break
}
switch ev := ev.(type) {
case key.FocusEvent:
if ev.Focus {
e.focused = true
e.focus = state.key
} else if state.key == e.focus {
e.focused = false
}
case key.Event:
if ev.State != key.Release {
break
}
if ev.Name != key.NameReturn && ev.Name != key.NameSpace {
break
}
if state.key != e.Value {
e.Value = state.key
changed = true
}
}
}
if state.click.Hovered() {
e.hovered = state.key
e.hovering = true
}
}
return changed
}
// Hovered returns the key that is highlighted, or false if none are.
func (e *Enum) Hovered() (string, bool) {
return e.hovered, e.hovering
}
// Focused reports the focused key, or false if no key is focused.
func (e *Enum) Focused() (string, bool) {
return e.focus, e.focused
}
// Layout adds the event handler for the key k.
func (e *Enum) Layout(gtx layout.Context, k string, content layout.Widget) layout.Dimensions {
e.Update(gtx)
m := op.Record(gtx.Ops)
dims := content(gtx)
c := m.Stop()
defer clip.Rect{Max: dims.Size}.Push(gtx.Ops).Pop()
state := e.index(k)
if state == nil {
state = &enumKey{
key: k,
}
e.keys = append(e.keys, state)
}
clk := &state.click
clk.Add(gtx.Ops)
event.InputOp(gtx.Ops, &state.tag)
semantic.SelectedOp(k == e.Value).Add(gtx.Ops)
semantic.EnabledOp(gtx.Enabled()).Add(gtx.Ops)
c.Add(gtx.Ops)
return dims
}