key.go 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. // SPDX-License-Identifier: Unlicense OR MIT
  2. package input
  3. import (
  4. "image"
  5. "sort"
  6. "gioui.org/f32"
  7. "gioui.org/io/event"
  8. "gioui.org/io/key"
  9. )
  10. // EditorState represents the state of an editor needed by input handlers.
  11. type EditorState struct {
  12. Selection struct {
  13. Transform f32.Affine2D
  14. key.Range
  15. key.Caret
  16. }
  17. Snippet key.Snippet
  18. }
  19. type TextInputState uint8
  20. type keyQueue struct {
  21. order []event.Tag
  22. dirOrder []dirFocusEntry
  23. hint key.InputHint
  24. }
  25. // keyState is the input state related to key events.
  26. type keyState struct {
  27. focus event.Tag
  28. state TextInputState
  29. content EditorState
  30. }
  31. type keyHandler struct {
  32. // visible will be true if the InputOp is present
  33. // in the current frame.
  34. visible bool
  35. // reset tracks whether the handler has seen a
  36. // focus reset.
  37. reset bool
  38. hint key.InputHint
  39. orderPlusOne int
  40. dirOrder int
  41. trans f32.Affine2D
  42. }
  43. type keyFilter []key.Filter
  44. type dirFocusEntry struct {
  45. tag event.Tag
  46. row int
  47. area int
  48. bounds image.Rectangle
  49. }
  50. const (
  51. TextInputKeep TextInputState = iota
  52. TextInputClose
  53. TextInputOpen
  54. )
  55. func (k *keyHandler) inputHint(hint key.InputHint) {
  56. k.hint = hint
  57. }
  58. // InputState returns the input state and returns a state
  59. // reset to [TextInputKeep].
  60. func (s keyState) InputState() (keyState, TextInputState) {
  61. state := s.state
  62. s.state = TextInputKeep
  63. return s, state
  64. }
  65. // InputHint returns the input hint from the focused handler and whether it was
  66. // changed since the last call.
  67. func (q *keyQueue) InputHint(handlers map[event.Tag]*handler, state keyState) (key.InputHint, bool) {
  68. focused, ok := handlers[state.focus]
  69. if !ok {
  70. return q.hint, false
  71. }
  72. old := q.hint
  73. q.hint = focused.key.hint
  74. return q.hint, old != q.hint
  75. }
  76. func (k *keyHandler) Reset() {
  77. k.visible = false
  78. k.orderPlusOne = 0
  79. k.hint = key.HintAny
  80. }
  81. func (q *keyQueue) Reset() {
  82. q.order = q.order[:0]
  83. q.dirOrder = q.dirOrder[:0]
  84. }
  85. func (k *keyHandler) ResetEvent() (event.Event, bool) {
  86. if k.reset {
  87. return nil, false
  88. }
  89. k.reset = true
  90. return key.FocusEvent{Focus: false}, true
  91. }
  92. func (q *keyQueue) Frame(handlers map[event.Tag]*handler, state keyState) keyState {
  93. if state.focus != nil {
  94. if h, ok := handlers[state.focus]; !ok || !h.filter.focusable || !h.key.visible {
  95. // Remove focus from the handler that is no longer focusable.
  96. state.focus = nil
  97. state.state = TextInputClose
  98. }
  99. }
  100. q.updateFocusLayout(handlers)
  101. return state
  102. }
  103. // updateFocusLayout partitions input handlers handlers into rows
  104. // for directional focus moves.
  105. //
  106. // The approach is greedy: pick the topmost handler and create a row
  107. // containing it. Then, extend the handler bounds to a horizontal beam
  108. // and add to the row every handler whose center intersect it. Repeat
  109. // until no handlers remain.
  110. func (q *keyQueue) updateFocusLayout(handlers map[event.Tag]*handler) {
  111. order := q.dirOrder
  112. // Sort by ascending y position.
  113. sort.SliceStable(order, func(i, j int) bool {
  114. return order[i].bounds.Min.Y < order[j].bounds.Min.Y
  115. })
  116. row := 0
  117. for len(order) > 0 {
  118. h := &order[0]
  119. h.row = row
  120. bottom := h.bounds.Max.Y
  121. end := 1
  122. for ; end < len(order); end++ {
  123. h := &order[end]
  124. center := (h.bounds.Min.Y + h.bounds.Max.Y) / 2
  125. if center > bottom {
  126. break
  127. }
  128. h.row = row
  129. }
  130. // Sort row by ascending x position.
  131. sort.SliceStable(order[:end], func(i, j int) bool {
  132. return order[i].bounds.Min.X < order[j].bounds.Min.X
  133. })
  134. order = order[end:]
  135. row++
  136. }
  137. for i, o := range q.dirOrder {
  138. handlers[o.tag].key.dirOrder = i
  139. }
  140. }
  141. // MoveFocus attempts to move the focus in the direction of dir.
  142. func (q *keyQueue) MoveFocus(handlers map[event.Tag]*handler, state keyState, dir key.FocusDirection) (keyState, []taggedEvent) {
  143. if len(q.dirOrder) == 0 {
  144. return state, nil
  145. }
  146. order := 0
  147. if state.focus != nil {
  148. order = handlers[state.focus].key.dirOrder
  149. }
  150. focus := q.dirOrder[order]
  151. switch dir {
  152. case key.FocusForward, key.FocusBackward:
  153. if len(q.order) == 0 {
  154. break
  155. }
  156. order := 0
  157. if dir == key.FocusBackward {
  158. order = -1
  159. }
  160. if state.focus != nil {
  161. order = handlers[state.focus].key.orderPlusOne - 1
  162. if dir == key.FocusForward {
  163. order++
  164. } else {
  165. order--
  166. }
  167. }
  168. order = (order + len(q.order)) % len(q.order)
  169. return q.Focus(handlers, state, q.order[order])
  170. case key.FocusRight, key.FocusLeft:
  171. next := order
  172. if state.focus != nil {
  173. next = order + 1
  174. if dir == key.FocusLeft {
  175. next = order - 1
  176. }
  177. }
  178. if 0 <= next && next < len(q.dirOrder) {
  179. newFocus := q.dirOrder[next]
  180. if newFocus.row == focus.row {
  181. return q.Focus(handlers, state, newFocus.tag)
  182. }
  183. }
  184. case key.FocusUp, key.FocusDown:
  185. delta := +1
  186. if dir == key.FocusUp {
  187. delta = -1
  188. }
  189. nextRow := 0
  190. if state.focus != nil {
  191. nextRow = focus.row + delta
  192. }
  193. var closest event.Tag
  194. dist := int(1e6)
  195. center := (focus.bounds.Min.X + focus.bounds.Max.X) / 2
  196. loop:
  197. for 0 <= order && order < len(q.dirOrder) {
  198. next := q.dirOrder[order]
  199. switch next.row {
  200. case nextRow:
  201. nextCenter := (next.bounds.Min.X + next.bounds.Max.X) / 2
  202. d := center - nextCenter
  203. if d < 0 {
  204. d = -d
  205. }
  206. if d > dist {
  207. break loop
  208. }
  209. dist = d
  210. closest = next.tag
  211. case nextRow + delta:
  212. break loop
  213. }
  214. order += delta
  215. }
  216. if closest != nil {
  217. return q.Focus(handlers, state, closest)
  218. }
  219. }
  220. return state, nil
  221. }
  222. func (q *keyQueue) BoundsFor(k *keyHandler) image.Rectangle {
  223. order := k.dirOrder
  224. return q.dirOrder[order].bounds
  225. }
  226. func (q *keyQueue) AreaFor(k *keyHandler) int {
  227. order := k.dirOrder
  228. return q.dirOrder[order].area
  229. }
  230. func (k *keyFilter) Matches(focus event.Tag, e key.Event, system bool) bool {
  231. for _, f := range *k {
  232. if keyFilterMatch(focus, f, e, system) {
  233. return true
  234. }
  235. }
  236. return false
  237. }
  238. func keyFilterMatch(focus event.Tag, f key.Filter, e key.Event, system bool) bool {
  239. if f.Focus != nil && f.Focus != focus {
  240. return false
  241. }
  242. if (f.Name != "" || system) && f.Name != e.Name {
  243. return false
  244. }
  245. if e.Modifiers&f.Required != f.Required {
  246. return false
  247. }
  248. if e.Modifiers&^(f.Required|f.Optional) != 0 {
  249. return false
  250. }
  251. return true
  252. }
  253. func (q *keyQueue) Focus(handlers map[event.Tag]*handler, state keyState, focus event.Tag) (keyState, []taggedEvent) {
  254. if focus == state.focus {
  255. return state, nil
  256. }
  257. state.content = EditorState{}
  258. var evts []taggedEvent
  259. if state.focus != nil {
  260. evts = append(evts, taggedEvent{tag: state.focus, event: key.FocusEvent{Focus: false}})
  261. }
  262. state.focus = focus
  263. if state.focus != nil {
  264. evts = append(evts, taggedEvent{tag: state.focus, event: key.FocusEvent{Focus: true}})
  265. }
  266. if state.focus == nil || state.state == TextInputKeep {
  267. state.state = TextInputClose
  268. }
  269. return state, evts
  270. }
  271. func (s keyState) softKeyboard(show bool) keyState {
  272. if show {
  273. s.state = TextInputOpen
  274. } else {
  275. s.state = TextInputClose
  276. }
  277. return s
  278. }
  279. func (k *keyFilter) Add(f key.Filter) {
  280. for _, f2 := range *k {
  281. if f == f2 {
  282. return
  283. }
  284. }
  285. *k = append(*k, f)
  286. }
  287. func (k *keyFilter) Merge(k2 keyFilter) {
  288. *k = append(*k, k2...)
  289. }
  290. func (q *keyQueue) inputOp(tag event.Tag, state *keyHandler, t f32.Affine2D, area int, bounds image.Rectangle) {
  291. state.visible = true
  292. if state.orderPlusOne == 0 {
  293. state.orderPlusOne = len(q.order) + 1
  294. q.order = append(q.order, tag)
  295. q.dirOrder = append(q.dirOrder, dirFocusEntry{tag: tag, area: area, bounds: bounds})
  296. }
  297. state.trans = t
  298. }
  299. func (q *keyQueue) setSelection(state keyState, req key.SelectionCmd) keyState {
  300. if req.Tag != state.focus {
  301. return state
  302. }
  303. state.content.Selection.Range = req.Range
  304. state.content.Selection.Caret = req.Caret
  305. return state
  306. }
  307. func (q *keyQueue) editorState(handlers map[event.Tag]*handler, state keyState) EditorState {
  308. s := state.content
  309. if f := state.focus; f != nil {
  310. s.Selection.Transform = handlers[f].key.trans
  311. }
  312. return s
  313. }
  314. func (q *keyQueue) setSnippet(state keyState, req key.SnippetCmd) keyState {
  315. if req.Tag == state.focus {
  316. state.content.Snippet = req.Snippet
  317. }
  318. return state
  319. }
  320. func (t TextInputState) String() string {
  321. switch t {
  322. case TextInputKeep:
  323. return "Keep"
  324. case TextInputClose:
  325. return "Close"
  326. case TextInputOpen:
  327. return "Open"
  328. default:
  329. panic("unexpected value")
  330. }
  331. }