diff --git a/io/input/clipboard_test.go b/io/input/clipboard_test.go index ca0fcf9d..9dc8ab52 100644 --- a/io/input/clipboard_test.go +++ b/io/input/clipboard_test.go @@ -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 { diff --git a/io/input/key_test.go b/io/input/key_test.go index 5c88f1bc..bb18ab5e 100644 --- a/io/input/key_test.go +++ b/io/input/key_test.go @@ -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) } } diff --git a/io/input/pointer_test.go b/io/input/pointer_test.go index 309f5e6c..484bb240 100644 --- a/io/input/pointer_test.go +++ b/io/input/pointer_test.go @@ -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 +} diff --git a/io/input/semantic_test.go b/io/input/semantic_test.go index 732d58a9..98af5285 100644 --- a/io/input/semantic_test.go +++ b/io/input/semantic_test.go @@ -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) diff --git a/widget/button_test.go b/widget/button_test.go index 7c8b52fd..a86c1536 100644 --- a/widget/button_test.go +++ b/widget/button_test.go @@ -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") } diff --git a/widget/editor_test.go b/widget/editor_test.go index 1fd94b1f..7021b97a 100644 --- a/widget/editor_test.go +++ b/widget/editor_test.go @@ -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