From 496fc3cc82c68dcf0567932c887b245d2a73b7e7 Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Sun, 26 Nov 2023 11:23:52 -0600 Subject: [PATCH] io/input: permit FocusCmd to explicitly set the focus to any tag If the client asks for the focus to be set to a tag, allow it. There is a check at the end of Router.Frame that clears the focus if the tag turns out to fail the requirements (visible and has asked for FocusEvents). The change simplifies the logic for determining whether a command can be executed immediately. Signed-off-by: Elias Naur --- io/input/key.go | 5 ----- io/input/key_test.go | 31 +++++++++++++++---------------- io/input/router.go | 33 ++++++++------------------------- 3 files changed, 23 insertions(+), 46 deletions(-) diff --git a/io/input/key.go b/io/input/key.go index 48cef1cf..30a37326 100644 --- a/io/input/key.go +++ b/io/input/key.go @@ -281,11 +281,6 @@ func keyFilterMatch(f key.Filter, e key.Event) bool { } func (q *keyQueue) Focus(handlers map[event.Tag]*handler, state keyState, focus event.Tag) (keyState, []taggedEvent) { - if focus != nil { - if h, exists := handlers[focus]; !exists || !h.filter.key.focusable || !h.key.visible { - focus = nil - } - } if focus == state.focus { return state, nil } diff --git a/io/input/key_test.go b/io/input/key_test.go index a376ac1d..14d0b8d3 100644 --- a/io/input/key_test.go +++ b/io/input/key_test.go @@ -62,6 +62,10 @@ func TestKeyStacked(t *testing.T) { ops := new(op.Ops) r := new(Router) + for i := range handlers { + assertKeyEvent(t, events(r, &handlers[i], key.FocusFilter{}), false) + } + event.InputOp(ops, &handlers[0]) r.Source().Execute(key.FocusCmd{}) r.Source().Execute(key.SoftKeyboardCmd{Show: false}) @@ -71,10 +75,6 @@ func TestKeyStacked(t *testing.T) { r.Source().Execute(key.SoftKeyboardCmd{Show: true}) event.InputOp(ops, &handlers[3]) - for i := range handlers { - assertKeyEvent(t, events(r, &handlers[i], key.FocusFilter{}), false) - } - r.Frame(ops) assertKeyEvent(t, events(r, &handlers[1], key.FocusFilter{}), true) @@ -101,14 +101,6 @@ func TestKeyRemoveFocus(t *testing.T) { ops := new(op.Ops) r := new(Router) - // New InputOp with Focus and Keyboard: - event.InputOp(ops, &handlers[0]) - r.Source().Execute(key.FocusCmd{Tag: &handlers[0]}) - r.Source().Execute(key.SoftKeyboardCmd{Show: true}) - - // New InputOp without any focus: - event.InputOp(ops, &handlers[1]) - filters := []event.Filter{ key.FocusFilter{}, key.Filter{Name: key.NameTab, Required: key.ModShortcut}, @@ -116,6 +108,13 @@ func TestKeyRemoveFocus(t *testing.T) { for i := range handlers { assertKeyEvent(t, events(r, &handlers[i], filters...), false) } + // New InputOp with Focus and Keyboard: + event.InputOp(ops, &handlers[0]) + r.Source().Execute(key.FocusCmd{Tag: &handlers[0]}) + r.Source().Execute(key.SoftKeyboardCmd{Show: true}) + + // New InputOp without any focus: + event.InputOp(ops, &handlers[1]) r.Frame(ops) @@ -180,6 +179,10 @@ func TestKeyFocusedInvisible(t *testing.T) { ops := new(op.Ops) r := new(Router) + for i := range handlers { + assertKeyEvent(t, events(r, &handlers[i], key.FocusFilter{}), false) + } + // Set new InputOp with focus: r.Source().Execute(key.FocusCmd{Tag: &handlers[0]}) event.InputOp(ops, &handlers[0]) @@ -188,10 +191,6 @@ func TestKeyFocusedInvisible(t *testing.T) { // Set new InputOp without focus: event.InputOp(ops, &handlers[1]) - for i := range handlers { - assertKeyEvent(t, events(r, &handlers[i], key.FocusFilter{}), false) - } - r.Frame(ops) assertKeyEvent(t, events(r, &handlers[0], key.FocusFilter{}), true) diff --git a/io/input/router.go b/io/input/router.go index 698382c4..f00e896a 100644 --- a/io/input/router.go +++ b/io/input/router.go @@ -112,9 +112,7 @@ type SemanticID uint type handler struct { // active tracks whether the handler was active in the current // frame. Router deletes state belonging to inactive handlers during Frame. - active bool - // old is true iff the handler was aded in a previous frame. - old bool + active bool pointer pointerHandler key keyHandler // filter the handler has asked for through event handling @@ -279,7 +277,6 @@ func (q *Router) Frame(frame *op.Ops) { delete(q.handlers, k) } else { h.active = false - h.old = true } } q.executeCommands() @@ -381,22 +378,14 @@ func (q *Router) processEvent(e event.Event) bool { } func (q *Router) execute(c Command) { - // The command can be executed immediately if: - // - // - event delivery is not frozen, and - // - the influencing tag and event receivers were all seen - // in the previous frame, and - // - no event receiver has completed their event handling. + // The command can be executed immediately if event delivery is not frozen, and + // no event receiver has completed their event handling. if !q.deferring { - tag, ch := q.executeCommand(c) + ch := q.executeCommand(c) immediate := true - if tag != nil { - h, ok := q.handlers[tag] - immediate = immediate && ok && h.old - } for _, e := range ch.events { h, ok := q.handlers[e.tag] - immediate = immediate && ok && h.old && !h.processedFilter.Matches(e.event) + immediate = immediate && (!ok || !h.processedFilter.Matches(e.event)) } if immediate { // Hold on to the remaining events for state replay. @@ -434,7 +423,7 @@ func (q *Router) lastState() inputState { func (q *Router) executeCommands() { for _, c := range q.commands { - _, ch := q.executeCommand(c) + ch := q.executeCommand(c) q.changeState(nil, ch.state, ch.events) } q.commands = nil @@ -442,31 +431,25 @@ func (q *Router) executeCommands() { // executeCommand the command and return the resulting state change along with the // tag the state change depended on, if any. -func (q *Router) executeCommand(c Command) (event.Tag, stateChange) { +func (q *Router) executeCommand(c Command) stateChange { state := q.state() var evts []taggedEvent - var tag event.Tag switch req := c.(type) { case key.SelectionCmd: - tag = req.Tag state.keyState = q.key.queue.setSelection(state.keyState, req) case key.FocusCmd: - tag = req.Tag state.keyState, evts = q.key.queue.Focus(q.handlers, state.keyState, req.Tag) case key.SoftKeyboardCmd: state.keyState = state.keyState.softKeyboard(req.Show) case key.SnippetCmd: - tag = req.Tag state.keyState = q.key.queue.setSnippet(state.keyState, req) case transfer.OfferCmd: - tag = req.Tag state.pointerState, evts = q.pointer.queue.offerData(q.handlers, state.pointerState, req) case clipboard.WriteCmd: q.cqueue.ProcessWriteClipboard(req) case clipboard.ReadCmd: state.clipboardState = q.cqueue.ProcessReadClipboard(state.clipboardState, req.Tag) case pointer.GrabCmd: - tag = req.Tag state.pointerState, evts = q.pointer.queue.grab(state.pointerState, req) case op.InvalidateCmd: if !q.wakeup || req.At.Before(q.wakeupTime) { @@ -474,7 +457,7 @@ func (q *Router) executeCommand(c Command) (event.Tag, stateChange) { q.wakeupTime = req.At } } - return tag, stateChange{state: state, events: evts} + return stateChange{state: state, events: evts} } func (q *Router) changeState(e event.Event, state inputState, evts []taggedEvent) bool {