From 2e9df04a7ba9c4eb5078e00aaa589218bf3a319e Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Thu, 24 Feb 2022 15:06:29 +0100 Subject: [PATCH] io/router: move focus on tab and shift+tab Fixes: https://todo.sr.ht/~eliasnaur/gio/195 Signed-off-by: Elias Naur --- io/router/key.go | 87 +++++++++++++++++++++++++++++++------------ io/router/key_test.go | 25 ++++++++++++- 2 files changed, 86 insertions(+), 26 deletions(-) diff --git a/io/router/key.go b/io/router/key.go index fa8347aa..a84cdb52 100644 --- a/io/router/key.go +++ b/io/router/key.go @@ -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 } diff --git a/io/router/key_test.go b/io/router/key_test.go index be50ac8a..d9d25800 100644 --- a/io/router/key_test.go +++ b/io/router/key_test.go @@ -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