github.com/utopiagio/gio@v0.0.8/io/input/pointer_test.go (about)

     1  // SPDX-License-Identifier: Unlicense OR MIT
     2  
     3  package input
     4  
     5  import (
     6  	"fmt"
     7  	"image"
     8  	"reflect"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/utopiagio/gio/f32"
    13  	"github.com/utopiagio/gio/io/event"
    14  	"github.com/utopiagio/gio/io/key"
    15  	"github.com/utopiagio/gio/io/pointer"
    16  	"github.com/utopiagio/gio/io/system"
    17  	"github.com/utopiagio/gio/io/transfer"
    18  	"github.com/utopiagio/gio/op"
    19  	"github.com/utopiagio/gio/op/clip"
    20  )
    21  
    22  func TestFilterReset(t *testing.T) {
    23  	r := new(Router)
    24  	if _, ok := r.Event(pointer.Filter{}); ok {
    25  		t.Fatal("empty filter matched reset event")
    26  	}
    27  	if _, ok := r.Event(pointer.Filter{Kinds: pointer.Cancel}); ok {
    28  		t.Fatal("second call to Event matched reset event")
    29  	}
    30  }
    31  
    32  func TestPointerNilTarget(t *testing.T) {
    33  	r := new(Router)
    34  	r.Event(pointer.Filter{Kinds: pointer.Press})
    35  	r.Frame(new(op.Ops))
    36  	r.Queue(pointer.Event{Kind: pointer.Press})
    37  	// Nil Targets should not receive events.
    38  	if _, ok := r.Event(pointer.Filter{Kinds: pointer.Press}); ok {
    39  		t.Errorf("nil target received event")
    40  	}
    41  }
    42  
    43  func TestPointerWakeup(t *testing.T) {
    44  	handler := new(int)
    45  	var ops op.Ops
    46  	var r Router
    47  	addPointerHandler(&r, &ops, handler, image.Rect(0, 0, 100, 100))
    48  
    49  	// Test that merely adding a handler doesn't trigger redraw.
    50  	r.Frame(&ops)
    51  	if _, wake := r.WakeupTime(); wake {
    52  		t.Errorf("adding pointer.InputOp triggered a redraw")
    53  	}
    54  }
    55  
    56  func TestPointerDrag(t *testing.T) {
    57  	handler := new(int)
    58  	var ops op.Ops
    59  	var r Router
    60  	f := addPointerHandler(&r, &ops, handler, image.Rect(0, 0, 100, 100))
    61  
    62  	r.Frame(&ops)
    63  	r.Queue(
    64  		// Press.
    65  		pointer.Event{
    66  			Kind:     pointer.Press,
    67  			Position: f32.Pt(50, 50),
    68  		},
    69  		// Move outside the area.
    70  		pointer.Event{
    71  			Kind:     pointer.Move,
    72  			Position: f32.Pt(150, 150),
    73  		},
    74  	)
    75  	assertEventPointerTypeSequence(t, events(&r, -1, f), pointer.Enter, pointer.Press, pointer.Leave, pointer.Drag)
    76  }
    77  
    78  func TestPointerDragNegative(t *testing.T) {
    79  	handler := new(int)
    80  	var ops op.Ops
    81  	var r Router
    82  	f := addPointerHandler(&r, &ops, handler, image.Rect(-100, -100, 0, 0))
    83  
    84  	r.Frame(&ops)
    85  	r.Queue(
    86  		// Press.
    87  		pointer.Event{
    88  			Kind:     pointer.Press,
    89  			Position: f32.Pt(-50, -50),
    90  		},
    91  		// Move outside the area.
    92  		pointer.Event{
    93  			Kind:     pointer.Move,
    94  			Position: f32.Pt(-150, -150),
    95  		},
    96  	)
    97  	assertEventPointerTypeSequence(t, events(&r, -1, f), pointer.Enter, pointer.Press, pointer.Leave, pointer.Drag)
    98  }
    99  
   100  func TestPointerGrab(t *testing.T) {
   101  	handler1 := new(int)
   102  	handler2 := new(int)
   103  	handler3 := new(int)
   104  	var ops op.Ops
   105  
   106  	filter := func(t event.Tag) event.Filter {
   107  		return pointer.Filter{Target: t, Kinds: pointer.Press | pointer.Release | pointer.Cancel}
   108  	}
   109  
   110  	event.Op(&ops, handler1)
   111  	event.Op(&ops, handler2)
   112  	event.Op(&ops, handler3)
   113  
   114  	var r Router
   115  	assertEventPointerTypeSequence(t, events(&r, -1, filter(handler1)), pointer.Cancel)
   116  	assertEventPointerTypeSequence(t, events(&r, -1, filter(handler2)), pointer.Cancel)
   117  	assertEventPointerTypeSequence(t, events(&r, -1, filter(handler3)), pointer.Cancel)
   118  	r.Frame(&ops)
   119  	r.Queue(
   120  		pointer.Event{
   121  			Kind:     pointer.Press,
   122  			Position: f32.Pt(50, 50),
   123  		},
   124  	)
   125  	assertEventPointerTypeSequence(t, events(&r, 1, filter(handler1)), pointer.Press)
   126  	assertEventPointerTypeSequence(t, events(&r, 1, filter(handler2)), pointer.Press)
   127  	assertEventPointerTypeSequence(t, events(&r, 1, filter(handler3)), pointer.Press)
   128  	r.Source().Execute(pointer.GrabCmd{Tag: handler1})
   129  	r.Queue(
   130  		pointer.Event{
   131  			Kind:     pointer.Release,
   132  			Position: f32.Pt(50, 50),
   133  		},
   134  	)
   135  	assertEventPointerTypeSequence(t, events(&r, -1, filter(handler1)), pointer.Release)
   136  	assertEventPointerTypeSequence(t, events(&r, -1, filter(handler2)), pointer.Cancel)
   137  	assertEventPointerTypeSequence(t, events(&r, -1, filter(handler3)), pointer.Cancel)
   138  }
   139  
   140  func TestPointerGrabSameHandlerTwice(t *testing.T) {
   141  	handler1 := new(int)
   142  	handler2 := new(int)
   143  	var ops op.Ops
   144  
   145  	filter := func(t event.Tag) event.Filter {
   146  		return pointer.Filter{Target: t, Kinds: pointer.Press | pointer.Release | pointer.Cancel}
   147  	}
   148  
   149  	event.Op(&ops, handler1)
   150  	event.Op(&ops, handler1)
   151  	event.Op(&ops, handler2)
   152  
   153  	var r Router
   154  	assertEventPointerTypeSequence(t, events(&r, -1, filter(handler1)), pointer.Cancel)
   155  	assertEventPointerTypeSequence(t, events(&r, -1, filter(handler2)), pointer.Cancel)
   156  	r.Frame(&ops)
   157  	r.Queue(
   158  		pointer.Event{
   159  			Kind:     pointer.Press,
   160  			Position: f32.Pt(50, 50),
   161  		},
   162  	)
   163  	assertEventPointerTypeSequence(t, events(&r, 1, filter(handler1)), pointer.Press)
   164  	assertEventPointerTypeSequence(t, events(&r, 1, filter(handler2)), pointer.Press)
   165  	r.Source().Execute(pointer.GrabCmd{Tag: handler1})
   166  	r.Queue(
   167  		pointer.Event{
   168  			Kind:     pointer.Release,
   169  			Position: f32.Pt(50, 50),
   170  		},
   171  	)
   172  	assertEventPointerTypeSequence(t, events(&r, -1, filter(handler1)), pointer.Release)
   173  	assertEventPointerTypeSequence(t, events(&r, -1, filter(handler2)), pointer.Cancel)
   174  }
   175  
   176  func TestPointerMove(t *testing.T) {
   177  	handler1 := new(int)
   178  	handler2 := new(int)
   179  	var ops op.Ops
   180  
   181  	filter := func(t event.Tag) event.Filter {
   182  		return pointer.Filter{
   183  			Target: t,
   184  			Kinds:  pointer.Move | pointer.Enter | pointer.Leave | pointer.Cancel,
   185  		}
   186  	}
   187  
   188  	// Handler 1 area: (0, 0) - (100, 100)
   189  	r1 := clip.Rect(image.Rect(0, 0, 100, 100)).Push(&ops)
   190  	event.Op(&ops, handler1)
   191  	// Handler 2 area: (50, 50) - (100, 100) (areas intersect).
   192  	r2 := clip.Rect(image.Rect(50, 50, 200, 200)).Push(&ops)
   193  	event.Op(&ops, handler2)
   194  	r2.Pop()
   195  	r1.Pop()
   196  
   197  	var r Router
   198  	assertEventPointerTypeSequence(t, events(&r, -1, filter(handler1)), pointer.Cancel)
   199  	assertEventPointerTypeSequence(t, events(&r, -1, filter(handler2)), pointer.Cancel)
   200  	r.Frame(&ops)
   201  	r.Queue(
   202  		// Hit both handlers.
   203  		pointer.Event{
   204  			Kind:     pointer.Move,
   205  			Position: f32.Pt(50, 50),
   206  		},
   207  		// Hit handler 1.
   208  		pointer.Event{
   209  			Kind:     pointer.Move,
   210  			Position: f32.Pt(49, 50),
   211  		},
   212  		// Hit no handlers.
   213  		pointer.Event{
   214  			Kind:     pointer.Move,
   215  			Position: f32.Pt(100, 50),
   216  		},
   217  		pointer.Event{
   218  			Kind: pointer.Cancel,
   219  		},
   220  	)
   221  	assertEventPointerTypeSequence(t, events(&r, -1, filter(handler1)), pointer.Enter, pointer.Move, pointer.Move, pointer.Leave, pointer.Cancel)
   222  	assertEventPointerTypeSequence(t, events(&r, -1, filter(handler2)), pointer.Enter, pointer.Move, pointer.Leave, pointer.Cancel)
   223  }
   224  
   225  func TestPointerTypes(t *testing.T) {
   226  	handler := new(int)
   227  	var ops op.Ops
   228  	r1 := clip.Rect(image.Rect(0, 0, 100, 100)).Push(&ops)
   229  	f := pointer.Filter{
   230  		Target: handler,
   231  		Kinds:  pointer.Press | pointer.Release | pointer.Cancel,
   232  	}
   233  	event.Op(&ops, handler)
   234  	r1.Pop()
   235  
   236  	var r Router
   237  	assertEventPointerTypeSequence(t, events(&r, -1, f), pointer.Cancel)
   238  	r.Frame(&ops)
   239  	r.Queue(
   240  		pointer.Event{
   241  			Kind:     pointer.Press,
   242  			Position: f32.Pt(50, 50),
   243  		},
   244  		pointer.Event{
   245  			Kind:     pointer.Move,
   246  			Position: f32.Pt(150, 150),
   247  		},
   248  		pointer.Event{
   249  			Kind:     pointer.Release,
   250  			Position: f32.Pt(150, 150),
   251  		},
   252  	)
   253  	assertEventPointerTypeSequence(t, events(&r, -1, f), pointer.Press, pointer.Release)
   254  }
   255  
   256  func TestPointerSystemAction(t *testing.T) {
   257  	t.Run("simple", func(t *testing.T) {
   258  		var ops op.Ops
   259  		r1 := clip.Rect(image.Rect(0, 0, 100, 100)).Push(&ops)
   260  		system.ActionInputOp(system.ActionMove).Add(&ops)
   261  		r1.Pop()
   262  
   263  		var r Router
   264  		r.Frame(&ops)
   265  		assertActionAt(t, r, f32.Pt(50, 50), system.ActionMove)
   266  	})
   267  	t.Run("covered by another clip", func(t *testing.T) {
   268  		var ops op.Ops
   269  		r1 := clip.Rect(image.Rect(0, 0, 100, 100)).Push(&ops)
   270  		system.ActionInputOp(system.ActionMove).Add(&ops)
   271  		clip.Rect(image.Rect(0, 0, 100, 100)).Push(&ops).Pop()
   272  		r1.Pop()
   273  
   274  		var r Router
   275  		r.Frame(&ops)
   276  		assertActionAt(t, r, f32.Pt(50, 50), system.ActionMove)
   277  	})
   278  	t.Run("uses topmost action op", func(t *testing.T) {
   279  		var ops op.Ops
   280  		r1 := clip.Rect(image.Rect(0, 0, 100, 100)).Push(&ops)
   281  		system.ActionInputOp(system.ActionMove).Add(&ops)
   282  		r2 := clip.Rect(image.Rect(0, 0, 100, 100)).Push(&ops)
   283  		system.ActionInputOp(system.ActionClose).Add(&ops)
   284  		r2.Pop()
   285  		r1.Pop()
   286  
   287  		var r Router
   288  		r.Frame(&ops)
   289  		assertActionAt(t, r, f32.Pt(50, 50), system.ActionClose)
   290  	})
   291  }
   292  
   293  func TestPointerPriority(t *testing.T) {
   294  	handler1 := new(int)
   295  	handler2 := new(int)
   296  	handler3 := new(int)
   297  	var ops op.Ops
   298  	var r Router
   299  
   300  	r1 := clip.Rect(image.Rect(0, 0, 100, 100)).Push(&ops)
   301  	f1 := func(t event.Tag) event.Filter {
   302  		return pointer.Filter{
   303  			Target:       t,
   304  			Kinds:        pointer.Scroll,
   305  			ScrollBounds: image.Rectangle{Max: image.Point{X: 100}},
   306  		}
   307  	}
   308  	events(&r, -1, f1(handler1))
   309  	event.Op(&ops, handler1)
   310  
   311  	r2 := clip.Rect(image.Rect(0, 0, 100, 50)).Push(&ops)
   312  	f2 := func(t event.Tag) event.Filter {
   313  		return pointer.Filter{
   314  			Target:       t,
   315  			Kinds:        pointer.Scroll,
   316  			ScrollBounds: image.Rectangle{Max: image.Point{X: 20}},
   317  		}
   318  	}
   319  	events(&r, -1, f2(handler2))
   320  	event.Op(&ops, handler2)
   321  	r2.Pop()
   322  	r1.Pop()
   323  
   324  	r3 := clip.Rect(image.Rect(0, 100, 100, 200)).Push(&ops)
   325  	f3 := func(t event.Tag) event.Filter {
   326  		return pointer.Filter{
   327  			Target:       t,
   328  			Kinds:        pointer.Scroll,
   329  			ScrollBounds: image.Rectangle{Min: image.Point{X: -20, Y: -40}},
   330  		}
   331  	}
   332  	events(&r, -1, f3(handler3))
   333  	event.Op(&ops, handler3)
   334  	r3.Pop()
   335  
   336  	r.Frame(&ops)
   337  	r.Queue(
   338  		// Hit handler 1 and 2.
   339  		pointer.Event{
   340  			Kind:     pointer.Scroll,
   341  			Position: f32.Pt(50, 25),
   342  			Scroll:   f32.Pt(50, 0),
   343  		},
   344  		// Hit handler 1.
   345  		pointer.Event{
   346  			Kind:     pointer.Scroll,
   347  			Position: f32.Pt(50, 75),
   348  			Scroll:   f32.Pt(50, 50),
   349  		},
   350  		// Hit handler 3.
   351  		pointer.Event{
   352  			Kind:     pointer.Scroll,
   353  			Position: f32.Pt(50, 150),
   354  			Scroll:   f32.Pt(-30, -30),
   355  		},
   356  		// Hit no handlers.
   357  		pointer.Event{
   358  			Kind:     pointer.Scroll,
   359  			Position: f32.Pt(50, 225),
   360  		},
   361  	)
   362  
   363  	hev1 := events(&r, -1, f1(handler1))
   364  	hev2 := events(&r, -1, f2(handler2))
   365  	hev3 := events(&r, -1, f3(handler3))
   366  	assertEventPointerTypeSequence(t, hev1, pointer.Scroll, pointer.Scroll)
   367  	assertEventPointerTypeSequence(t, hev2, pointer.Scroll)
   368  	assertEventPointerTypeSequence(t, hev3, pointer.Scroll)
   369  	assertEventPriorities(t, hev1, pointer.Shared, pointer.Foremost)
   370  	assertEventPriorities(t, hev2, pointer.Foremost)
   371  	assertEventPriorities(t, hev3, pointer.Foremost)
   372  	assertScrollEvent(t, hev1[0], f32.Pt(30, 0))
   373  	assertScrollEvent(t, hev2[0], f32.Pt(20, 0))
   374  	assertScrollEvent(t, hev1[1], f32.Pt(50, 0))
   375  	assertScrollEvent(t, hev3[0], f32.Pt(-20, -30))
   376  }
   377  
   378  func TestPointerEnterLeave(t *testing.T) {
   379  	handler1 := new(int)
   380  	handler2 := new(int)
   381  	var ops op.Ops
   382  	var r Router
   383  
   384  	// Handler 1 area: (0, 0) - (100, 100)
   385  	f1 := addPointerHandler(&r, &ops, handler1, image.Rect(0, 0, 100, 100))
   386  
   387  	// Handler 2 area: (50, 50) - (200, 200) (areas overlap).
   388  	f2 := addPointerHandler(&r, &ops, handler2, image.Rect(50, 50, 200, 200))
   389  
   390  	r.Frame(&ops)
   391  	// Hit both handlers.
   392  	r.Queue(
   393  		pointer.Event{
   394  			Kind:     pointer.Move,
   395  			Position: f32.Pt(50, 50),
   396  		},
   397  	)
   398  	// First event for a handler is always a Cancel.
   399  	// Only handler2 should receive the enter/move events because it is on top
   400  	// and handler1 is not an ancestor in the hit tree.
   401  	assertEventPointerTypeSequence(t, events(&r, -1, f1))
   402  	assertEventPointerTypeSequence(t, events(&r, -1, f2), pointer.Enter, pointer.Move)
   403  
   404  	// Leave the second area by moving into the first.
   405  	r.Queue(
   406  		pointer.Event{
   407  			Kind:     pointer.Move,
   408  			Position: f32.Pt(45, 45),
   409  		},
   410  	)
   411  	// The cursor leaves handler2 and enters handler1.
   412  	assertEventPointerTypeSequence(t, events(&r, -1, f1), pointer.Enter, pointer.Move)
   413  	assertEventPointerTypeSequence(t, events(&r, -1, f2), pointer.Leave)
   414  
   415  	// Move, but stay within the same hit area.
   416  	r.Queue(
   417  		pointer.Event{
   418  			Kind:     pointer.Move,
   419  			Position: f32.Pt(40, 40),
   420  		},
   421  	)
   422  	assertEventPointerTypeSequence(t, events(&r, -1, f1), pointer.Move)
   423  	assertEventPointerTypeSequence(t, events(&r, -1, f2))
   424  
   425  	// Move outside of both inputs.
   426  	r.Queue(
   427  		pointer.Event{
   428  			Kind:     pointer.Move,
   429  			Position: f32.Pt(300, 300),
   430  		},
   431  	)
   432  	assertEventPointerTypeSequence(t, events(&r, -1, f1), pointer.Leave)
   433  	assertEventPointerTypeSequence(t, events(&r, -1, f2))
   434  
   435  	// Check that a Press event generates Enter Events.
   436  	r.Queue(
   437  		pointer.Event{
   438  			Kind:     pointer.Press,
   439  			Position: f32.Pt(125, 125),
   440  		},
   441  	)
   442  	assertEventPointerTypeSequence(t, events(&r, -1, f1))
   443  	assertEventPointerTypeSequence(t, events(&r, -1, f2), pointer.Enter, pointer.Press)
   444  
   445  	// Check that a drag only affects the participating handlers.
   446  	r.Queue(
   447  		// Leave
   448  		pointer.Event{
   449  			Kind:     pointer.Move,
   450  			Position: f32.Pt(25, 25),
   451  		},
   452  		// Enter
   453  		pointer.Event{
   454  			Kind:     pointer.Move,
   455  			Position: f32.Pt(50, 50),
   456  		},
   457  	)
   458  	assertEventPointerTypeSequence(t, events(&r, -1, f1))
   459  	assertEventPointerTypeSequence(t, events(&r, -1, f2), pointer.Leave, pointer.Drag, pointer.Enter, pointer.Drag)
   460  
   461  	// Check that a Release event generates Enter/Leave Events.
   462  	r.Queue(
   463  		pointer.Event{
   464  			Kind: pointer.Release,
   465  			Position: f32.Pt(25,
   466  				25),
   467  		},
   468  	)
   469  	assertEventPointerTypeSequence(t, events(&r, -1, f1), pointer.Enter)
   470  	// The second handler gets the release event because the press started inside it.
   471  	assertEventPointerTypeSequence(t, events(&r, -1, f2), pointer.Release, pointer.Leave)
   472  }
   473  
   474  func TestMultipleAreas(t *testing.T) {
   475  	handler := new(int)
   476  
   477  	var ops op.Ops
   478  	var r Router
   479  
   480  	f := addPointerHandler(&r, &ops, handler, image.Rect(0, 0, 100, 100))
   481  	r1 := clip.Rect(image.Rect(50, 50, 200, 200)).Push(&ops)
   482  	// Test that declaring a handler twice doesn't affect event handling.
   483  	event.Op(&ops, handler)
   484  	r1.Pop()
   485  
   486  	assertEventPointerTypeSequence(t, events(&r, -1, f))
   487  	r.Frame(&ops)
   488  	// Hit first area, then second area, then both.
   489  	r.Queue(
   490  		pointer.Event{
   491  			Kind:     pointer.Move,
   492  			Position: f32.Pt(25, 25),
   493  		},
   494  		pointer.Event{
   495  			Kind:     pointer.Move,
   496  			Position: f32.Pt(150, 150),
   497  		},
   498  		pointer.Event{
   499  			Kind:     pointer.Move,
   500  			Position: f32.Pt(50, 50),
   501  		},
   502  	)
   503  	assertEventPointerTypeSequence(t, events(&r, -1, f), pointer.Enter, pointer.Move, pointer.Move, pointer.Move)
   504  }
   505  
   506  func TestPointerEnterLeaveNested(t *testing.T) {
   507  	handler1 := new(int)
   508  	handler2 := new(int)
   509  	var ops op.Ops
   510  
   511  	filter := func(t event.Tag) event.Filter {
   512  		return pointer.Filter{
   513  			Target: t,
   514  			Kinds:  pointer.Press | pointer.Move | pointer.Release | pointer.Enter | pointer.Leave | pointer.Cancel,
   515  		}
   516  	}
   517  
   518  	// Handler 1 area: (0, 0) - (100, 100)
   519  	r1 := clip.Rect(image.Rect(0, 0, 100, 100)).Push(&ops)
   520  	event.Op(&ops, handler1)
   521  
   522  	// Handler 2 area: (25, 25) - (75, 75) (nested within first).
   523  	r2 := clip.Rect(image.Rect(25, 25, 75, 75)).Push(&ops)
   524  	event.Op(&ops, handler2)
   525  	r2.Pop()
   526  	r1.Pop()
   527  
   528  	var r Router
   529  	assertEventPointerTypeSequence(t, events(&r, -1, filter(handler1)), pointer.Cancel)
   530  	assertEventPointerTypeSequence(t, events(&r, -1, filter(handler2)), pointer.Cancel)
   531  	r.Frame(&ops)
   532  	// Hit both handlers.
   533  	r.Queue(
   534  		pointer.Event{
   535  			Kind:     pointer.Move,
   536  			Position: f32.Pt(50, 50),
   537  		},
   538  	)
   539  	// First event for a handler is always a Cancel.
   540  	// Both handlers should receive the Enter and Move events because handler2 is a child of handler1.
   541  	assertEventPointerTypeSequence(t, events(&r, -1, filter(handler1)), pointer.Enter, pointer.Move)
   542  	assertEventPointerTypeSequence(t, events(&r, -1, filter(handler2)), pointer.Enter, pointer.Move)
   543  
   544  	// Leave the second area by moving into the first.
   545  	r.Queue(
   546  		pointer.Event{
   547  			Kind:     pointer.Move,
   548  			Position: f32.Pt(20, 20),
   549  		},
   550  	)
   551  	assertEventPointerTypeSequence(t, events(&r, -1, filter(handler1)), pointer.Move)
   552  	assertEventPointerTypeSequence(t, events(&r, -1, filter(handler2)), pointer.Leave)
   553  
   554  	// Move, but stay within the same hit area.
   555  	r.Queue(
   556  		pointer.Event{
   557  			Kind:     pointer.Move,
   558  			Position: f32.Pt(10, 10),
   559  		},
   560  	)
   561  	assertEventPointerTypeSequence(t, events(&r, -1, filter(handler1)), pointer.Move)
   562  	assertEventPointerTypeSequence(t, events(&r, -1, filter(handler2)))
   563  
   564  	// Move outside of both inputs.
   565  	r.Queue(
   566  		pointer.Event{
   567  			Kind:     pointer.Move,
   568  			Position: f32.Pt(200, 200),
   569  		},
   570  	)
   571  	assertEventPointerTypeSequence(t, events(&r, -1, filter(handler1)), pointer.Leave)
   572  	assertEventPointerTypeSequence(t, events(&r, -1, filter(handler2)))
   573  
   574  	// Check that a Press event generates Enter Events.
   575  	r.Queue(
   576  		pointer.Event{
   577  			Kind:     pointer.Press,
   578  			Position: f32.Pt(50, 50),
   579  		},
   580  	)
   581  	assertEventPointerTypeSequence(t, events(&r, -1, filter(handler1)), pointer.Enter, pointer.Press)
   582  	assertEventPointerTypeSequence(t, events(&r, -1, filter(handler2)), pointer.Enter, pointer.Press)
   583  
   584  	// Check that a Release event generates Enter/Leave Events.
   585  	r.Queue(
   586  		pointer.Event{
   587  			Kind:     pointer.Release,
   588  			Position: f32.Pt(20, 20),
   589  		},
   590  	)
   591  	assertEventPointerTypeSequence(t, events(&r, -1, filter(handler1)), pointer.Release)
   592  	assertEventPointerTypeSequence(t, events(&r, -1, filter(handler2)), pointer.Release, pointer.Leave)
   593  }
   594  
   595  func TestPointerActiveInputDisappears(t *testing.T) {
   596  	handler1 := new(int)
   597  	var ops op.Ops
   598  	var r Router
   599  
   600  	// Draw handler.
   601  	ops.Reset()
   602  	f := addPointerHandler(&r, &ops, handler1, image.Rect(0, 0, 100, 100))
   603  	r.Frame(&ops)
   604  	r.Queue(
   605  		pointer.Event{
   606  			Kind:     pointer.Move,
   607  			Position: f32.Pt(25, 25),
   608  		},
   609  	)
   610  	assertEventPointerTypeSequence(t, events(&r, -1, f), pointer.Enter, pointer.Move)
   611  	r.Frame(&ops)
   612  
   613  	// Re-render with handler missing.
   614  	ops.Reset()
   615  	r.Frame(&ops)
   616  	r.Queue(
   617  		pointer.Event{
   618  			Kind:     pointer.Move,
   619  			Position: f32.Pt(25, 25),
   620  		},
   621  	)
   622  	assertEventPointerTypeSequence(t, events(&r, -1, f), pointer.Cancel)
   623  }
   624  
   625  func TestMultitouch(t *testing.T) {
   626  	var ops op.Ops
   627  	var r Router
   628  
   629  	// Add two separate handlers.
   630  	h1, h2 := new(int), new(int)
   631  	f1 := addPointerHandler(&r, &ops, h1, image.Rect(0, 0, 100, 100))
   632  	f2 := addPointerHandler(&r, &ops, h2, image.Rect(0, 100, 100, 200))
   633  
   634  	h1pt, h2pt := f32.Pt(0, 0), f32.Pt(0, 100)
   635  	var p1, p2 pointer.ID = 0, 1
   636  
   637  	r.Frame(&ops)
   638  	r.Queue(
   639  		pointer.Event{
   640  			Kind:      pointer.Press,
   641  			Position:  h1pt,
   642  			PointerID: p1,
   643  		},
   644  	)
   645  	r.Queue(
   646  		pointer.Event{
   647  			Kind:      pointer.Press,
   648  			Position:  h2pt,
   649  			PointerID: p2,
   650  		},
   651  	)
   652  	r.Queue(
   653  		pointer.Event{
   654  			Kind:      pointer.Release,
   655  			Position:  h2pt,
   656  			PointerID: p2,
   657  		},
   658  	)
   659  	assertEventPointerTypeSequence(t, events(&r, -1, f1), pointer.Enter, pointer.Press)
   660  	assertEventPointerTypeSequence(t, events(&r, -1, f2), pointer.Enter, pointer.Press, pointer.Release)
   661  }
   662  
   663  func TestCursor(t *testing.T) {
   664  	_at := func(x, y float32) []event.Event {
   665  		return []event.Event{pointer.Event{
   666  			Kind:     pointer.Move,
   667  			Source:   pointer.Mouse,
   668  			Buttons:  pointer.ButtonPrimary,
   669  			Position: f32.Pt(x, y),
   670  		}}
   671  	}
   672  	ops := new(op.Ops)
   673  	var r Router
   674  	for _, tc := range []struct {
   675  		label   string
   676  		events  []event.Event
   677  		cursors []pointer.Cursor
   678  		want    pointer.Cursor
   679  	}{
   680  		{label: "no movement",
   681  			cursors: []pointer.Cursor{pointer.CursorPointer},
   682  			want:    pointer.CursorDefault,
   683  		},
   684  		{label: "move inside",
   685  			cursors: []pointer.Cursor{pointer.CursorPointer},
   686  			events:  _at(50, 50),
   687  			want:    pointer.CursorPointer,
   688  		},
   689  		{label: "move outside",
   690  			cursors: []pointer.Cursor{pointer.CursorPointer},
   691  			events:  _at(200, 200),
   692  			want:    pointer.CursorDefault,
   693  		},
   694  		{label: "move back inside",
   695  			cursors: []pointer.Cursor{pointer.CursorPointer},
   696  			events:  _at(50, 50),
   697  			want:    pointer.CursorPointer,
   698  		},
   699  		{label: "send key events while inside",
   700  			cursors: []pointer.Cursor{pointer.CursorPointer},
   701  			events: []event.Event{
   702  				key.Event{Name: "A", State: key.Press},
   703  				key.Event{Name: "A", State: key.Release},
   704  			},
   705  			want: pointer.CursorPointer,
   706  		},
   707  		{label: "send key events while outside",
   708  			cursors: []pointer.Cursor{pointer.CursorPointer},
   709  			events: append(
   710  				_at(200, 200),
   711  				key.Event{Name: "A", State: key.Press},
   712  				key.Event{Name: "A", State: key.Release},
   713  			),
   714  			want: pointer.CursorDefault,
   715  		},
   716  		{label: "add new input on top while inside",
   717  			cursors: []pointer.Cursor{pointer.CursorPointer, pointer.CursorCrosshair},
   718  			events: append(
   719  				_at(50, 50),
   720  				key.Event{
   721  					Name:  "A",
   722  					State: key.Press,
   723  				},
   724  			),
   725  			want: pointer.CursorCrosshair,
   726  		},
   727  		{label: "remove input on top while inside",
   728  			cursors: []pointer.Cursor{pointer.CursorPointer},
   729  			events: append(
   730  				_at(50, 50),
   731  				key.Event{
   732  					Name:  "A",
   733  					State: key.Press,
   734  				},
   735  			),
   736  			want: pointer.CursorPointer,
   737  		},
   738  	} {
   739  		t.Run(tc.label, func(t *testing.T) {
   740  			ops.Reset()
   741  			defer clip.Rect(image.Rectangle{Max: image.Pt(100, 100)}).Push(ops).Pop()
   742  			for _, c := range tc.cursors {
   743  				c.Add(ops)
   744  			}
   745  			r.Frame(ops)
   746  			r.Queue(tc.events...)
   747  			// The cursor should now have been changed if the mouse moved over the declared area.
   748  			if got, want := r.Cursor(), tc.want; got != want {
   749  				t.Errorf("got %q; want %q", got, want)
   750  			}
   751  		})
   752  	}
   753  }
   754  
   755  func TestPassOp(t *testing.T) {
   756  	var ops op.Ops
   757  
   758  	h1, h2, h3, h4 := new(int), new(int), new(int), new(int)
   759  	area := clip.Rect(image.Rect(0, 0, 100, 100))
   760  	root := area.Push(&ops)
   761  	event.Op(&ops, &h1)
   762  	event.Op(&ops, h1)
   763  	child1 := area.Push(&ops)
   764  	event.Op(&ops, h2)
   765  	child1.Pop()
   766  	child2 := area.Push(&ops)
   767  	pass := pointer.PassOp{}.Push(&ops)
   768  	event.Op(&ops, h3)
   769  	event.Op(&ops, h4)
   770  	pass.Pop()
   771  	child2.Pop()
   772  	root.Pop()
   773  
   774  	var r Router
   775  	filter := func(t event.Tag) event.Filter {
   776  		return pointer.Filter{Target: t, Kinds: pointer.Press | pointer.Cancel}
   777  	}
   778  	assertEventPointerTypeSequence(t, events(&r, -1, filter(h1)), pointer.Cancel)
   779  	assertEventPointerTypeSequence(t, events(&r, -1, filter(h2)), pointer.Cancel)
   780  	assertEventPointerTypeSequence(t, events(&r, -1, filter(h3)), pointer.Cancel)
   781  	assertEventPointerTypeSequence(t, events(&r, -1, filter(h4)), pointer.Cancel)
   782  	r.Frame(&ops)
   783  	r.Queue(
   784  		pointer.Event{
   785  			Kind: pointer.Press,
   786  		},
   787  	)
   788  	assertEventPointerTypeSequence(t, events(&r, -1, filter(h1)), pointer.Press)
   789  	assertEventPointerTypeSequence(t, events(&r, -1, filter(h2)), pointer.Press)
   790  	assertEventPointerTypeSequence(t, events(&r, -1, filter(h3)), pointer.Press)
   791  	assertEventPointerTypeSequence(t, events(&r, -1, filter(h4)), pointer.Press)
   792  }
   793  
   794  func TestAreaPassthrough(t *testing.T) {
   795  	var ops op.Ops
   796  
   797  	h := new(int)
   798  	event.Op(&ops, h)
   799  	clip.Rect(image.Rect(0, 0, 100, 100)).Push(&ops).Pop()
   800  	var r Router
   801  	f := pointer.Filter{
   802  		Target: h,
   803  		Kinds:  pointer.Press | pointer.Cancel,
   804  	}
   805  	assertEventPointerTypeSequence(t, events(&r, -1, f), pointer.Cancel)
   806  	r.Frame(&ops)
   807  	r.Queue(
   808  		pointer.Event{
   809  			Kind: pointer.Press,
   810  		},
   811  	)
   812  	assertEventPointerTypeSequence(t, events(&r, -1, f), pointer.Press)
   813  }
   814  
   815  func TestEllipse(t *testing.T) {
   816  	var ops op.Ops
   817  
   818  	h := new(int)
   819  	cl := clip.Ellipse(image.Rect(0, 0, 100, 100)).Push(&ops)
   820  	event.Op(&ops, h)
   821  	cl.Pop()
   822  	var r Router
   823  	f := pointer.Filter{
   824  		Target: h,
   825  		Kinds:  pointer.Press | pointer.Cancel,
   826  	}
   827  	assertEventPointerTypeSequence(t, events(&r, -1, f), pointer.Cancel)
   828  	r.Frame(&ops)
   829  	r.Queue(
   830  		// Outside ellipse.
   831  		pointer.Event{
   832  			Position: f32.Pt(10, 10),
   833  			Kind:     pointer.Press,
   834  		},
   835  		pointer.Event{
   836  			Kind: pointer.Release,
   837  		},
   838  		// Inside ellipse.
   839  		pointer.Event{
   840  			Position: f32.Pt(50, 50),
   841  			Kind:     pointer.Press,
   842  		},
   843  	)
   844  	assertEventPointerTypeSequence(t, events(&r, -1, f), pointer.Press)
   845  }
   846  
   847  func TestTransfer(t *testing.T) {
   848  	srcArea := image.Rect(0, 0, 20, 20)
   849  	tgtArea := srcArea.Add(image.Pt(40, 0))
   850  	setup := func(r *Router, ops *op.Ops, srcType, tgtType string) (src, tgt event.Tag) {
   851  		src, tgt = new(int), new(int)
   852  		events(r, -1, transfer.SourceFilter{Target: src, Type: srcType})
   853  		events(r, -1, transfer.TargetFilter{Target: tgt, Type: tgtType})
   854  
   855  		srcStack := clip.Rect(srcArea).Push(ops)
   856  		event.Op(ops, src)
   857  		srcStack.Pop()
   858  
   859  		tgt1Stack := clip.Rect(tgtArea).Push(ops)
   860  		event.Op(ops, tgt)
   861  		tgt1Stack.Pop()
   862  
   863  		return src, tgt
   864  	}
   865  
   866  	t.Run("drop on no target", func(t *testing.T) {
   867  		ops := new(op.Ops)
   868  		var r Router
   869  		src, tgt := setup(&r, ops, "file", "file")
   870  		r.Frame(ops)
   871  		// Initiate a drag.
   872  		r.Queue(
   873  			pointer.Event{
   874  				Position: f32.Pt(10, 10),
   875  				Kind:     pointer.Press,
   876  			},
   877  			pointer.Event{
   878  				Position: f32.Pt(10, 10),
   879  				Kind:     pointer.Move,
   880  			},
   881  		)
   882  		assertEventSequence(t, events(&r, -1, transfer.SourceFilter{Target: src, Type: "file"}))
   883  		assertEventSequence(t, events(&r, -1, transfer.TargetFilter{Target: tgt, Type: "file"}), transfer.InitiateEvent{})
   884  
   885  		// Drop.
   886  		r.Queue(
   887  			pointer.Event{
   888  				Position: f32.Pt(30, 10),
   889  				Kind:     pointer.Move,
   890  			},
   891  			pointer.Event{
   892  				Position: f32.Pt(30, 10),
   893  				Kind:     pointer.Release,
   894  			},
   895  		)
   896  		assertEventSequence(t, events(&r, -1, transfer.SourceFilter{Target: src, Type: "file"}), transfer.CancelEvent{})
   897  		assertEventSequence(t, events(&r, -1, transfer.TargetFilter{Target: tgt, Type: "file"}), transfer.CancelEvent{})
   898  	})
   899  
   900  	t.Run("drag with valid and invalid targets", func(t *testing.T) {
   901  		ops := new(op.Ops)
   902  		var r Router
   903  		src, tgt1 := setup(&r, ops, "file", "file")
   904  		tgt2 := new(int)
   905  		events(&r, -1, transfer.TargetFilter{Target: tgt2, Type: "nofile"})
   906  		stack := clip.Rect(tgtArea).Push(ops)
   907  		event.Op(ops, tgt2)
   908  		stack.Pop()
   909  		r.Frame(ops)
   910  		// Initiate a drag.
   911  		r.Queue(
   912  			pointer.Event{
   913  				Position: f32.Pt(10, 10),
   914  				Kind:     pointer.Press,
   915  			},
   916  			pointer.Event{
   917  				Position: f32.Pt(10, 10),
   918  				Kind:     pointer.Move,
   919  			},
   920  		)
   921  		assertEventSequence(t, events(&r, -1, transfer.SourceFilter{Target: src, Type: "file"}))
   922  		assertEventSequence(t, events(&r, -1, transfer.TargetFilter{Target: tgt1, Type: "file"}), transfer.InitiateEvent{})
   923  		assertEventSequence(t, events(&r, -1, transfer.TargetFilter{Target: tgt2, Type: "nofile"}))
   924  	})
   925  
   926  	t.Run("drop on invalid target", func(t *testing.T) {
   927  		ops := new(op.Ops)
   928  		var r Router
   929  		src, tgt := setup(&r, ops, "file", "nofile")
   930  		r.Frame(ops)
   931  		// Drag.
   932  		r.Queue(
   933  			pointer.Event{
   934  				Position: f32.Pt(10, 10),
   935  				Kind:     pointer.Press,
   936  			},
   937  			pointer.Event{
   938  				Position: f32.Pt(10, 10),
   939  				Kind:     pointer.Move,
   940  			},
   941  		)
   942  		assertEventSequence(t, events(&r, -1, transfer.SourceFilter{Target: src, Type: "file"}))
   943  		assertEventSequence(t, events(&r, -1, transfer.TargetFilter{Target: tgt, Type: "nofile"}))
   944  
   945  		// Drop.
   946  		r.Queue(
   947  			pointer.Event{
   948  				Position: f32.Pt(40, 10),
   949  				Kind:     pointer.Release,
   950  			},
   951  		)
   952  		assertEventSequence(t, events(&r, -1, transfer.SourceFilter{Target: src, Type: "file"}), transfer.CancelEvent{})
   953  		assertEventSequence(t, events(&r, -1, transfer.TargetFilter{Target: tgt, Type: "nofile"}))
   954  	})
   955  
   956  	t.Run("drop on valid target", func(t *testing.T) {
   957  		ops := new(op.Ops)
   958  		var r Router
   959  		src, tgt := setup(&r, ops, "file", "file")
   960  		// Make the target also a source. This should have no effect.
   961  		events(&r, -1, transfer.SourceFilter{Target: tgt, Type: "file"})
   962  		r.Frame(ops)
   963  		// Drag.
   964  		r.Queue(
   965  			pointer.Event{
   966  				Position: f32.Pt(10, 10),
   967  				Kind:     pointer.Press,
   968  			},
   969  			pointer.Event{
   970  				Position: f32.Pt(10, 10),
   971  				Kind:     pointer.Move,
   972  			},
   973  		)
   974  		assertEventSequence(t, events(&r, 1, transfer.TargetFilter{Target: tgt, Type: "file"}), transfer.InitiateEvent{})
   975  
   976  		// Drop.
   977  		r.Queue(
   978  			pointer.Event{
   979  				Position: f32.Pt(40, 10),
   980  				Kind:     pointer.Release,
   981  			},
   982  		)
   983  		assertEventSequence(t, events(&r, 1, transfer.SourceFilter{Target: src, Type: "file"}), transfer.RequestEvent{Type: "file"})
   984  
   985  		// Offer valid type and data.
   986  		ofr := &offer{data: "hello"}
   987  		r.Source().Execute(transfer.OfferCmd{Tag: src, Type: "file", Data: ofr})
   988  		assertEventSequence(t, events(&r, -1, transfer.SourceFilter{Target: src, Type: "file"}), transfer.CancelEvent{})
   989  		evs := events(&r, -1, transfer.TargetFilter{Target: tgt, Type: "file"})
   990  		if len(evs) != 2 {
   991  			t.Fatalf("unexpected number of events: %d, want 2", len(evs))
   992  		}
   993  		assertEventSequence(t, evs[1:], transfer.CancelEvent{})
   994  		dataEvent, ok := evs[0].(transfer.DataEvent)
   995  		if !ok {
   996  			t.Fatalf("unexpected event type: %T, want %T", dataEvent, transfer.DataEvent{})
   997  		}
   998  		if got, want := dataEvent.Type, "file"; got != want {
   999  			t.Fatalf("got %s; want %s", got, want)
  1000  		}
  1001  		if got, want := dataEvent.Open(), ofr; got != want {
  1002  			t.Fatalf("got %v; want %v", got, want)
  1003  		}
  1004  
  1005  		// Drag and drop complete.
  1006  		if ofr.closed {
  1007  			t.Error("offer closed prematurely")
  1008  		}
  1009  	})
  1010  
  1011  	t.Run("drop on valid target, DataEvent not used", func(t *testing.T) {
  1012  		ops := new(op.Ops)
  1013  		var r Router
  1014  		src, tgt := setup(&r, ops, "file", "file")
  1015  		// Make the target also a source. This should have no effect.
  1016  		events(&r, -1, transfer.SourceFilter{Target: tgt, Type: "file"})
  1017  		r.Frame(ops)
  1018  		// Drag.
  1019  		r.Queue(
  1020  			pointer.Event{
  1021  				Position: f32.Pt(10, 10),
  1022  				Kind:     pointer.Press,
  1023  			},
  1024  			pointer.Event{
  1025  				Position: f32.Pt(10, 10),
  1026  				Kind:     pointer.Move,
  1027  			},
  1028  			pointer.Event{
  1029  				Position: f32.Pt(40, 10),
  1030  				Kind:     pointer.Release,
  1031  			},
  1032  		)
  1033  		ofr := &offer{data: "hello"}
  1034  		events(&r, -1, transfer.SourceFilter{Target: src, Type: "file"})
  1035  		events(&r, -1, transfer.TargetFilter{Target: tgt, Type: "file"})
  1036  		r.Frame(ops)
  1037  		r.Source().Execute(transfer.OfferCmd{Tag: src, Type: "file", Data: ofr})
  1038  		assertEventSequence(t, events(&r, -1, transfer.SourceFilter{Target: src, Type: "file"}), transfer.CancelEvent{})
  1039  		// Ignore DataEvent and verify that the next frame closes it as unused.
  1040  		assertEventSequence(t, events(&r, -1, transfer.TargetFilter{Target: tgt, Type: "file"})[1:], transfer.CancelEvent{})
  1041  		r.Frame(ops)
  1042  		if !ofr.closed {
  1043  			t.Error("offer was not closed")
  1044  		}
  1045  	})
  1046  }
  1047  
  1048  func TestDeferredInputOp(t *testing.T) {
  1049  	var ops op.Ops
  1050  
  1051  	var r Router
  1052  	m := op.Record(&ops)
  1053  	event.Op(&ops, new(int))
  1054  	call := m.Stop()
  1055  
  1056  	op.Defer(&ops, call)
  1057  	r.Frame(&ops)
  1058  }
  1059  
  1060  func TestPassCursor(t *testing.T) {
  1061  	var ops op.Ops
  1062  	var r Router
  1063  
  1064  	rect := clip.Rect(image.Rect(0, 0, 100, 100))
  1065  	background := rect.Push(&ops)
  1066  	event.Op(&ops, 1)
  1067  	pointer.CursorDefault.Add(&ops)
  1068  	background.Pop()
  1069  
  1070  	overlayPass := pointer.PassOp{}.Push(&ops)
  1071  	overlay := rect.Push(&ops)
  1072  	event.Op(&ops, 2)
  1073  	want := pointer.CursorPointer
  1074  	want.Add(&ops)
  1075  	overlay.Pop()
  1076  	overlayPass.Pop()
  1077  	r.Frame(&ops)
  1078  	r.Queue(pointer.Event{
  1079  		Position: f32.Pt(10, 10),
  1080  		Kind:     pointer.Move,
  1081  	})
  1082  	r.Frame(&ops)
  1083  	if got := r.Cursor(); want != got {
  1084  		t.Errorf("got cursor %v, want %v", got, want)
  1085  	}
  1086  }
  1087  
  1088  // offer satisfies io.ReadCloser for use in data transfers.
  1089  type offer struct {
  1090  	data   string
  1091  	closed bool
  1092  }
  1093  
  1094  func (offer) Read([]byte) (int, error) { return 0, nil }
  1095  func (o *offer) Close() error {
  1096  	o.closed = true
  1097  	return nil
  1098  }
  1099  
  1100  // addPointerHandler adds a pointer.InputOp for the tag in a
  1101  // rectangular area.
  1102  func addPointerHandler(r *Router, ops *op.Ops, tag event.Tag, area image.Rectangle) pointer.Filter {
  1103  	f := pointer.Filter{
  1104  		Target: tag,
  1105  		Kinds:  pointer.Press | pointer.Release | pointer.Move | pointer.Drag | pointer.Enter | pointer.Leave | pointer.Cancel,
  1106  	}
  1107  	events(r, -1, f)
  1108  	defer clip.Rect(area).Push(ops).Pop()
  1109  	event.Op(ops, tag)
  1110  	return f
  1111  }
  1112  
  1113  // pointerTypes converts a sequence of event.Event to their pointer.Types. It assumes
  1114  // that all input events are of underlying type pointer.Event, and thus will
  1115  // panic if some are not.
  1116  func pointerTypes(events []event.Event) []pointer.Kind {
  1117  	var types []pointer.Kind
  1118  	for _, e := range events {
  1119  		if e, ok := e.(pointer.Event); ok {
  1120  			types = append(types, e.Kind)
  1121  		}
  1122  	}
  1123  	return types
  1124  }
  1125  
  1126  // assertEventPointerTypeSequence checks that the provided events match the expected pointer event types
  1127  // in the provided order.
  1128  func assertEventPointerTypeSequence(t *testing.T, events []event.Event, expected ...pointer.Kind) {
  1129  	t.Helper()
  1130  	got := pointerTypes(events)
  1131  	if !reflect.DeepEqual(got, expected) {
  1132  		t.Errorf("expected %v events, got %v", expected, got)
  1133  	}
  1134  }
  1135  
  1136  // assertEventSequence checks that the provided events match the expected ones
  1137  // in the provided order.
  1138  func assertEventSequence(t *testing.T, got []event.Event, expected ...event.Event) {
  1139  	t.Helper()
  1140  	if len(expected) == 0 {
  1141  		if len(got) > 0 {
  1142  			t.Errorf("unexpected events: %v", eventsToString(got))
  1143  		}
  1144  		return
  1145  	}
  1146  	if !reflect.DeepEqual(got, expected) {
  1147  		t.Errorf("expected %s events, got %s", eventsToString(expected), eventsToString(got))
  1148  	}
  1149  }
  1150  
  1151  // assertEventTypeSequence checks that the provided event types match expected.
  1152  func assertEventTypeSequence(t *testing.T, got []event.Event, expected ...event.Event) {
  1153  	t.Helper()
  1154  	match := len(expected) == len(got)
  1155  	if match {
  1156  		for i, ge := range got {
  1157  			exp := expected[i]
  1158  			match = match && reflect.TypeOf(ge) == reflect.TypeOf(exp)
  1159  		}
  1160  	}
  1161  	if !match {
  1162  		t.Errorf("expected event types %s, got %s", eventTypesToString(expected), eventTypesToString(got))
  1163  	}
  1164  }
  1165  
  1166  func eventTypesToString(evs []event.Event) string {
  1167  	var s []string
  1168  	for _, e := range evs {
  1169  		s = append(s, fmt.Sprintf("%T", e))
  1170  	}
  1171  	return "[" + strings.Join(s, ",") + "]"
  1172  }
  1173  
  1174  func eventsToString(evs []event.Event) string {
  1175  	var s []string
  1176  	for _, ev := range evs {
  1177  		switch e := ev.(type) {
  1178  		case pointer.Event:
  1179  			s = append(s, fmt.Sprintf("%T{%s}", e, e.Kind.String()))
  1180  		default:
  1181  			s = append(s, fmt.Sprintf("{%T}", e))
  1182  		}
  1183  	}
  1184  	return "[" + strings.Join(s, ",") + "]"
  1185  }
  1186  
  1187  // assertEventPriorities checks that the pointer.Event priorities of events match prios.
  1188  func assertEventPriorities(t *testing.T, events []event.Event, prios ...pointer.Priority) {
  1189  	t.Helper()
  1190  	var got []pointer.Priority
  1191  	for _, e := range events {
  1192  		if e, ok := e.(pointer.Event); ok {
  1193  			got = append(got, e.Priority)
  1194  		}
  1195  	}
  1196  	if !reflect.DeepEqual(got, prios) {
  1197  		t.Errorf("expected priorities %v, got %v", prios, got)
  1198  	}
  1199  }
  1200  
  1201  // assertScrollEvent checks that the event scrolling amount matches the supplied value.
  1202  func assertScrollEvent(t *testing.T, ev event.Event, scroll f32.Point) {
  1203  	t.Helper()
  1204  	if got, want := ev.(pointer.Event).Scroll, scroll; got != want {
  1205  		t.Errorf("got %v; want %v", got, want)
  1206  	}
  1207  }
  1208  
  1209  // assertActionAt checks that the router has a system action of the expected type at point.
  1210  func assertActionAt(t *testing.T, q Router, point f32.Point, expected system.Action) {
  1211  	t.Helper()
  1212  	action, ok := q.ActionAt(point)
  1213  	if !ok {
  1214  		t.Errorf("expected action %v at %v, got no action", expected, point)
  1215  	} else if action != expected {
  1216  		t.Errorf("expected action %v at %v, got %v", expected, point, action)
  1217  	}
  1218  }
  1219  
  1220  func BenchmarkRouterAdd(b *testing.B) {
  1221  	// Set this to the number of overlapping handlers that you want to
  1222  	// evaluate performance for. Typical values for the example applications
  1223  	// are 1-3, though checking highers values helps evaluate performance for
  1224  	// more complex applications.
  1225  	const startingHandlerCount = 3
  1226  	const maxHandlerCount = 100
  1227  	for i := startingHandlerCount; i < maxHandlerCount; i *= 3 {
  1228  		handlerCount := i
  1229  		b.Run(fmt.Sprintf("%d-handlers", i), func(b *testing.B) {
  1230  			handlers := make([]event.Tag, handlerCount)
  1231  			for i := 0; i < handlerCount; i++ {
  1232  				h := new(int)
  1233  				*h = i
  1234  				handlers[i] = h
  1235  			}
  1236  			var ops op.Ops
  1237  
  1238  			var r Router
  1239  			for i := range handlers {
  1240  				clip.Rect(image.Rectangle{
  1241  					Max: image.Point{
  1242  						X: 100,
  1243  						Y: 100,
  1244  					},
  1245  				}).
  1246  					Push(&ops)
  1247  				events(&r, -1, pointer.Filter{Target: handlers[i], Kinds: pointer.Move})
  1248  				event.Op(&ops, handlers[i])
  1249  			}
  1250  			r.Frame(&ops)
  1251  			b.ReportAllocs()
  1252  			b.ResetTimer()
  1253  			for i := 0; i < b.N; i++ {
  1254  				r.Queue(
  1255  					pointer.Event{
  1256  						Kind:     pointer.Move,
  1257  						Position: f32.Pt(50, 50),
  1258  					},
  1259  				)
  1260  			}
  1261  		})
  1262  	}
  1263  }
  1264  
  1265  func events(r *Router, n int, filters ...event.Filter) []event.Event {
  1266  	var events []event.Event
  1267  	for {
  1268  		if n != -1 && len(events) == n {
  1269  			break
  1270  		}
  1271  		e, ok := r.Event(filters...)
  1272  		if !ok {
  1273  			break
  1274  		}
  1275  		events = append(events, e)
  1276  	}
  1277  	return events
  1278  }