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) {
|
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 {
|
if focus == state.focus {
|
||||||
return state, nil
|
return state, nil
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-16
@@ -62,6 +62,10 @@ func TestKeyStacked(t *testing.T) {
|
|||||||
ops := new(op.Ops)
|
ops := new(op.Ops)
|
||||||
r := new(Router)
|
r := new(Router)
|
||||||
|
|
||||||
|
for i := range handlers {
|
||||||
|
assertKeyEvent(t, events(r, &handlers[i], key.FocusFilter{}), false)
|
||||||
|
}
|
||||||
|
|
||||||
event.InputOp(ops, &handlers[0])
|
event.InputOp(ops, &handlers[0])
|
||||||
r.Source().Execute(key.FocusCmd{})
|
r.Source().Execute(key.FocusCmd{})
|
||||||
r.Source().Execute(key.SoftKeyboardCmd{Show: false})
|
r.Source().Execute(key.SoftKeyboardCmd{Show: false})
|
||||||
@@ -71,10 +75,6 @@ func TestKeyStacked(t *testing.T) {
|
|||||||
r.Source().Execute(key.SoftKeyboardCmd{Show: true})
|
r.Source().Execute(key.SoftKeyboardCmd{Show: true})
|
||||||
event.InputOp(ops, &handlers[3])
|
event.InputOp(ops, &handlers[3])
|
||||||
|
|
||||||
for i := range handlers {
|
|
||||||
assertKeyEvent(t, events(r, &handlers[i], key.FocusFilter{}), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
r.Frame(ops)
|
r.Frame(ops)
|
||||||
|
|
||||||
assertKeyEvent(t, events(r, &handlers[1], key.FocusFilter{}), true)
|
assertKeyEvent(t, events(r, &handlers[1], key.FocusFilter{}), true)
|
||||||
@@ -101,14 +101,6 @@ func TestKeyRemoveFocus(t *testing.T) {
|
|||||||
ops := new(op.Ops)
|
ops := new(op.Ops)
|
||||||
r := new(Router)
|
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{
|
filters := []event.Filter{
|
||||||
key.FocusFilter{},
|
key.FocusFilter{},
|
||||||
key.Filter{Name: key.NameTab, Required: key.ModShortcut},
|
key.Filter{Name: key.NameTab, Required: key.ModShortcut},
|
||||||
@@ -116,6 +108,13 @@ func TestKeyRemoveFocus(t *testing.T) {
|
|||||||
for i := range handlers {
|
for i := range handlers {
|
||||||
assertKeyEvent(t, events(r, &handlers[i], filters...), false)
|
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)
|
r.Frame(ops)
|
||||||
|
|
||||||
@@ -180,6 +179,10 @@ func TestKeyFocusedInvisible(t *testing.T) {
|
|||||||
ops := new(op.Ops)
|
ops := new(op.Ops)
|
||||||
r := new(Router)
|
r := new(Router)
|
||||||
|
|
||||||
|
for i := range handlers {
|
||||||
|
assertKeyEvent(t, events(r, &handlers[i], key.FocusFilter{}), false)
|
||||||
|
}
|
||||||
|
|
||||||
// Set new InputOp with focus:
|
// Set new InputOp with focus:
|
||||||
r.Source().Execute(key.FocusCmd{Tag: &handlers[0]})
|
r.Source().Execute(key.FocusCmd{Tag: &handlers[0]})
|
||||||
event.InputOp(ops, &handlers[0])
|
event.InputOp(ops, &handlers[0])
|
||||||
@@ -188,10 +191,6 @@ func TestKeyFocusedInvisible(t *testing.T) {
|
|||||||
// Set new InputOp without focus:
|
// Set new InputOp without focus:
|
||||||
event.InputOp(ops, &handlers[1])
|
event.InputOp(ops, &handlers[1])
|
||||||
|
|
||||||
for i := range handlers {
|
|
||||||
assertKeyEvent(t, events(r, &handlers[i], key.FocusFilter{}), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
r.Frame(ops)
|
r.Frame(ops)
|
||||||
|
|
||||||
assertKeyEvent(t, events(r, &handlers[0], key.FocusFilter{}), true)
|
assertKeyEvent(t, events(r, &handlers[0], key.FocusFilter{}), true)
|
||||||
|
|||||||
+8
-25
@@ -112,9 +112,7 @@ type SemanticID uint
|
|||||||
type handler struct {
|
type handler struct {
|
||||||
// active tracks whether the handler was active in the current
|
// active tracks whether the handler was active in the current
|
||||||
// frame. Router deletes state belonging to inactive handlers during Frame.
|
// frame. Router deletes state belonging to inactive handlers during Frame.
|
||||||
active bool
|
active bool
|
||||||
// old is true iff the handler was aded in a previous frame.
|
|
||||||
old bool
|
|
||||||
pointer pointerHandler
|
pointer pointerHandler
|
||||||
key keyHandler
|
key keyHandler
|
||||||
// filter the handler has asked for through event handling
|
// filter the handler has asked for through event handling
|
||||||
@@ -279,7 +277,6 @@ func (q *Router) Frame(frame *op.Ops) {
|
|||||||
delete(q.handlers, k)
|
delete(q.handlers, k)
|
||||||
} else {
|
} else {
|
||||||
h.active = false
|
h.active = false
|
||||||
h.old = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
q.executeCommands()
|
q.executeCommands()
|
||||||
@@ -381,22 +378,14 @@ func (q *Router) processEvent(e event.Event) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (q *Router) execute(c Command) {
|
func (q *Router) execute(c Command) {
|
||||||
// The command can be executed immediately if:
|
// The command can be executed immediately if event delivery is not frozen, and
|
||||||
//
|
// no event receiver has completed their event handling.
|
||||||
// - 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.
|
|
||||||
if !q.deferring {
|
if !q.deferring {
|
||||||
tag, ch := q.executeCommand(c)
|
ch := q.executeCommand(c)
|
||||||
immediate := true
|
immediate := true
|
||||||
if tag != nil {
|
|
||||||
h, ok := q.handlers[tag]
|
|
||||||
immediate = immediate && ok && h.old
|
|
||||||
}
|
|
||||||
for _, e := range ch.events {
|
for _, e := range ch.events {
|
||||||
h, ok := q.handlers[e.tag]
|
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 {
|
if immediate {
|
||||||
// Hold on to the remaining events for state replay.
|
// Hold on to the remaining events for state replay.
|
||||||
@@ -434,7 +423,7 @@ func (q *Router) lastState() inputState {
|
|||||||
|
|
||||||
func (q *Router) executeCommands() {
|
func (q *Router) executeCommands() {
|
||||||
for _, c := range q.commands {
|
for _, c := range q.commands {
|
||||||
_, ch := q.executeCommand(c)
|
ch := q.executeCommand(c)
|
||||||
q.changeState(nil, ch.state, ch.events)
|
q.changeState(nil, ch.state, ch.events)
|
||||||
}
|
}
|
||||||
q.commands = nil
|
q.commands = nil
|
||||||
@@ -442,31 +431,25 @@ func (q *Router) executeCommands() {
|
|||||||
|
|
||||||
// executeCommand the command and return the resulting state change along with the
|
// executeCommand the command and return the resulting state change along with the
|
||||||
// tag the state change depended on, if any.
|
// 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()
|
state := q.state()
|
||||||
var evts []taggedEvent
|
var evts []taggedEvent
|
||||||
var tag event.Tag
|
|
||||||
switch req := c.(type) {
|
switch req := c.(type) {
|
||||||
case key.SelectionCmd:
|
case key.SelectionCmd:
|
||||||
tag = req.Tag
|
|
||||||
state.keyState = q.key.queue.setSelection(state.keyState, req)
|
state.keyState = q.key.queue.setSelection(state.keyState, req)
|
||||||
case key.FocusCmd:
|
case key.FocusCmd:
|
||||||
tag = req.Tag
|
|
||||||
state.keyState, evts = q.key.queue.Focus(q.handlers, state.keyState, req.Tag)
|
state.keyState, evts = q.key.queue.Focus(q.handlers, state.keyState, req.Tag)
|
||||||
case key.SoftKeyboardCmd:
|
case key.SoftKeyboardCmd:
|
||||||
state.keyState = state.keyState.softKeyboard(req.Show)
|
state.keyState = state.keyState.softKeyboard(req.Show)
|
||||||
case key.SnippetCmd:
|
case key.SnippetCmd:
|
||||||
tag = req.Tag
|
|
||||||
state.keyState = q.key.queue.setSnippet(state.keyState, req)
|
state.keyState = q.key.queue.setSnippet(state.keyState, req)
|
||||||
case transfer.OfferCmd:
|
case transfer.OfferCmd:
|
||||||
tag = req.Tag
|
|
||||||
state.pointerState, evts = q.pointer.queue.offerData(q.handlers, state.pointerState, req)
|
state.pointerState, evts = q.pointer.queue.offerData(q.handlers, state.pointerState, req)
|
||||||
case clipboard.WriteCmd:
|
case clipboard.WriteCmd:
|
||||||
q.cqueue.ProcessWriteClipboard(req)
|
q.cqueue.ProcessWriteClipboard(req)
|
||||||
case clipboard.ReadCmd:
|
case clipboard.ReadCmd:
|
||||||
state.clipboardState = q.cqueue.ProcessReadClipboard(state.clipboardState, req.Tag)
|
state.clipboardState = q.cqueue.ProcessReadClipboard(state.clipboardState, req.Tag)
|
||||||
case pointer.GrabCmd:
|
case pointer.GrabCmd:
|
||||||
tag = req.Tag
|
|
||||||
state.pointerState, evts = q.pointer.queue.grab(state.pointerState, req)
|
state.pointerState, evts = q.pointer.queue.grab(state.pointerState, req)
|
||||||
case op.InvalidateCmd:
|
case op.InvalidateCmd:
|
||||||
if !q.wakeup || req.At.Before(q.wakeupTime) {
|
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
|
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 {
|
func (q *Router) changeState(e event.Event, state inputState, evts []taggedEvent) bool {
|
||||||
|
|||||||
Reference in New Issue
Block a user