gioui.org@v0.6.1-0.20240506124620-7a9ce51988ce/widget/enum.go (about) 1 // SPDX-License-Identifier: Unlicense OR MIT 2 3 package widget 4 5 import ( 6 "gioui.org/gesture" 7 "gioui.org/io/event" 8 "gioui.org/io/key" 9 "gioui.org/io/pointer" 10 "gioui.org/io/semantic" 11 "gioui.org/layout" 12 "gioui.org/op" 13 "gioui.org/op/clip" 14 ) 15 16 type Enum struct { 17 Value string 18 hovered string 19 hovering bool 20 21 focus string 22 focused bool 23 24 keys []*enumKey 25 } 26 27 type enumKey struct { 28 key string 29 click gesture.Click 30 tag struct{} 31 } 32 33 func (e *Enum) index(k string) *enumKey { 34 for _, v := range e.keys { 35 if v.key == k { 36 return v 37 } 38 } 39 return nil 40 } 41 42 // Update the state and report whether Value has changed by user interaction. 43 func (e *Enum) Update(gtx layout.Context) bool { 44 if !gtx.Enabled() { 45 e.focused = false 46 } 47 e.hovering = false 48 changed := false 49 for _, state := range e.keys { 50 for { 51 ev, ok := state.click.Update(gtx.Source) 52 if !ok { 53 break 54 } 55 switch ev.Kind { 56 case gesture.KindPress: 57 if ev.Source == pointer.Mouse { 58 gtx.Execute(key.FocusCmd{Tag: &state.tag}) 59 } 60 case gesture.KindClick: 61 if state.key != e.Value { 62 e.Value = state.key 63 changed = true 64 } 65 } 66 } 67 for { 68 ev, ok := gtx.Event( 69 key.FocusFilter{Target: &state.tag}, 70 key.Filter{Focus: &state.tag, Name: key.NameReturn}, 71 key.Filter{Focus: &state.tag, Name: key.NameSpace}, 72 ) 73 if !ok { 74 break 75 } 76 switch ev := ev.(type) { 77 case key.FocusEvent: 78 if ev.Focus { 79 e.focused = true 80 e.focus = state.key 81 } else if state.key == e.focus { 82 e.focused = false 83 } 84 case key.Event: 85 if ev.State != key.Release { 86 break 87 } 88 if ev.Name != key.NameReturn && ev.Name != key.NameSpace { 89 break 90 } 91 if state.key != e.Value { 92 e.Value = state.key 93 changed = true 94 } 95 } 96 } 97 if state.click.Hovered() { 98 e.hovered = state.key 99 e.hovering = true 100 } 101 } 102 103 return changed 104 } 105 106 // Hovered returns the key that is highlighted, or false if none are. 107 func (e *Enum) Hovered() (string, bool) { 108 return e.hovered, e.hovering 109 } 110 111 // Focused reports the focused key, or false if no key is focused. 112 func (e *Enum) Focused() (string, bool) { 113 return e.focus, e.focused 114 } 115 116 // Layout adds the event handler for the key k. 117 func (e *Enum) Layout(gtx layout.Context, k string, content layout.Widget) layout.Dimensions { 118 e.Update(gtx) 119 m := op.Record(gtx.Ops) 120 dims := content(gtx) 121 c := m.Stop() 122 defer clip.Rect{Max: dims.Size}.Push(gtx.Ops).Pop() 123 124 state := e.index(k) 125 if state == nil { 126 state = &enumKey{ 127 key: k, 128 } 129 e.keys = append(e.keys, state) 130 } 131 clk := &state.click 132 clk.Add(gtx.Ops) 133 event.Op(gtx.Ops, &state.tag) 134 semantic.SelectedOp(k == e.Value).Add(gtx.Ops) 135 semantic.EnabledOp(gtx.Enabled()).Add(gtx.Ops) 136 c.Add(gtx.Ops) 137 138 return dims 139 }