io/input: tighten tests

Now that event delivery can be interleaved with commands, tests can be
made more precise.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2023-11-27 11:30:34 -06:00
parent ed0d5d5767
commit 20c28ef282
6 changed files with 277 additions and 496 deletions
+34 -72
View File
@@ -8,70 +8,58 @@ import (
"testing"
"gioui.org/io/clipboard"
"gioui.org/io/event"
"gioui.org/io/transfer"
"gioui.org/op"
)
func TestClipboardDuplicateEvent(t *testing.T) {
ops, router, handler := new(op.Ops), new(Router), make([]int, 2)
ops, r, handlers := new(op.Ops), new(Router), make([]int, 2)
// Both must receive the event once
router.Source().Execute(clipboard.ReadCmd{Tag: &handler[0]})
router.Source().Execute(clipboard.ReadCmd{Tag: &handler[1]})
// Both must receive the event once.
r.Source().Execute(clipboard.ReadCmd{Tag: &handlers[0]})
r.Source().Execute(clipboard.ReadCmd{Tag: &handlers[1]})
router.Frame(ops)
event := transfer.DataEvent{
Type: "application/text",
Open: func() io.ReadCloser {
return io.NopCloser(strings.NewReader("Test"))
},
}
router.Queue(event)
assertClipboardEvent(t, events(router, &handler[0], transfer.TargetFilter{Type: "application/text"}), true)
assertClipboardEvent(t, events(router, &handler[1], transfer.TargetFilter{Type: "application/text"}), true)
assertClipboardReadCmd(t, router, 0)
ops.Reset()
r.Queue(event)
for i := range handlers {
f := transfer.TargetFilter{Target: &handlers[i], Type: "application/text"}
assertEventTypeSequence(t, events(r, -1, f), transfer.DataEvent{})
}
assertClipboardReadCmd(t, r, 0)
// No ReadCmd
r.Source().Execute(clipboard.ReadCmd{Tag: &handlers[0]})
router.Frame(ops)
assertClipboardReadCmd(t, router, 0)
assertClipboardEvent(t, events(router, &handler[0]), false)
assertClipboardEvent(t, events(router, &handler[1]), false)
ops.Reset()
router.Source().Execute(clipboard.ReadCmd{Tag: &handler[0]})
router.Frame(ops)
r.Frame(ops)
// No ClipboardEvent sent
assertClipboardReadCmd(t, router, 1)
assertClipboardEvent(t, events(router, &handler[0]), false)
assertClipboardEvent(t, events(router, &handler[1]), false)
ops.Reset()
assertClipboardReadCmd(t, r, 1)
for i := range handlers {
f := transfer.TargetFilter{Target: &handlers[i]}
assertEventTypeSequence(t, events(r, -1, f))
}
}
func TestQueueProcessReadClipboard(t *testing.T) {
ops, router, handler := new(op.Ops), new(Router), make([]int, 2)
ops.Reset()
ops, r, handler := new(op.Ops), new(Router), make([]int, 2)
// Request read
router.Source().Execute(clipboard.ReadCmd{Tag: &handler[0]})
r.Source().Execute(clipboard.ReadCmd{Tag: &handler[0]})
router.Frame(ops)
assertClipboardReadCmd(t, router, 1)
assertClipboardReadCmd(t, r, 1)
ops.Reset()
for i := 0; i < 3; i++ {
// No ReadCmd
// One receiver must still wait for response
router.Frame(ops)
assertClipboardReadDuplicated(t, router, 1)
ops.Reset()
r.Frame(ops)
assertClipboardReadDuplicated(t, r, 1)
}
router.Frame(ops)
// Send the clipboard event
event := transfer.DataEvent{
Type: "application/text",
@@ -79,55 +67,29 @@ func TestQueueProcessReadClipboard(t *testing.T) {
return io.NopCloser(strings.NewReader("Text 2"))
},
}
router.Queue(event)
assertClipboardEvent(t, events(router, &handler[0], transfer.TargetFilter{Type: "application/text"}), true)
assertClipboardReadCmd(t, router, 0)
ops.Reset()
// No ReadCmd
// There's no receiver waiting
router.Frame(ops)
assertClipboardReadCmd(t, router, 0)
assertClipboardEvent(t, events(router, &handler[0]), false)
ops.Reset()
r.Queue(event)
assertEventTypeSequence(t, events(r, -1, transfer.TargetFilter{Target: &handler[0], Type: "application/text"}), transfer.DataEvent{})
assertClipboardReadCmd(t, r, 0)
}
func TestQueueProcessWriteClipboard(t *testing.T) {
router := new(Router)
r := new(Router)
const mime = "application/text"
router.Source().Execute(clipboard.WriteCmd{Type: mime, Data: io.NopCloser(strings.NewReader("Write 1"))})
r.Source().Execute(clipboard.WriteCmd{Type: mime, Data: io.NopCloser(strings.NewReader("Write 1"))})
assertClipboardWriteCmd(t, router, mime, "Write 1")
assertClipboardWriteCmd(t, router, "", "")
assertClipboardWriteCmd(t, r, mime, "Write 1")
assertClipboardWriteCmd(t, r, "", "")
router.Source().Execute(clipboard.WriteCmd{Type: mime, Data: io.NopCloser(strings.NewReader("Write 2"))})
r.Source().Execute(clipboard.WriteCmd{Type: mime, Data: io.NopCloser(strings.NewReader("Write 2"))})
assertClipboardReadCmd(t, router, 0)
assertClipboardWriteCmd(t, router, mime, "Write 2")
}
func assertClipboardEvent(t *testing.T, events []event.Event, expected bool) {
t.Helper()
var evtClipboard int
for _, e := range events {
switch e.(type) {
case transfer.DataEvent:
evtClipboard++
}
}
if evtClipboard <= 0 && expected {
t.Error("expected to receive some event")
}
if evtClipboard > 0 && !expected {
t.Error("unexpected event received")
}
assertClipboardReadCmd(t, r, 0)
assertClipboardWriteCmd(t, r, mime, "Write 2")
}
func assertClipboardReadCmd(t *testing.T, router *Router, expected int) {
t.Helper()
if got := len(router.lastState().receivers); got != expected {
if got := len(router.state().receivers); got != expected {
t.Errorf("unexpected %d receivers, got %d", expected, got)
}
if router.ClipboardRequested() != (expected > 0) {
@@ -137,7 +99,7 @@ func assertClipboardReadCmd(t *testing.T, router *Router, expected int) {
func assertClipboardReadDuplicated(t *testing.T, router *Router, expected int) {
t.Helper()
if len(router.lastState().receivers) != expected {
if len(router.state().receivers) != expected {
t.Error("receivers removed")
}
if router.ClipboardRequested() != false {
+52 -256
View File
@@ -4,7 +4,6 @@ package input
import (
"image"
"reflect"
"testing"
"gioui.org/f32"
@@ -18,160 +17,89 @@ import (
func TestInputWakeup(t *testing.T) {
handler := new(int)
var ops op.Ops
// InputOps shouldn't trigger redraws.
event.InputOp(&ops, handler)
var r Router
// Test that merely adding a handler doesn't trigger redraw.
evts := events(&r, handler, key.FocusFilter{})
// Reset events shouldn't either.
evts := events(&r, -1, key.FocusFilter{Target: new(int)}, key.Filter{Name: "A"})
assertEventSequence(t, evts, key.FocusEvent{Focus: false})
r.Frame(&ops)
if _, wake := r.WakeupTime(); wake {
t.Errorf("adding key.InputOp triggered a redraw")
t.Errorf("InputOp or the resetting FocusEvent triggered a wakeup")
}
if len(evts) != 1 {
t.Errorf("no Focus event for newly registered key.InputOp")
// And neither does events that don't match anything.
if r.Queue(key.SnippetEvent{}) {
t.Errorf("a not-matching event triggered a wakeup")
}
// However, events that does match should trigger wakeup.
if !r.Queue(key.Event{Name: "A"}) {
t.Errorf("a key.Event didn't trigger redraw")
}
}
func TestKeyMultiples(t *testing.T) {
handlers := make([]int, 3)
ops := new(op.Ops)
r := new(Router)
r.Source().Execute(key.SoftKeyboardCmd{Show: true})
event.InputOp(ops, &handlers[0])
event.InputOp(ops, &handlers[1])
// The last one must be focused:
event.InputOp(ops, &handlers[2])
for i := range handlers {
assertKeyEvent(t, events(r, &handlers[i], key.FocusFilter{}), false)
assertEventSequence(t, events(r, 1, key.FocusFilter{Target: &handlers[i]}), key.FocusEvent{Focus: false})
}
r.Frame(ops)
r.Source().Execute(key.FocusCmd{Tag: &handlers[2]})
assertKeyEvent(t, events(r, &handlers[2], key.FocusFilter{}), true)
assertEventSequence(t, events(r, -1, key.FocusFilter{Target: &handlers[2]}), key.FocusEvent{Focus: true})
assertFocus(t, r, &handlers[2])
assertKeyboard(t, r, TextInputOpen)
}
func TestKeyStacked(t *testing.T) {
handlers := make([]int, 4)
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})
event.InputOp(ops, &handlers[1])
r.Source().Execute(key.FocusCmd{Tag: &handlers[1]})
event.InputOp(ops, &handlers[2])
r.Source().Execute(key.SoftKeyboardCmd{Show: true})
event.InputOp(ops, &handlers[3])
r.Frame(ops)
assertKeyEvent(t, events(r, &handlers[1], key.FocusFilter{}), true)
assertFocus(t, r, &handlers[1])
assertKeyboard(t, r, TextInputOpen)
}
func TestKeySoftKeyboardNoFocus(t *testing.T) {
ops := new(op.Ops)
r := new(Router)
// It's possible to open the keyboard
// without any active focus:
r.Source().Execute(key.SoftKeyboardCmd{Show: true})
r.Frame(ops)
assertFocus(t, r, nil)
assertKeyboard(t, r, TextInputOpen)
}
func TestKeyRemoveFocus(t *testing.T) {
handlers := make([]int, 2)
ops := new(op.Ops)
r := new(Router)
filters := []event.Filter{
key.FocusFilter{},
key.Filter{Name: key.NameTab, Required: key.ModShortcut},
filters := func(h event.Tag) []event.Filter {
return []event.Filter{
key.FocusFilter{Target: h},
key.Filter{Focus: h, Name: key.NameTab, Required: key.ModShortcut},
}
}
var all []event.Filter
for i := range handlers {
assertKeyEvent(t, events(r, &handlers[i], filters...), false)
all = append(all, filters(&handlers[i])...)
}
// New InputOp with Focus and Keyboard:
event.InputOp(ops, &handlers[0])
assertEventSequence(t, events(r, len(handlers), all...), key.FocusEvent{}, key.FocusEvent{})
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)
// Add some key events:
evt := event.Event(key.Event{Name: key.NameTab, Modifiers: key.ModShortcut, State: key.Press})
evt := key.Event{Name: key.NameTab, Modifiers: key.ModShortcut, State: key.Press}
r.Queue(evt)
assertKeyEvent(t, events(r, &handlers[0], filters...), true, evt)
assertEventSequence(t, events(r, 2, filters(&handlers[0])...), key.FocusEvent{Focus: true}, evt)
assertFocus(t, r, &handlers[0])
assertKeyboard(t, r, TextInputOpen)
ops.Reset()
// Will get the focus removed:
event.InputOp(ops, &handlers[0])
// Unchanged:
event.InputOp(ops, &handlers[1])
// Remove focus by focusing on a tag that don't exist.
// Frame removes focus from tags that don't filter for focus events nor mentioned in an InputOp.
r.Source().Execute(key.FocusCmd{Tag: new(int)})
r.Frame(new(op.Ops))
r.Frame(ops)
assertKeyEventUnexpected(t, events(r, &handlers[1], key.FocusFilter{}))
assertEventSequence(t, events(r, -1, filters(&handlers[1])...))
assertFocus(t, r, nil)
assertKeyboard(t, r, TextInputClose)
ops.Reset()
event.InputOp(ops, &handlers[0])
event.InputOp(ops, &handlers[1])
assertKeyEventUnexpected(t, events(r, &handlers[0], key.FocusFilter{}))
assertKeyEventUnexpected(t, events(r, &handlers[1], key.FocusFilter{}))
assertFocus(t, r, nil)
assertKeyboard(t, r, TextInputClose)
r.Frame(ops)
ops.Reset()
// Set focus to InputOp which already
// exists in the previous frame:
r.Source().Execute(key.FocusCmd{Tag: &handlers[0]})
event.InputOp(ops, &handlers[0])
r.Source().Execute(key.SoftKeyboardCmd{Show: true})
assertFocus(t, r, &handlers[0])
ops.Reset()
// Remove focus.
event.InputOp(ops, &handlers[1])
r.Source().Execute(key.FocusCmd{})
assertKeyEventUnexpected(t, events(r, &handlers[1], key.FocusFilter{}))
assertFocus(t, r, nil)
assertKeyboard(t, r, TextInputClose)
}
func TestKeyFocusedInvisible(t *testing.T) {
@@ -180,44 +108,27 @@ func TestKeyFocusedInvisible(t *testing.T) {
r := new(Router)
for i := range handlers {
assertKeyEvent(t, events(r, &handlers[i], key.FocusFilter{}), false)
assertEventSequence(t, events(r, 1, key.FocusFilter{Target: &handlers[i]}), key.FocusEvent{Focus: false})
}
// Set new InputOp with focus:
r.Source().Execute(key.FocusCmd{Tag: &handlers[0]})
event.InputOp(ops, &handlers[0])
r.Source().Execute(key.SoftKeyboardCmd{Show: true})
// Set new InputOp without focus:
event.InputOp(ops, &handlers[1])
r.Frame(ops)
assertKeyEvent(t, events(r, &handlers[0], key.FocusFilter{}), true)
assertEventSequence(t, events(r, 1, key.FocusFilter{Target: &handlers[0]}), key.FocusEvent{Focus: true})
assertFocus(t, r, &handlers[0])
assertKeyboard(t, r, TextInputOpen)
ops.Reset()
//
// Removed first (focused) element!
//
// Unchanged:
event.InputOp(ops, &handlers[1])
// Frame will clear the focus because the handler is not visible.
r.Frame(ops)
assertKeyEventUnexpected(t, events(r, &handlers[0], key.FocusFilter{}))
assertKeyEventUnexpected(t, events(r, &handlers[1], key.FocusFilter{}))
for i := range handlers {
assertEventSequence(t, events(r, -1, key.FocusFilter{Target: &handlers[i]}))
}
assertFocus(t, r, nil)
assertKeyboard(t, r, TextInputClose)
r.Frame(ops)
// Unchanged
event.InputOp(ops, &handlers[1])
r.Frame(ops)
ops.Reset()
@@ -226,18 +137,7 @@ func TestKeyFocusedInvisible(t *testing.T) {
// It must receive one `Event{Focus: false}`.
event.InputOp(ops, &handlers[0])
// Unchanged
event.InputOp(ops, &handlers[1])
for i := range handlers {
assertKeyEventUnexpected(t, events(r, &handlers[i], key.FocusFilter{}))
}
r.Frame(ops)
assertKeyEventUnexpected(t, events(r, &handlers[1], key.FocusFilter{}))
assertFocus(t, r, nil)
assertKeyboard(t, r, TextInputClose)
assertEventSequence(t, events(r, -1, key.FocusFilter{Target: &handlers[0]}), key.FocusEvent{Focus: false})
}
func TestNoOps(t *testing.T) {
@@ -259,7 +159,7 @@ func TestDirectionalFocus(t *testing.T) {
cl := clip.Rect(bounds).Push(ops)
event.InputOp(ops, &handlers[i])
cl.Pop()
events(r, &handlers[i], key.FocusFilter{})
events(r, -1, key.FocusFilter{Target: &handlers[i]})
}
r.Frame(ops)
@@ -292,14 +192,14 @@ func TestFocusScroll(t *testing.T) {
h := new(int)
filters := []event.Filter{
key.FocusFilter{},
key.FocusFilter{Target: h},
pointer.Filter{
Target: h,
Kinds: pointer.Scroll,
ScrollBounds: image.Rect(-100, -100, 100, 100),
},
}
events(r, h, filters...)
events(r, -1, filters...)
parent := clip.Rect(image.Rect(1, 1, 14, 39)).Push(ops)
cl := clip.Rect(image.Rect(10, -20, 20, 30)).Push(ops)
event.InputOp(ops, h)
@@ -311,7 +211,7 @@ func TestFocusScroll(t *testing.T) {
r.MoveFocus(key.FocusLeft)
r.RevealFocus(image.Rect(0, 0, 15, 40))
evts := events(r, h, filters...)
evts := events(r, -1, filters...)
assertScrollEvent(t, evts[len(evts)-1], f32.Pt(6, -9))
}
@@ -321,13 +221,13 @@ func TestFocusClick(t *testing.T) {
h := new(int)
filters := []event.Filter{
key.FocusFilter{},
key.FocusFilter{Target: h},
pointer.Filter{
Target: h,
Kinds: pointer.Press | pointer.Release,
},
}
assertEventPointerTypeSequence(t, events(r, h, filters...), pointer.Cancel)
assertEventPointerTypeSequence(t, events(r, -1, filters...), pointer.Cancel)
cl := clip.Rect(image.Rect(0, 0, 10, 10)).Push(ops)
event.InputOp(ops, h)
cl.Pop()
@@ -336,7 +236,7 @@ func TestFocusClick(t *testing.T) {
r.MoveFocus(key.FocusLeft)
r.ClickFocus()
assertEventPointerTypeSequence(t, events(r, h, filters...), pointer.Press, pointer.Release)
assertEventPointerTypeSequence(t, events(r, -1, filters...), pointer.Press, pointer.Release)
}
func TestNoFocus(t *testing.T) {
@@ -345,134 +245,30 @@ func TestNoFocus(t *testing.T) {
}
func TestKeyRouting(t *testing.T) {
handlers := make([]int, 5)
ops := new(op.Ops)
macroOps := new(op.Ops)
r := new(Router)
rect := clip.Rect{Max: image.Pt(10, 10)}
macro := op.Record(macroOps)
event.InputOp(ops, &handlers[0])
cl1 := rect.Push(ops)
event.InputOp(ops, &handlers[1])
event.InputOp(ops, &handlers[2])
cl1.Pop()
cl2 := rect.Push(ops)
event.InputOp(ops, &handlers[3])
event.InputOp(ops, &handlers[4])
cl2.Pop()
call := macro.Stop()
call.Add(ops)
fa := []event.Filter{
key.FocusFilter{},
key.Filter{Name: "A"},
}
fb := []event.Filter{
key.FocusFilter{},
key.Filter{Name: "B"},
}
events(r, &handlers[0], fa...)
events(r, &handlers[1], fb...)
events(r, &handlers[2], fa...)
events(r, &handlers[3], key.FocusFilter{})
events(r, &handlers[4], fa...)
r.Frame(ops)
h := new(int)
A, B := key.Event{Name: "A"}, key.Event{Name: "B"}
// Register filters.
events(r, -1, key.Filter{Name: "A"}, key.Filter{Name: "B"})
r.Frame(new(op.Ops))
r.Queue(A, B)
// With no focus, the events should traverse the final branch of the hit tree
// searching for handlers.
if evts := events(r, &handlers[4], fa...); len(evts) != 1 || evts[0] != A {
t.Errorf("expected key event")
}
events(r, &handlers[3], key.FocusFilter{})
events(r, &handlers[2], fa...)
if evts := events(r, &handlers[1], fb...); len(evts) != 1 || evts[0] != B {
t.Errorf("expected key event")
}
events(r, &handlers[0], fa...)
r2 := new(Router)
events(r2, &handlers[0], fa...)
events(r2, &handlers[1], fb...)
events(r2, &handlers[2], fa...)
events(r2, &handlers[3], key.FocusFilter{})
events(r2, &handlers[4], fa...)
r2.Source().Execute(key.FocusCmd{Tag: &handlers[3]})
r2.Frame(ops)
r2.Queue(A, B)
// With focus, the events should traverse the branch of the hit tree
// containing the focused element.
assertKeyEvent(t, events(r2, &handlers[3], key.FocusFilter{}), true)
if evts := events(r2, &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) {
t.Helper()
var evtFocus int
var evtKeyPress int
for _, e := range events {
switch ev := e.(type) {
case key.FocusEvent:
if ev.Focus != expectedFocus {
t.Errorf("focus is expected to be %v, got %v", expectedFocus, ev.Focus)
}
evtFocus++
case key.Event, key.EditEvent:
if len(expectedInputs) <= evtKeyPress {
t.Fatalf("unexpected key events")
}
if !reflect.DeepEqual(ev, expectedInputs[evtKeyPress]) {
t.Errorf("expected %v events, got %v", expectedInputs[evtKeyPress], ev)
}
evtKeyPress++
}
}
if evtFocus <= 0 {
t.Errorf("expected focus event")
}
if evtFocus > 1 {
t.Errorf("expected single focus event")
}
if evtKeyPress != len(expectedInputs) {
t.Errorf("expected key events")
}
}
func assertKeyEventUnexpected(t *testing.T, events []event.Event) {
t.Helper()
var evtFocus int
for _, e := range events {
switch e.(type) {
case key.FocusEvent:
evtFocus++
}
}
if evtFocus > 1 {
t.Errorf("unexpected focus event")
}
// The handler is not focused, so only B is delivered.
assertEventSequence(t, events(r, -1, key.Filter{Focus: h, Name: "A"}, key.Filter{Name: "B"}), B)
r.Source().Execute(key.FocusCmd{Tag: h})
// A is delivered to the focused handler.
assertEventSequence(t, events(r, -1, key.Filter{Focus: h, Name: "A"}, key.Filter{Name: "B"}), A)
}
func assertFocus(t *testing.T, router *Router, expected event.Tag) {
t.Helper()
if got := router.lastState().focus; got != expected {
if got := router.state().focus; got != expected {
t.Errorf("expected %v to be focused, got %v", expected, got)
}
}
func assertKeyboard(t *testing.T, router *Router, expected TextInputState) {
t.Helper()
if got := router.lastState().state; got != expected {
if got := router.state().state; got != expected {
t.Errorf("expected %v keyboard, got %v", expected, got)
}
}
+186 -153
View File
@@ -62,36 +62,7 @@ func TestPointerDrag(t *testing.T) {
Position: f32.Pt(150, 150),
},
)
assertEventPointerTypeSequence(t, events(&r, handler, f), pointer.Enter, pointer.Press, pointer.Leave, pointer.Drag)
}
func events(r *Router, h event.Tag, filters ...event.Filter) []event.Event {
// Hack to facilitate transition to per-filter tags.
for i, f := range filters {
switch f := f.(type) {
case key.FocusFilter:
f.Target = h
filters[i] = f
case transfer.SourceFilter:
f.Target = h
filters[i] = f
case transfer.TargetFilter:
f.Target = h
filters[i] = f
case pointer.Filter:
f.Target = h
filters[i] = f
}
}
var events []event.Event
for {
e, ok := r.Event(filters...)
if !ok {
break
}
events = append(events, e)
}
return events
assertEventPointerTypeSequence(t, events(&r, -1, f), pointer.Enter, pointer.Press, pointer.Leave, pointer.Drag)
}
func TestPointerDragNegative(t *testing.T) {
@@ -113,7 +84,7 @@ func TestPointerDragNegative(t *testing.T) {
Position: f32.Pt(-150, -150),
},
)
assertEventPointerTypeSequence(t, events(&r, handler, f), pointer.Enter, pointer.Press, pointer.Leave, pointer.Drag)
assertEventPointerTypeSequence(t, events(&r, -1, f), pointer.Enter, pointer.Press, pointer.Leave, pointer.Drag)
}
func TestPointerGrab(t *testing.T) {
@@ -122,16 +93,18 @@ func TestPointerGrab(t *testing.T) {
handler3 := new(int)
var ops op.Ops
filter := pointer.Filter{Kinds: pointer.Press | pointer.Release}
filter := func(t event.Tag) event.Filter {
return pointer.Filter{Target: t, Kinds: pointer.Press | pointer.Release}
}
event.InputOp(&ops, handler1)
event.InputOp(&ops, handler2)
event.InputOp(&ops, handler3)
var r Router
assertEventPointerTypeSequence(t, events(&r, handler1, filter), pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, handler2, filter), pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, handler3, filter), pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler1)), pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler2)), pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler3)), pointer.Cancel)
r.Frame(&ops)
r.Queue(
pointer.Event{
@@ -139,9 +112,9 @@ func TestPointerGrab(t *testing.T) {
Position: f32.Pt(50, 50),
},
)
assertEventPointerTypeSequence(t, events(&r, handler1, filter), pointer.Press)
assertEventPointerTypeSequence(t, events(&r, handler2, filter), pointer.Press)
assertEventPointerTypeSequence(t, events(&r, handler3, filter), pointer.Press)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler1)), pointer.Press)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler2)), pointer.Press)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler3)), pointer.Press)
r.Source().Execute(pointer.GrabCmd{Tag: handler1})
r.Frame(&ops)
r.Queue(
@@ -150,9 +123,9 @@ func TestPointerGrab(t *testing.T) {
Position: f32.Pt(50, 50),
},
)
assertEventPointerTypeSequence(t, events(&r, handler1, filter), pointer.Release)
assertEventPointerTypeSequence(t, events(&r, handler2, filter), pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, handler3, filter), pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler1)), pointer.Release)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler2)), pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler3)), pointer.Cancel)
}
func TestPointerGrabSameHandlerTwice(t *testing.T) {
@@ -160,15 +133,17 @@ func TestPointerGrabSameHandlerTwice(t *testing.T) {
handler2 := new(int)
var ops op.Ops
filter := pointer.Filter{Kinds: pointer.Press | pointer.Release}
filter := func(t event.Tag) event.Filter {
return pointer.Filter{Target: t, Kinds: pointer.Press | pointer.Release}
}
event.InputOp(&ops, handler1)
event.InputOp(&ops, handler1)
event.InputOp(&ops, handler2)
var r Router
assertEventPointerTypeSequence(t, events(&r, handler1, filter), pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, handler2, filter), pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler1)), pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler2)), pointer.Cancel)
r.Frame(&ops)
r.Queue(
pointer.Event{
@@ -176,8 +151,8 @@ func TestPointerGrabSameHandlerTwice(t *testing.T) {
Position: f32.Pt(50, 50),
},
)
assertEventPointerTypeSequence(t, events(&r, handler1, filter), pointer.Press)
assertEventPointerTypeSequence(t, events(&r, handler2, filter), pointer.Press)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler1)), pointer.Press)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler2)), pointer.Press)
r.Source().Execute(pointer.GrabCmd{Tag: handler1})
r.Frame(&ops)
r.Queue(
@@ -186,8 +161,8 @@ func TestPointerGrabSameHandlerTwice(t *testing.T) {
Position: f32.Pt(50, 50),
},
)
assertEventPointerTypeSequence(t, events(&r, handler1, filter), pointer.Release)
assertEventPointerTypeSequence(t, events(&r, handler2, filter), pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler1)), pointer.Release)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler2)), pointer.Cancel)
}
func TestPointerMove(t *testing.T) {
@@ -195,8 +170,11 @@ func TestPointerMove(t *testing.T) {
handler2 := new(int)
var ops op.Ops
f := pointer.Filter{
Kinds: pointer.Move | pointer.Enter | pointer.Leave,
filter := func(t event.Tag) event.Filter {
return pointer.Filter{
Target: t,
Kinds: pointer.Move | pointer.Enter | pointer.Leave,
}
}
// Handler 1 area: (0, 0) - (100, 100)
@@ -209,8 +187,8 @@ func TestPointerMove(t *testing.T) {
r1.Pop()
var r Router
assertEventPointerTypeSequence(t, events(&r, handler1, f), pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, handler2, f), pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler1)), pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler2)), pointer.Cancel)
r.Frame(&ops)
r.Queue(
// Hit both handlers.
@@ -232,8 +210,8 @@ func TestPointerMove(t *testing.T) {
Kind: pointer.Cancel,
},
)
assertEventPointerTypeSequence(t, events(&r, handler1, f), pointer.Enter, pointer.Move, pointer.Move, pointer.Leave, pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, handler2, f), pointer.Enter, pointer.Move, pointer.Leave, pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler1)), pointer.Enter, pointer.Move, pointer.Move, pointer.Leave, pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler2)), pointer.Enter, pointer.Move, pointer.Leave, pointer.Cancel)
}
func TestPointerTypes(t *testing.T) {
@@ -241,13 +219,14 @@ func TestPointerTypes(t *testing.T) {
var ops op.Ops
r1 := clip.Rect(image.Rect(0, 0, 100, 100)).Push(&ops)
f := pointer.Filter{
Kinds: pointer.Press | pointer.Release,
Target: handler,
Kinds: pointer.Press | pointer.Release,
}
event.InputOp(&ops, handler)
r1.Pop()
var r Router
assertEventPointerTypeSequence(t, events(&r, handler, f), pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, -1, f), pointer.Cancel)
r.Frame(&ops)
r.Queue(
pointer.Event{
@@ -263,7 +242,7 @@ func TestPointerTypes(t *testing.T) {
Position: f32.Pt(150, 150),
},
)
assertEventPointerTypeSequence(t, events(&r, handler, f), pointer.Press, pointer.Release)
assertEventPointerTypeSequence(t, events(&r, -1, f), pointer.Press, pointer.Release)
}
func TestPointerSystemAction(t *testing.T) {
@@ -311,29 +290,38 @@ func TestPointerPriority(t *testing.T) {
var r Router
r1 := clip.Rect(image.Rect(0, 0, 100, 100)).Push(&ops)
f1 := pointer.Filter{
Kinds: pointer.Scroll,
ScrollBounds: image.Rectangle{Max: image.Point{X: 100}},
f1 := func(t event.Tag) event.Filter {
return pointer.Filter{
Target: t,
Kinds: pointer.Scroll,
ScrollBounds: image.Rectangle{Max: image.Point{X: 100}},
}
}
events(&r, handler1, f1)
events(&r, -1, f1(handler1))
event.InputOp(&ops, handler1)
r2 := clip.Rect(image.Rect(0, 0, 100, 50)).Push(&ops)
f2 := pointer.Filter{
Kinds: pointer.Scroll,
ScrollBounds: image.Rectangle{Max: image.Point{X: 20}},
f2 := func(t event.Tag) event.Filter {
return pointer.Filter{
Target: t,
Kinds: pointer.Scroll,
ScrollBounds: image.Rectangle{Max: image.Point{X: 20}},
}
}
events(&r, handler2, f2)
events(&r, -1, f2(handler2))
event.InputOp(&ops, handler2)
r2.Pop()
r1.Pop()
r3 := clip.Rect(image.Rect(0, 100, 100, 200)).Push(&ops)
f3 := pointer.Filter{
Kinds: pointer.Scroll,
ScrollBounds: image.Rectangle{Min: image.Point{X: -20, Y: -40}},
f3 := func(t event.Tag) event.Filter {
return pointer.Filter{
Target: t,
Kinds: pointer.Scroll,
ScrollBounds: image.Rectangle{Min: image.Point{X: -20, Y: -40}},
}
}
events(&r, handler3, f3)
events(&r, -1, f3(handler3))
event.InputOp(&ops, handler3)
r3.Pop()
@@ -364,9 +352,9 @@ func TestPointerPriority(t *testing.T) {
},
)
hev1 := events(&r, handler1, f1)
hev2 := events(&r, handler2, f2)
hev3 := events(&r, handler3, f3)
hev1 := events(&r, -1, f1(handler1))
hev2 := events(&r, -1, f2(handler2))
hev3 := events(&r, -1, f3(handler3))
assertEventPointerTypeSequence(t, hev1, pointer.Scroll, pointer.Scroll)
assertEventPointerTypeSequence(t, hev2, pointer.Scroll)
assertEventPointerTypeSequence(t, hev3, pointer.Scroll)
@@ -402,8 +390,8 @@ func TestPointerEnterLeave(t *testing.T) {
// First event for a handler is always a Cancel.
// Only handler2 should receive the enter/move events because it is on top
// and handler1 is not an ancestor in the hit tree.
assertEventPointerTypeSequence(t, events(&r, handler1, f1))
assertEventPointerTypeSequence(t, events(&r, handler2, f2), pointer.Enter, pointer.Move)
assertEventPointerTypeSequence(t, events(&r, -1, f1))
assertEventPointerTypeSequence(t, events(&r, -1, f2), pointer.Enter, pointer.Move)
// Leave the second area by moving into the first.
r.Queue(
@@ -413,8 +401,8 @@ func TestPointerEnterLeave(t *testing.T) {
},
)
// The cursor leaves handler2 and enters handler1.
assertEventPointerTypeSequence(t, events(&r, handler1, f1), pointer.Enter, pointer.Move)
assertEventPointerTypeSequence(t, events(&r, handler2, f2), pointer.Leave)
assertEventPointerTypeSequence(t, events(&r, -1, f1), pointer.Enter, pointer.Move)
assertEventPointerTypeSequence(t, events(&r, -1, f2), pointer.Leave)
// Move, but stay within the same hit area.
r.Queue(
@@ -423,8 +411,8 @@ func TestPointerEnterLeave(t *testing.T) {
Position: f32.Pt(40, 40),
},
)
assertEventPointerTypeSequence(t, events(&r, handler1, f1), pointer.Move)
assertEventPointerTypeSequence(t, events(&r, handler2, f2))
assertEventPointerTypeSequence(t, events(&r, -1, f1), pointer.Move)
assertEventPointerTypeSequence(t, events(&r, -1, f2))
// Move outside of both inputs.
r.Queue(
@@ -433,8 +421,8 @@ func TestPointerEnterLeave(t *testing.T) {
Position: f32.Pt(300, 300),
},
)
assertEventPointerTypeSequence(t, events(&r, handler1, f1), pointer.Leave)
assertEventPointerTypeSequence(t, events(&r, handler2, f2))
assertEventPointerTypeSequence(t, events(&r, -1, f1), pointer.Leave)
assertEventPointerTypeSequence(t, events(&r, -1, f2))
// Check that a Press event generates Enter Events.
r.Queue(
@@ -443,8 +431,8 @@ func TestPointerEnterLeave(t *testing.T) {
Position: f32.Pt(125, 125),
},
)
assertEventPointerTypeSequence(t, events(&r, handler1, f1))
assertEventPointerTypeSequence(t, events(&r, handler2, f2), pointer.Enter, pointer.Press)
assertEventPointerTypeSequence(t, events(&r, -1, f1))
assertEventPointerTypeSequence(t, events(&r, -1, f2), pointer.Enter, pointer.Press)
// Check that a drag only affects the participating handlers.
r.Queue(
@@ -459,8 +447,8 @@ func TestPointerEnterLeave(t *testing.T) {
Position: f32.Pt(50, 50),
},
)
assertEventPointerTypeSequence(t, events(&r, handler1, f1))
assertEventPointerTypeSequence(t, events(&r, handler2, f2), pointer.Leave, pointer.Drag, pointer.Enter, pointer.Drag)
assertEventPointerTypeSequence(t, events(&r, -1, f1))
assertEventPointerTypeSequence(t, events(&r, -1, f2), pointer.Leave, pointer.Drag, pointer.Enter, pointer.Drag)
// Check that a Release event generates Enter/Leave Events.
r.Queue(
@@ -470,9 +458,9 @@ func TestPointerEnterLeave(t *testing.T) {
25),
},
)
assertEventPointerTypeSequence(t, events(&r, handler1, f1), pointer.Enter)
assertEventPointerTypeSequence(t, events(&r, -1, f1), pointer.Enter)
// The second handler gets the release event because the press started inside it.
assertEventPointerTypeSequence(t, events(&r, handler2, f2), pointer.Release, pointer.Leave)
assertEventPointerTypeSequence(t, events(&r, -1, f2), pointer.Release, pointer.Leave)
}
func TestMultipleAreas(t *testing.T) {
@@ -487,7 +475,7 @@ func TestMultipleAreas(t *testing.T) {
event.InputOp(&ops, handler)
r1.Pop()
assertEventPointerTypeSequence(t, events(&r, handler, f))
assertEventPointerTypeSequence(t, events(&r, -1, f))
r.Frame(&ops)
// Hit first area, then second area, then both.
r.Queue(
@@ -504,7 +492,7 @@ func TestMultipleAreas(t *testing.T) {
Position: f32.Pt(50, 50),
},
)
assertEventPointerTypeSequence(t, events(&r, handler, f), pointer.Enter, pointer.Move, pointer.Move, pointer.Move)
assertEventPointerTypeSequence(t, events(&r, -1, f), pointer.Enter, pointer.Move, pointer.Move, pointer.Move)
}
func TestPointerEnterLeaveNested(t *testing.T) {
@@ -512,7 +500,12 @@ func TestPointerEnterLeaveNested(t *testing.T) {
handler2 := new(int)
var ops op.Ops
f := pointer.Filter{Kinds: pointer.Press | pointer.Move | pointer.Release | pointer.Enter | pointer.Leave}
filter := func(t event.Tag) event.Filter {
return pointer.Filter{
Target: t,
Kinds: pointer.Press | pointer.Move | pointer.Release | pointer.Enter | pointer.Leave,
}
}
// Handler 1 area: (0, 0) - (100, 100)
r1 := clip.Rect(image.Rect(0, 0, 100, 100)).Push(&ops)
@@ -525,8 +518,8 @@ func TestPointerEnterLeaveNested(t *testing.T) {
r1.Pop()
var r Router
assertEventPointerTypeSequence(t, events(&r, handler1, f), pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, handler2, f), pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler1)), pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler2)), pointer.Cancel)
r.Frame(&ops)
// Hit both handlers.
r.Queue(
@@ -537,8 +530,8 @@ func TestPointerEnterLeaveNested(t *testing.T) {
)
// First event for a handler is always a Cancel.
// Both handlers should receive the Enter and Move events because handler2 is a child of handler1.
assertEventPointerTypeSequence(t, events(&r, handler1, f), pointer.Enter, pointer.Move)
assertEventPointerTypeSequence(t, events(&r, handler2, f), pointer.Enter, pointer.Move)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler1)), pointer.Enter, pointer.Move)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler2)), pointer.Enter, pointer.Move)
// Leave the second area by moving into the first.
r.Queue(
@@ -547,8 +540,8 @@ func TestPointerEnterLeaveNested(t *testing.T) {
Position: f32.Pt(20, 20),
},
)
assertEventPointerTypeSequence(t, events(&r, handler1, f), pointer.Move)
assertEventPointerTypeSequence(t, events(&r, handler2, f), pointer.Leave)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler1)), pointer.Move)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler2)), pointer.Leave)
// Move, but stay within the same hit area.
r.Queue(
@@ -557,8 +550,8 @@ func TestPointerEnterLeaveNested(t *testing.T) {
Position: f32.Pt(10, 10),
},
)
assertEventPointerTypeSequence(t, events(&r, handler1, f), pointer.Move)
assertEventPointerTypeSequence(t, events(&r, handler2, f))
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler1)), pointer.Move)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler2)))
// Move outside of both inputs.
r.Queue(
@@ -567,8 +560,8 @@ func TestPointerEnterLeaveNested(t *testing.T) {
Position: f32.Pt(200, 200),
},
)
assertEventPointerTypeSequence(t, events(&r, handler1, f), pointer.Leave)
assertEventPointerTypeSequence(t, events(&r, handler2, f))
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler1)), pointer.Leave)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler2)))
// Check that a Press event generates Enter Events.
r.Queue(
@@ -577,8 +570,8 @@ func TestPointerEnterLeaveNested(t *testing.T) {
Position: f32.Pt(50, 50),
},
)
assertEventPointerTypeSequence(t, events(&r, handler1, f), pointer.Enter, pointer.Press)
assertEventPointerTypeSequence(t, events(&r, handler2, f), pointer.Enter, pointer.Press)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler1)), pointer.Enter, pointer.Press)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler2)), pointer.Enter, pointer.Press)
// Check that a Release event generates Enter/Leave Events.
r.Queue(
@@ -587,8 +580,8 @@ func TestPointerEnterLeaveNested(t *testing.T) {
Position: f32.Pt(20, 20),
},
)
assertEventPointerTypeSequence(t, events(&r, handler1, f), pointer.Release)
assertEventPointerTypeSequence(t, events(&r, handler2, f), pointer.Release, pointer.Leave)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler1)), pointer.Release)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler2)), pointer.Release, pointer.Leave)
}
func TestPointerActiveInputDisappears(t *testing.T) {
@@ -606,7 +599,7 @@ func TestPointerActiveInputDisappears(t *testing.T) {
Position: f32.Pt(25, 25),
},
)
assertEventPointerTypeSequence(t, events(&r, handler1, f), pointer.Enter, pointer.Move)
assertEventPointerTypeSequence(t, events(&r, -1, f), pointer.Enter, pointer.Move)
r.Frame(&ops)
// Re-render with handler missing.
@@ -618,7 +611,7 @@ func TestPointerActiveInputDisappears(t *testing.T) {
Position: f32.Pt(25, 25),
},
)
assertEventPointerTypeSequence(t, events(&r, handler1, f), pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, -1, f), pointer.Cancel)
}
func TestMultitouch(t *testing.T) {
@@ -655,8 +648,8 @@ func TestMultitouch(t *testing.T) {
PointerID: p2,
},
)
assertEventPointerTypeSequence(t, events(&r, h1, f1), pointer.Enter, pointer.Press)
assertEventPointerTypeSequence(t, events(&r, h2, f2), pointer.Enter, pointer.Press, pointer.Release)
assertEventPointerTypeSequence(t, events(&r, -1, f1), pointer.Enter, pointer.Press)
assertEventPointerTypeSequence(t, events(&r, -1, f2), pointer.Enter, pointer.Press, pointer.Release)
}
func TestCursor(t *testing.T) {
@@ -743,7 +736,6 @@ func TestCursor(t *testing.T) {
}
r.Frame(ops)
r.Queue(tc.events...)
r.Frame(ops)
// The cursor should now have been changed if the mouse moved over the declared area.
if got, want := r.Cursor(), tc.want; got != want {
t.Errorf("got %q; want %q", got, want)
@@ -772,21 +764,23 @@ func TestPassOp(t *testing.T) {
root.Pop()
var r Router
f := pointer.Filter{Kinds: pointer.Press}
assertEventPointerTypeSequence(t, events(&r, h1, f), pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, h2, f), pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, h3, f), pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, h4, f), pointer.Cancel)
filter := func(t event.Tag) event.Filter {
return pointer.Filter{Target: t, Kinds: pointer.Press}
}
assertEventPointerTypeSequence(t, events(&r, -1, filter(h1)), pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, -1, filter(h2)), pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, -1, filter(h3)), pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, -1, filter(h4)), pointer.Cancel)
r.Frame(&ops)
r.Queue(
pointer.Event{
Kind: pointer.Press,
},
)
assertEventPointerTypeSequence(t, events(&r, h1, f), pointer.Press)
assertEventPointerTypeSequence(t, events(&r, h2, f), pointer.Press)
assertEventPointerTypeSequence(t, events(&r, h3, f), pointer.Press)
assertEventPointerTypeSequence(t, events(&r, h4, f), pointer.Press)
assertEventPointerTypeSequence(t, events(&r, -1, filter(h1)), pointer.Press)
assertEventPointerTypeSequence(t, events(&r, -1, filter(h2)), pointer.Press)
assertEventPointerTypeSequence(t, events(&r, -1, filter(h3)), pointer.Press)
assertEventPointerTypeSequence(t, events(&r, -1, filter(h4)), pointer.Press)
}
func TestAreaPassthrough(t *testing.T) {
@@ -797,16 +791,17 @@ func TestAreaPassthrough(t *testing.T) {
clip.Rect(image.Rect(0, 0, 100, 100)).Push(&ops).Pop()
var r Router
f := pointer.Filter{
Kinds: pointer.Press,
Target: h,
Kinds: pointer.Press,
}
assertEventPointerTypeSequence(t, events(&r, h, f), pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, -1, f), pointer.Cancel)
r.Frame(&ops)
r.Queue(
pointer.Event{
Kind: pointer.Press,
},
)
assertEventPointerTypeSequence(t, events(&r, h, f), pointer.Press)
assertEventPointerTypeSequence(t, events(&r, -1, f), pointer.Press)
}
func TestEllipse(t *testing.T) {
@@ -818,9 +813,10 @@ func TestEllipse(t *testing.T) {
cl.Pop()
var r Router
f := pointer.Filter{
Kinds: pointer.Press,
Target: h,
Kinds: pointer.Press,
}
assertEventPointerTypeSequence(t, events(&r, h, f), pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, -1, f), pointer.Cancel)
r.Frame(&ops)
r.Queue(
// Outside ellipse.
@@ -837,7 +833,7 @@ func TestEllipse(t *testing.T) {
Kind: pointer.Press,
},
)
assertEventPointerTypeSequence(t, events(&r, h, f), pointer.Press)
assertEventPointerTypeSequence(t, events(&r, -1, f), pointer.Press)
}
func TestTransfer(t *testing.T) {
@@ -845,8 +841,8 @@ func TestTransfer(t *testing.T) {
tgtArea := srcArea.Add(image.Pt(40, 0))
setup := func(r *Router, ops *op.Ops, srcType, tgtType string) (src, tgt event.Tag) {
src, tgt = new(int), new(int)
events(r, src, transfer.SourceFilter{Type: srcType})
events(r, tgt, transfer.TargetFilter{Type: tgtType})
events(r, -1, transfer.SourceFilter{Target: src, Type: srcType})
events(r, -1, transfer.TargetFilter{Target: tgt, Type: tgtType})
srcStack := clip.Rect(srcArea).Push(ops)
event.InputOp(ops, src)
@@ -875,8 +871,8 @@ func TestTransfer(t *testing.T) {
Kind: pointer.Move,
},
)
assertEventSequence(t, events(&r, src, transfer.SourceFilter{Type: "file"}))
assertEventSequence(t, events(&r, tgt, transfer.TargetFilter{Type: "file"}), transfer.InitiateEvent{})
assertEventSequence(t, events(&r, -1, transfer.SourceFilter{Target: src, Type: "file"}))
assertEventSequence(t, events(&r, -1, transfer.TargetFilter{Target: tgt, Type: "file"}), transfer.InitiateEvent{})
// Drop.
r.Queue(
@@ -889,8 +885,8 @@ func TestTransfer(t *testing.T) {
Kind: pointer.Release,
},
)
assertEventSequence(t, events(&r, src, transfer.SourceFilter{Type: "file"}), transfer.CancelEvent{})
assertEventSequence(t, events(&r, tgt, transfer.TargetFilter{Type: "file"}), transfer.CancelEvent{})
assertEventSequence(t, events(&r, -1, transfer.SourceFilter{Target: src, Type: "file"}), transfer.CancelEvent{})
assertEventSequence(t, events(&r, -1, transfer.TargetFilter{Target: tgt, Type: "file"}), transfer.CancelEvent{})
})
t.Run("drag with valid and invalid targets", func(t *testing.T) {
@@ -898,7 +894,7 @@ func TestTransfer(t *testing.T) {
var r Router
src, tgt1 := setup(&r, ops, "file", "file")
tgt2 := new(int)
events(&r, tgt2, transfer.TargetFilter{Type: "nofile"})
events(&r, -1, transfer.TargetFilter{Target: tgt2, Type: "nofile"})
stack := clip.Rect(tgtArea).Push(ops)
event.InputOp(ops, tgt2)
stack.Pop()
@@ -914,9 +910,9 @@ func TestTransfer(t *testing.T) {
Kind: pointer.Move,
},
)
assertEventSequence(t, events(&r, src, transfer.SourceFilter{Type: "file"}))
assertEventSequence(t, events(&r, tgt1, transfer.TargetFilter{Type: "file"}), transfer.InitiateEvent{})
assertEventSequence(t, events(&r, tgt2, transfer.TargetFilter{Type: "nofile"}))
assertEventSequence(t, events(&r, -1, transfer.SourceFilter{Target: src, Type: "file"}))
assertEventSequence(t, events(&r, -1, transfer.TargetFilter{Target: tgt1, Type: "file"}), transfer.InitiateEvent{})
assertEventSequence(t, events(&r, -1, transfer.TargetFilter{Target: tgt2, Type: "nofile"}))
})
t.Run("drop on invalid target", func(t *testing.T) {
@@ -935,8 +931,8 @@ func TestTransfer(t *testing.T) {
Kind: pointer.Move,
},
)
assertEventSequence(t, events(&r, src, transfer.SourceFilter{Type: "file"}))
assertEventSequence(t, events(&r, tgt, transfer.TargetFilter{Type: "nofile"}))
assertEventSequence(t, events(&r, -1, transfer.SourceFilter{Target: src, Type: "file"}))
assertEventSequence(t, events(&r, -1, transfer.TargetFilter{Target: tgt, Type: "nofile"}))
// Drop.
r.Queue(
@@ -945,8 +941,8 @@ func TestTransfer(t *testing.T) {
Kind: pointer.Release,
},
)
assertEventSequence(t, events(&r, src, transfer.SourceFilter{Type: "file"}), transfer.CancelEvent{})
assertEventSequence(t, events(&r, tgt, transfer.TargetFilter{Type: "nofile"}))
assertEventSequence(t, events(&r, -1, transfer.SourceFilter{Target: src, Type: "file"}), transfer.CancelEvent{})
assertEventSequence(t, events(&r, -1, transfer.TargetFilter{Target: tgt, Type: "nofile"}))
})
t.Run("drop on valid target", func(t *testing.T) {
@@ -954,7 +950,7 @@ func TestTransfer(t *testing.T) {
var r Router
src, tgt := setup(&r, ops, "file", "file")
// Make the target also a source. This should have no effect.
events(&r, tgt, transfer.SourceFilter{Type: "file"})
events(&r, -1, transfer.SourceFilter{Target: tgt, Type: "file"})
r.Frame(ops)
// Drag.
r.Queue(
@@ -967,8 +963,7 @@ func TestTransfer(t *testing.T) {
Kind: pointer.Move,
},
)
assertEventSequence(t, events(&r, src, transfer.SourceFilter{Type: "file"}))
assertEventSequence(t, events(&r, tgt, transfer.TargetFilter{Type: "file"}), transfer.InitiateEvent{})
assertEventSequence(t, events(&r, 1, transfer.TargetFilter{Target: tgt, Type: "file"}), transfer.InitiateEvent{})
// Drop.
r.Queue(
@@ -977,14 +972,13 @@ func TestTransfer(t *testing.T) {
Kind: pointer.Release,
},
)
assertEventSequence(t, events(&r, src, transfer.SourceFilter{Type: "file"}), transfer.RequestEvent{Type: "file"})
assertEventSequence(t, events(&r, 1, transfer.SourceFilter{Target: src, Type: "file"}), transfer.RequestEvent{Type: "file"})
// Offer valid type and data.
ofr := &offer{data: "hello"}
r.Source().Execute(transfer.OfferCmd{Tag: src, Type: "file", Data: ofr})
r.Frame(ops)
assertEventSequence(t, events(&r, src, transfer.SourceFilter{Type: "file"}), transfer.CancelEvent{})
evs := events(&r, tgt, transfer.TargetFilter{Type: "file"})
assertEventSequence(t, events(&r, -1, transfer.SourceFilter{Target: src, Type: "file"}), transfer.CancelEvent{})
evs := events(&r, -1, transfer.TargetFilter{Target: tgt, Type: "file"})
if len(evs) != 2 {
t.Fatalf("unexpected number of events: %d, want 2", len(evs))
}
@@ -1011,7 +1005,7 @@ func TestTransfer(t *testing.T) {
var r Router
src, tgt := setup(&r, ops, "file", "file")
// Make the target also a source. This should have no effect.
events(&r, tgt, transfer.SourceFilter{Type: "file"})
events(&r, -1, transfer.SourceFilter{Target: tgt, Type: "file"})
r.Frame(ops)
// Drag.
r.Queue(
@@ -1029,13 +1023,13 @@ func TestTransfer(t *testing.T) {
},
)
ofr := &offer{data: "hello"}
events(&r, src, transfer.SourceFilter{Type: "file"})
events(&r, tgt, transfer.TargetFilter{Type: "file"})
r.Source().Execute(transfer.OfferCmd{Tag: src, Type: "file", Data: ofr})
events(&r, -1, transfer.SourceFilter{Target: src, Type: "file"})
events(&r, -1, transfer.TargetFilter{Target: tgt, Type: "file"})
r.Frame(ops)
assertEventSequence(t, events(&r, src, transfer.SourceFilter{Type: "file"}), transfer.CancelEvent{})
r.Source().Execute(transfer.OfferCmd{Tag: src, Type: "file", Data: ofr})
assertEventSequence(t, events(&r, -1, transfer.SourceFilter{Target: src, Type: "file"}), transfer.CancelEvent{})
// Ignore DataEvent and verify that the next frame closes it as unused.
assertEventSequence(t, events(&r, tgt, transfer.TargetFilter{Type: "file"})[1:], transfer.CancelEvent{})
assertEventSequence(t, events(&r, -1, transfer.TargetFilter{Target: tgt, Type: "file"})[1:], transfer.CancelEvent{})
r.Frame(ops)
if !ofr.closed {
t.Error("offer was not closed")
@@ -1099,9 +1093,10 @@ func (o *offer) Close() error {
// rectangular area.
func addPointerHandler(r *Router, ops *op.Ops, tag event.Tag, area image.Rectangle) pointer.Filter {
f := pointer.Filter{
Kinds: pointer.Press | pointer.Release | pointer.Move | pointer.Drag | pointer.Enter | pointer.Leave,
Target: tag,
Kinds: pointer.Press | pointer.Release | pointer.Move | pointer.Drag | pointer.Enter | pointer.Leave,
}
events(r, tag, f)
events(r, -1, f)
defer clip.Rect(area).Push(ops).Pop()
event.InputOp(ops, tag)
return f
@@ -1145,6 +1140,29 @@ func assertEventSequence(t *testing.T, got []event.Event, expected ...event.Even
}
}
// assertEventTypeSequence checks that the provided event types match expected.
func assertEventTypeSequence(t *testing.T, got []event.Event, expected ...event.Event) {
t.Helper()
match := len(expected) == len(got)
if match {
for i, ge := range got {
exp := expected[i]
match = match && reflect.TypeOf(ge) == reflect.TypeOf(exp)
}
}
if !match {
t.Errorf("expected event types %s, got %s", eventTypesToString(expected), eventTypesToString(got))
}
}
func eventTypesToString(evs []event.Event) string {
var s []string
for _, e := range evs {
s = append(s, fmt.Sprintf("%T", e))
}
return "[" + strings.Join(s, ",") + "]"
}
func eventsToString(evs []event.Event) string {
var s []string
for _, ev := range evs {
@@ -1218,7 +1236,7 @@ func BenchmarkRouterAdd(b *testing.B) {
},
}).
Push(&ops)
events(&r, handlers[i], pointer.Filter{Kinds: pointer.Move})
events(&r, -1, pointer.Filter{Target: handlers[i], Kinds: pointer.Move})
event.InputOp(&ops, handlers[i])
}
r.Frame(&ops)
@@ -1235,3 +1253,18 @@ func BenchmarkRouterAdd(b *testing.B) {
})
}
}
func events(r *Router, n int, filters ...event.Filter) []event.Event {
var events []event.Event
for {
if n != -1 && len(events) == n {
break
}
e, ok := r.Event(filters...)
if !ok {
break
}
events = append(events, e)
}
return events
}
+3 -2
View File
@@ -84,8 +84,9 @@ func TestSemanticDescription(t *testing.T) {
semantic.EnabledOp(false).Add(&ops)
semantic.SelectedOp(true).Add(&ops)
var r Router
events(&r, h, pointer.Filter{
Kinds: pointer.Press | pointer.Release,
events(&r, -1, pointer.Filter{
Target: h,
Kinds: pointer.Press | pointer.Release,
})
r.Frame(&ops)
tree := r.AppendSemantics(nil)
+1 -12
View File
@@ -37,19 +37,14 @@ func TestClickable(t *testing.T) {
layout()
r.Frame(gtx.Ops)
}
// frame: request focus for button 1
gtx.Execute(key.FocusCmd{Tag: &b1})
frame()
// frame: gain focus for button 1
frame()
if !b1.Focused() {
t.Error("button 1 did not gain focus")
}
if b2.Focused() {
t.Error("button 2 should not have focus")
}
// frame: press & release return
frame()
r.Queue(
key.Event{
Name: key.NameReturn,
@@ -66,21 +61,17 @@ func TestClickable(t *testing.T) {
if b2.Clicked(gtx) {
t.Error("button 2 got clicked when it did not have focus")
}
// frame: press return down
r.Queue(
key.Event{
Name: key.NameReturn,
State: key.Press,
},
)
frame()
if b1.Clicked(gtx) {
t.Error("button 1 got clicked, even if it only got return press")
}
// frame: request focus for button 2
gtx.Execute(key.FocusCmd{Tag: &b2})
frame()
// frame: gain focus for button 2
gtx.Execute(key.FocusCmd{Tag: &b2})
frame()
if b1.Focused() {
t.Error("button 1 should not have focus")
@@ -88,14 +79,12 @@ func TestClickable(t *testing.T) {
if !b2.Focused() {
t.Error("button 2 did not gain focus")
}
// frame: release return
r.Queue(
key.Event{
Name: key.NameReturn,
State: key.Release,
},
)
frame()
if b1.Clicked(gtx) {
t.Error("button 1 got clicked, even if it had lost focus")
}
+1 -1
View File
@@ -902,11 +902,11 @@ g 2 4 6 8 g
var tim time.Duration
selected := func(start, end int) string {
gtx.Execute(key.FocusCmd{Tag: e})
// Layout once with no events; populate e.lines.
e.Layout(gtx, cache, font, fontSize, op.CallOp{}, op.CallOp{})
e.Events() // throw away any events from this layout
gtx.Execute(key.FocusCmd{Tag: e})
r.Frame(gtx.Ops)
gtx.Source = r.Source()
// Build the selection events