io/router: move focus on tab and shift+tab

Fixes: https://todo.sr.ht/~eliasnaur/gio/195
Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2022-02-24 15:06:29 +01:00
parent cd2ade0583
commit 2e9df04a7b
2 changed files with 86 additions and 26 deletions
+63 -24
View File
@@ -22,6 +22,7 @@ type TextInputState uint8
type keyQueue struct {
focus event.Tag
order []event.Tag
handlers map[event.Tag]*keyHandler
state TextInputState
hint key.InputHint
@@ -34,6 +35,7 @@ type keyHandler struct {
visible bool
new bool
hint key.InputHint
order int
}
// keyCollector tracks state required to update a keyQueue
@@ -53,7 +55,9 @@ const (
// InputState returns the last text input state as
// determined in Frame.
func (q *keyQueue) InputState() TextInputState {
return q.state
state := q.state
q.state = TextInputKeep
return state
}
// InputHint returns the input mode from the most recent key.InputOp.
@@ -76,49 +80,81 @@ func (q *keyQueue) Reset() {
}
for _, h := range q.handlers {
h.visible, h.new = false, false
h.order = -1
}
q.state = TextInputKeep
q.order = q.order[:0]
}
func (q *keyQueue) Frame(events *handlerEvents, collector keyCollector) {
changed, focus := collector.changed, collector.focus
for k, h := range q.handlers {
if !h.visible {
delete(q.handlers, k)
if q.focus == k {
// Remove the focus from the handler that is no longer visible.
// Remove focus from the handler that is no longer visible.
q.focus = nil
q.state = TextInputClose
}
} else if h.new && k != collector.focus {
} else if h.new && k != focus {
// Reset the handler on (each) first appearance, but don't trigger redraw.
events.AddNoRedraw(k, key.FocusEvent{Focus: false})
}
}
if collector.changed && collector.focus != nil {
if _, exists := q.handlers[collector.focus]; !exists {
collector.focus = nil
}
}
if collector.changed && collector.focus != q.focus {
q.content = EditorState{}
if q.focus != nil {
events.Add(q.focus, key.FocusEvent{Focus: false})
}
q.focus = collector.focus
if q.focus != nil {
events.Add(q.focus, key.FocusEvent{Focus: true})
} else {
q.state = TextInputClose
}
if changed {
q.setFocus(focus, events)
}
}
func (q *keyQueue) Push(e event.Event, events *handlerEvents) {
// Convert tab or shift+tab presses to focus moves.
if e, ok := e.(key.Event); ok && e.Name == key.NameTab && e.Modifiers&^key.ModShift == 0 {
if e.State == key.Release || len(q.order) == 0 {
return
}
forward := !e.Modifiers.Contain(key.ModShift)
order := 0
if !forward {
order = -1
}
if q.focus != nil {
order = q.handlers[q.focus].order
if forward {
order++
} else {
order--
}
}
order = (order + len(q.order)) % len(q.order)
q.setFocus(q.order[order], events)
return
}
if q.focus != nil {
events.Add(q.focus, e)
}
}
func (q *keyQueue) setFocus(focus event.Tag, events *handlerEvents) {
if focus != nil {
if _, exists := q.handlers[focus]; !exists {
focus = nil
}
}
if focus == q.focus {
return
}
q.content = EditorState{}
if q.focus != nil {
events.Add(q.focus, key.FocusEvent{Focus: false})
}
q.focus = focus
if q.focus != nil {
events.Add(q.focus, key.FocusEvent{Focus: true})
}
if q.focus == nil || q.state == TextInputKeep {
q.state = TextInputClose
}
}
func (k *keyCollector) focusOp(tag event.Tag) {
k.focus = tag
k.changed = true
@@ -134,11 +170,14 @@ func (k *keyCollector) softKeyboard(show bool) {
func (k *keyCollector) handlerFor(tag event.Tag) *keyHandler {
h, ok := k.q.handlers[tag]
if ok {
return h
if !ok {
h = &keyHandler{new: true, order: -1}
k.q.handlers[tag] = h
}
if h.order == -1 {
h.order = len(k.q.order)
k.q.order = append(k.q.order, tag)
}
h = &keyHandler{new: true}
k.q.handlers[tag] = h
return h
}
+23 -2
View File
@@ -145,7 +145,7 @@ func TestKeyRemoveFocus(t *testing.T) {
assertKeyEventUnexpected(t, r.Events(&handlers[0]))
assertKeyEventUnexpected(t, r.Events(&handlers[1]))
assertFocus(t, r, nil)
assertKeyboard(t, r, TextInputKeep)
assertKeyboard(t, r, TextInputClose)
ops.Reset()
@@ -216,7 +216,7 @@ func TestKeyFocusedInvisible(t *testing.T) {
assertKeyEvent(t, r.Events(&handlers[0]), false)
assertKeyEventUnexpected(t, r.Events(&handlers[1]))
assertFocus(t, r, nil)
assertKeyboard(t, r, TextInputKeep)
assertKeyboard(t, r, TextInputClose)
}
@@ -225,6 +225,27 @@ func TestNoOps(t *testing.T) {
r.Frame(nil)
}
func TestTabFocus(t *testing.T) {
handlers := make([]int, 3)
ops := new(op.Ops)
r := new(Router)
for i := range handlers {
key.InputOp{Tag: &handlers[i]}.Add(ops)
}
r.Frame(ops)
tab := func(mod key.Modifiers) {
r.Queue(
key.Event{Name: key.NameTab, State: key.Press, Modifiers: mod},
key.Event{Name: key.NameTab, State: key.Release, Modifiers: mod},
)
}
tab(0)
tab(key.ModShift)
assertFocus(t, r, &handlers[2])
}
func assertKeyEvent(t *testing.T, events []event.Event, expected bool, expectedInputs ...event.Event) {
t.Helper()
var evtFocus int