enum.go 2.7 KB

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