mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
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 <mail@eliasnaur.com>
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
+15
-16
@@ -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)
|
||||
|
||||
+8
-25
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user