forked from joejulian/gio
io/input: deliver reset events lazily
Refactor delivery of reset events to be resolved and delivered as part of Source.Events. This is a preparation for changing event handling to be lazy. Reset events are delivered to event handlers that are either new or haven't been active in the previous frame for a particular event type (pointer or key events), to ensure the handler state is reset. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+15
-9
@@ -36,8 +36,10 @@ type keyQueue struct {
|
||||
type keyHandler struct {
|
||||
// visible will be true if the InputOp is present
|
||||
// in the current frame.
|
||||
visible bool
|
||||
new bool
|
||||
visible bool
|
||||
// reset tracks whether the handler has seen a
|
||||
// focus reset.
|
||||
reset bool
|
||||
focusable bool
|
||||
active bool
|
||||
hint key.InputHint
|
||||
@@ -97,7 +99,16 @@ func (q *keyQueue) Reset() {
|
||||
q.dirOrder = q.dirOrder[:0]
|
||||
}
|
||||
|
||||
func (q *keyQueue) Frame(events *handlerEvents) {
|
||||
func (q *keyQueue) ResetEvent(k event.Tag) (event.Event, bool) {
|
||||
h, ok := q.handlers[k]
|
||||
if !ok || h.reset {
|
||||
return nil, false
|
||||
}
|
||||
h.reset = true
|
||||
return key.FocusEvent{Focus: false}, true
|
||||
}
|
||||
|
||||
func (q *keyQueue) Frame() {
|
||||
for k, h := range q.handlers {
|
||||
if !h.visible || !h.focusable {
|
||||
if q.focus == k {
|
||||
@@ -110,11 +121,6 @@ func (q *keyQueue) Frame(events *handlerEvents) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if h.new && k != q.focus {
|
||||
// Reset the handler on (each) first appearance, but don't trigger redraw.
|
||||
events.AddNoRedraw(k, key.FocusEvent{Focus: false})
|
||||
}
|
||||
h.new = false
|
||||
h.visible = false
|
||||
h.focusable = false
|
||||
h.active = false
|
||||
@@ -325,7 +331,7 @@ func (q *keyQueue) focusable(tag event.Tag) {
|
||||
func (q *keyQueue) handlerFor(tag event.Tag) *keyHandler {
|
||||
h, ok := q.handlers[tag]
|
||||
if !ok {
|
||||
h = &keyHandler{new: true, order: -1}
|
||||
h = &keyHandler{order: -1}
|
||||
if q.handlers == nil {
|
||||
q.handlers = make(map[event.Tag]*keyHandler)
|
||||
}
|
||||
|
||||
+20
-25
@@ -22,13 +22,12 @@ func TestInputWakeup(t *testing.T) {
|
||||
|
||||
var r Router
|
||||
// Test that merely adding a handler doesn't trigger redraw.
|
||||
r.Events(handler, key.FocusFilter{})
|
||||
evts := r.Events(handler, key.FocusFilter{})
|
||||
r.Frame(&ops)
|
||||
if _, wake := r.WakeupTime(); wake {
|
||||
t.Errorf("adding key.InputOp triggered a redraw")
|
||||
}
|
||||
// However, adding a handler queues a Focus(false) event.
|
||||
if evts := r.Events(handler, key.FocusFilter{}); len(evts) != 1 {
|
||||
if len(evts) != 1 {
|
||||
t.Errorf("no Focus event for newly registered key.InputOp")
|
||||
}
|
||||
}
|
||||
@@ -47,15 +46,14 @@ func TestKeyMultiples(t *testing.T) {
|
||||
event.InputOp(ops, &handlers[2])
|
||||
|
||||
for i := range handlers {
|
||||
r.Events(&handlers[i], key.FocusFilter{})
|
||||
assertKeyEvent(t, r.Events(&handlers[i], key.FocusFilter{}), false)
|
||||
}
|
||||
|
||||
r.Frame(ops)
|
||||
|
||||
assertKeyEvent(t, r.Events(&handlers[0], key.FocusFilter{}), false)
|
||||
assertKeyEvent(t, r.Events(&handlers[1], key.FocusFilter{}), false)
|
||||
assertKeyEvent(t, r.Events(&handlers[2], key.FocusFilter{}), true)
|
||||
assertFocus(t, r, &handlers[2])
|
||||
|
||||
assertKeyboard(t, r, TextInputOpen)
|
||||
}
|
||||
|
||||
@@ -74,15 +72,12 @@ func TestKeyStacked(t *testing.T) {
|
||||
event.InputOp(ops, &handlers[3])
|
||||
|
||||
for i := range handlers {
|
||||
r.Events(&handlers[i], key.FocusFilter{})
|
||||
assertKeyEvent(t, r.Events(&handlers[i], key.FocusFilter{}), false)
|
||||
}
|
||||
|
||||
r.Frame(ops)
|
||||
|
||||
assertKeyEvent(t, r.Events(&handlers[0], key.FocusFilter{}), false)
|
||||
assertKeyEvent(t, r.Events(&handlers[1], key.FocusFilter{}), true)
|
||||
assertKeyEvent(t, r.Events(&handlers[2], key.FocusFilter{}), false)
|
||||
assertKeyEvent(t, r.Events(&handlers[3], key.FocusFilter{}), false)
|
||||
assertFocus(t, r, &handlers[1])
|
||||
assertKeyboard(t, r, TextInputOpen)
|
||||
}
|
||||
@@ -119,7 +114,7 @@ func TestKeyRemoveFocus(t *testing.T) {
|
||||
key.Filter{Name: key.NameTab, Required: key.ModShortcut},
|
||||
}
|
||||
for i := range handlers {
|
||||
r.Events(&handlers[i], filters...)
|
||||
assertKeyEvent(t, r.Events(&handlers[i], filters...), false)
|
||||
}
|
||||
|
||||
r.Frame(ops)
|
||||
@@ -129,7 +124,6 @@ func TestKeyRemoveFocus(t *testing.T) {
|
||||
r.Queue(evt)
|
||||
|
||||
assertKeyEvent(t, r.Events(&handlers[0], filters...), true, evt)
|
||||
assertKeyEvent(t, r.Events(&handlers[1], filters...), false)
|
||||
assertFocus(t, r, &handlers[0])
|
||||
assertKeyboard(t, r, TextInputOpen)
|
||||
|
||||
@@ -195,13 +189,12 @@ func TestKeyFocusedInvisible(t *testing.T) {
|
||||
event.InputOp(ops, &handlers[1])
|
||||
|
||||
for i := range handlers {
|
||||
r.Events(&handlers[i], key.FocusFilter{})
|
||||
assertKeyEvent(t, r.Events(&handlers[i], key.FocusFilter{}), false)
|
||||
}
|
||||
|
||||
r.Frame(ops)
|
||||
|
||||
assertKeyEvent(t, r.Events(&handlers[0], key.FocusFilter{}), true)
|
||||
assertKeyEvent(t, r.Events(&handlers[1], key.FocusFilter{}), false)
|
||||
assertFocus(t, r, &handlers[0])
|
||||
assertKeyboard(t, r, TextInputOpen)
|
||||
|
||||
@@ -238,12 +231,11 @@ func TestKeyFocusedInvisible(t *testing.T) {
|
||||
event.InputOp(ops, &handlers[1])
|
||||
|
||||
for i := range handlers {
|
||||
r.Events(&handlers[i], key.FocusFilter{})
|
||||
assertKeyEventUnexpected(t, r.Events(&handlers[i], key.FocusFilter{}))
|
||||
}
|
||||
|
||||
r.Frame(ops)
|
||||
|
||||
assertKeyEvent(t, r.Events(&handlers[0], key.FocusFilter{}), false)
|
||||
assertKeyEventUnexpected(t, r.Events(&handlers[1], key.FocusFilter{}))
|
||||
assertFocus(t, r, nil)
|
||||
assertKeyboard(t, r, TextInputClose)
|
||||
@@ -393,11 +385,15 @@ func TestKeyRouting(t *testing.T) {
|
||||
|
||||
// With no focus, the events should traverse the final branch of the hit tree
|
||||
// searching for handlers.
|
||||
assertKeyEvent(t, r.Events(&handlers[4], fa...), false, A)
|
||||
assertKeyEvent(t, r.Events(&handlers[3], key.FocusFilter{}), false)
|
||||
assertKeyEvent(t, r.Events(&handlers[2], fa...), false)
|
||||
assertKeyEvent(t, r.Events(&handlers[1], fb...), false, B)
|
||||
assertKeyEvent(t, r.Events(&handlers[0], fa...), false)
|
||||
if evts := r.Events(&handlers[4], fa...); len(evts) != 1 || evts[0] != A {
|
||||
t.Errorf("expected key event")
|
||||
}
|
||||
r.Events(&handlers[3], key.FocusFilter{})
|
||||
r.Events(&handlers[2], fa...)
|
||||
if evts := r.Events(&handlers[1], fb...); len(evts) != 1 || evts[0] != B {
|
||||
t.Errorf("expected key event")
|
||||
}
|
||||
r.Events(&handlers[0], fa...)
|
||||
|
||||
r2 := new(Router)
|
||||
|
||||
@@ -414,11 +410,10 @@ func TestKeyRouting(t *testing.T) {
|
||||
|
||||
// With focus, the events should traverse the branch of the hit tree
|
||||
// containing the focused element.
|
||||
assertKeyEvent(t, r2.Events(&handlers[4], fa...), false)
|
||||
assertKeyEvent(t, r2.Events(&handlers[3], key.FocusFilter{}), true)
|
||||
assertKeyEvent(t, r2.Events(&handlers[2], fa...), false)
|
||||
assertKeyEvent(t, r2.Events(&handlers[1], fb...), false)
|
||||
assertKeyEvent(t, r2.Events(&handlers[0], fa...), false, A)
|
||||
if evts := r2.Events(&handlers[0], fa...); len(evts) != 1 || evts[0] != A {
|
||||
t.Errorf("expected key event")
|
||||
}
|
||||
}
|
||||
|
||||
func assertKeyEvent(t *testing.T, events []event.Event, expectedFocus bool, expectedInputs ...event.Event) {
|
||||
|
||||
+24
-15
@@ -61,7 +61,10 @@ type pointerInfo struct {
|
||||
}
|
||||
|
||||
type pointerHandler struct {
|
||||
area int
|
||||
area int
|
||||
// setup tracks whether the handler has received
|
||||
// the pointer.Cancel event that resets its state.
|
||||
setup bool
|
||||
active bool
|
||||
types pointer.Kind
|
||||
// min and max horizontal/vertical scroll
|
||||
@@ -224,19 +227,19 @@ func (c *pointerCollector) addHitNode(n hitNode) {
|
||||
}
|
||||
|
||||
// newHandler returns the current handler or a new one for tag.
|
||||
func (c *pointerCollector) newHandler(tag event.Tag, events *handlerEvents) *pointerHandler {
|
||||
func (c *pointerCollector) newHandler(tag event.Tag) *pointerHandler {
|
||||
areaID := c.currentArea()
|
||||
c.addHitNode(hitNode{
|
||||
area: areaID,
|
||||
tag: tag,
|
||||
pass: c.state.pass > 0,
|
||||
})
|
||||
h := c.q.handlerFor(tag, events)
|
||||
h := c.q.handlerFor(tag)
|
||||
h.area = areaID
|
||||
return h
|
||||
}
|
||||
|
||||
func (q *pointerQueue) handlerFor(tag event.Tag, events *handlerEvents) *pointerHandler {
|
||||
func (q *pointerQueue) handlerFor(tag event.Tag) *pointerHandler {
|
||||
h, ok := q.handlers[tag]
|
||||
if !ok {
|
||||
h = &pointerHandler{
|
||||
@@ -246,9 +249,6 @@ func (q *pointerQueue) handlerFor(tag event.Tag, events *handlerEvents) *pointer
|
||||
q.handlers = make(map[event.Tag]*pointerHandler)
|
||||
}
|
||||
q.handlers[tag] = h
|
||||
// Cancel handlers on (each) first appearance, but don't
|
||||
// trigger redraw.
|
||||
events.AddNoRedraw(tag, pointer.Event{Kind: pointer.Cancel})
|
||||
}
|
||||
if !h.active {
|
||||
h.types = 0
|
||||
@@ -286,19 +286,28 @@ func (q *pointerQueue) grab(req pointer.GrabCmd, events *handlerEvents) {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *pointerCollector) inputOp(tag event.Tag, events *handlerEvents) {
|
||||
func (c *pointerCollector) inputOp(tag event.Tag) {
|
||||
areaID := c.currentArea()
|
||||
area := &c.q.areas[areaID]
|
||||
area.semantic.content.tag = tag
|
||||
c.newHandler(tag, events)
|
||||
c.newHandler(tag)
|
||||
}
|
||||
|
||||
func (q *pointerQueue) filterTag(tag event.Tag, f pointer.Filter, events *handlerEvents) {
|
||||
h := q.handlerFor(tag, events)
|
||||
func (q *pointerQueue) filterTag(tag event.Tag, f pointer.Filter) {
|
||||
h := q.handlerFor(tag)
|
||||
h.types = h.types | f.Kinds
|
||||
h.scrollRange = h.scrollRange.Union(f.ScrollBounds)
|
||||
}
|
||||
|
||||
func (q *pointerQueue) ResetEvent(tag event.Tag) (event.Event, bool) {
|
||||
h, ok := q.handlers[tag]
|
||||
if !ok || h.setup {
|
||||
return nil, false
|
||||
}
|
||||
h.setup = true
|
||||
return pointer.Event{Kind: pointer.Cancel}, true
|
||||
}
|
||||
|
||||
func (c *pointerCollector) semanticLabel(lbl string) {
|
||||
areaID := c.currentArea()
|
||||
area := &c.q.areas[areaID]
|
||||
@@ -340,13 +349,13 @@ func (c *pointerCollector) cursor(cursor pointer.Cursor) {
|
||||
area.cursor = cursor
|
||||
}
|
||||
|
||||
func (q *pointerQueue) sourceFilter(tag event.Tag, f transfer.SourceFilter, events *handlerEvents) {
|
||||
h := q.handlerFor(tag, events)
|
||||
func (q *pointerQueue) sourceFilter(tag event.Tag, f transfer.SourceFilter) {
|
||||
h := q.handlerFor(tag)
|
||||
h.sourceMimes = append(h.sourceMimes, f.Type)
|
||||
}
|
||||
|
||||
func (q *pointerQueue) targetFilter(tag event.Tag, f transfer.TargetFilter, events *handlerEvents) {
|
||||
h := q.handlerFor(tag, events)
|
||||
func (q *pointerQueue) targetFilter(tag event.Tag, f transfer.TargetFilter) {
|
||||
h := q.handlerFor(tag)
|
||||
h.targetMimes = append(h.targetMimes, f.Type)
|
||||
}
|
||||
|
||||
|
||||
+14
-11
@@ -129,22 +129,29 @@ func (s Source) Events(k event.Tag, filters ...event.Filter) []event.Event {
|
||||
}
|
||||
|
||||
func (q *Router) Events(k event.Tag, filters ...event.Filter) []event.Event {
|
||||
var resetEvents []event.Event
|
||||
for _, f := range filters {
|
||||
switch f := f.(type) {
|
||||
case key.Filter:
|
||||
q.key.queue.filter(k, f)
|
||||
case key.FocusFilter:
|
||||
q.key.queue.focusable(k)
|
||||
if reset, ok := q.key.queue.ResetEvent(k); ok {
|
||||
resetEvents = append(resetEvents, reset)
|
||||
}
|
||||
case pointer.Filter:
|
||||
q.pointer.queue.filterTag(k, f, &q.handlers)
|
||||
q.pointer.queue.filterTag(k, f)
|
||||
if reset, ok := q.pointer.queue.ResetEvent(k); ok {
|
||||
resetEvents = append(resetEvents, reset)
|
||||
}
|
||||
case transfer.SourceFilter:
|
||||
q.pointer.queue.sourceFilter(k, f, &q.handlers)
|
||||
q.pointer.queue.sourceFilter(k, f)
|
||||
case transfer.TargetFilter:
|
||||
q.pointer.queue.targetFilter(k, f, &q.handlers)
|
||||
q.pointer.queue.targetFilter(k, f)
|
||||
}
|
||||
}
|
||||
events := q.handlers.Events(k, filters...)
|
||||
return events
|
||||
return append(resetEvents, events...)
|
||||
}
|
||||
|
||||
// Frame replaces the declared handlers from the supplied
|
||||
@@ -161,7 +168,7 @@ func (q *Router) Frame(frame *op.Ops) {
|
||||
q.collect()
|
||||
q.executeCommands()
|
||||
q.pointer.queue.Frame(&q.handlers)
|
||||
q.key.queue.Frame(&q.handlers)
|
||||
q.key.queue.Frame()
|
||||
|
||||
if q.handlers.HadEvents() {
|
||||
q.wakeup = true
|
||||
@@ -461,7 +468,7 @@ func (q *Router) collect() {
|
||||
|
||||
case ops.TypeInput:
|
||||
tag := encOp.Refs[0].(event.Tag)
|
||||
pc.inputOp(tag, &q.handlers)
|
||||
pc.inputOp(tag)
|
||||
a := pc.currentArea()
|
||||
b := pc.currentAreaBounds()
|
||||
kq.inputOp(tag, t, a, b)
|
||||
@@ -522,13 +529,9 @@ func (h *handlerEvents) init() {
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handlerEvents) AddNoRedraw(k event.Tag, e event.Event) {
|
||||
func (h *handlerEvents) Add(k event.Tag, e event.Event) {
|
||||
h.init()
|
||||
h.handlers[k] = append(h.handlers[k], e)
|
||||
}
|
||||
|
||||
func (h *handlerEvents) Add(k event.Tag, e event.Event) {
|
||||
h.AddNoRedraw(k, e)
|
||||
h.hadEvents = true
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user