key.go raw
1 // SPDX-License-Identifier: Unlicense OR MIT
2
3 package router
4
5 import (
6 "github.com/p9c/p9/pkg/gel/gio/internal/opconst"
7 "github.com/p9c/p9/pkg/gel/gio/internal/ops"
8 "github.com/p9c/p9/pkg/gel/gio/io/event"
9 "github.com/p9c/p9/pkg/gel/gio/io/key"
10 "github.com/p9c/p9/pkg/gel/gio/op"
11 )
12
13 type TextInputState uint8
14
15 type keyQueue struct {
16 focus event.Tag
17 handlers map[event.Tag]*keyHandler
18 reader ops.Reader
19 state TextInputState
20 }
21
22 type keyHandler struct {
23 // visible will be true if the InputOp is present
24 // in the current frame.
25 visible bool
26 new bool
27 }
28
29 const (
30 TextInputKeep TextInputState = iota
31 TextInputClose
32 TextInputOpen
33 )
34
35 // InputState returns the last text input state as
36 // determined in Frame.
37 func (q *keyQueue) InputState() TextInputState {
38 return q.state
39 }
40
41 func (q *keyQueue) Frame(root *op.Ops, events *handlerEvents) {
42 if q.handlers == nil {
43 q.handlers = make(map[event.Tag]*keyHandler)
44 }
45 for _, h := range q.handlers {
46 h.visible, h.new = false, false
47 }
48 q.reader.Reset(root)
49
50 focus, changed, state := q.resolveFocus(events)
51 for k, h := range q.handlers {
52 if !h.visible {
53 delete(q.handlers, k)
54 if q.focus == k {
55 // Remove the focus from the handler that is no longer visible.
56 q.focus = nil
57 state = TextInputClose
58 }
59 } else if h.new && k != focus {
60 // Reset the handler on (each) first appearance, but don't trigger redraw.
61 events.AddNoRedraw(k, key.FocusEvent{Focus: false})
62 }
63 }
64 if changed && focus != nil {
65 if _, exists := q.handlers[focus]; !exists {
66 focus = nil
67 }
68 }
69 if changed && focus != q.focus {
70 if q.focus != nil {
71 events.Add(q.focus, key.FocusEvent{Focus: false})
72 }
73 q.focus = focus
74 if q.focus != nil {
75 events.Add(q.focus, key.FocusEvent{Focus: true})
76 } else {
77 state = TextInputClose
78 }
79 }
80 q.state = state
81 }
82
83 func (q *keyQueue) Push(e event.Event, events *handlerEvents) {
84 if q.focus != nil {
85 events.Add(q.focus, e)
86 }
87 }
88
89 func (q *keyQueue) resolveFocus(events *handlerEvents) (focus event.Tag, changed bool, state TextInputState) {
90 for encOp, ok := q.reader.Decode(); ok; encOp, ok = q.reader.Decode() {
91 switch opconst.OpType(encOp.Data[0]) {
92 case opconst.TypeKeyFocus:
93 op := decodeFocusOp(encOp.Data, encOp.Refs)
94 changed = true
95 focus = op.Tag
96 case opconst.TypeKeySoftKeyboard:
97 op := decodeSoftKeyboardOp(encOp.Data, encOp.Refs)
98 if op.Show {
99 state = TextInputOpen
100 } else {
101 state = TextInputClose
102 }
103 case opconst.TypeKeyInput:
104 op := decodeKeyInputOp(encOp.Data, encOp.Refs)
105 h, ok := q.handlers[op.Tag]
106 if !ok {
107 h = &keyHandler{new: true}
108 q.handlers[op.Tag] = h
109 }
110 h.visible = true
111 }
112 }
113 return
114 }
115
116 func decodeKeyInputOp(d []byte, refs []interface{}) key.InputOp {
117 if opconst.OpType(d[0]) != opconst.TypeKeyInput {
118 panic("invalid op")
119 }
120 return key.InputOp{
121 Tag: refs[0].(event.Tag),
122 }
123 }
124
125 func decodeSoftKeyboardOp(d []byte, refs []interface{}) key.SoftKeyboardOp {
126 if opconst.OpType(d[0]) != opconst.TypeKeySoftKeyboard {
127 panic("invalid op")
128 }
129 return key.SoftKeyboardOp{
130 Show: d[1] != 0,
131 }
132 }
133
134 func decodeFocusOp(d []byte, refs []interface{}) key.FocusOp {
135 if opconst.OpType(d[0]) != opconst.TypeKeyFocus {
136 panic("invalid op")
137 }
138 return key.FocusOp{
139 Tag: refs[0],
140 }
141 }
142