github.com/cybriq/giocore@v0.0.7-0.20210703034601-cfb9cb5f3900/io/router/pointer_test.go (about) 1 // SPDX-License-Identifier: Unlicense OR MIT 2 3 package router 4 5 import ( 6 "fmt" 7 "image" 8 "reflect" 9 "testing" 10 11 "github.com/cybriq/giocore/f32" 12 "github.com/cybriq/giocore/io/event" 13 "github.com/cybriq/giocore/io/key" 14 "github.com/cybriq/giocore/io/pointer" 15 "github.com/cybriq/giocore/op" 16 ) 17 18 func TestPointerWakeup(t *testing.T) { 19 handler := new(int) 20 var ops op.Ops 21 addPointerHandler(&ops, handler, image.Rect(0, 0, 100, 100)) 22 23 var r Router 24 // Test that merely adding a handler doesn't trigger redraw. 25 r.Frame(&ops) 26 if _, wake := r.WakeupTime(); wake { 27 t.Errorf("adding pointer.InputOp triggered a redraw") 28 } 29 // However, adding a handler queues a Cancel event. 30 assertEventSequence(t, r.Events(handler), pointer.Cancel) 31 // Verify that r.Events does trigger a redraw. 32 r.Frame(&ops) 33 if _, wake := r.WakeupTime(); !wake { 34 t.Errorf("pointer.Cancel event didn't trigger a redraw") 35 } 36 } 37 38 func TestPointerDrag(t *testing.T) { 39 handler := new(int) 40 var ops op.Ops 41 addPointerHandler(&ops, handler, image.Rect(0, 0, 100, 100)) 42 43 var r Router 44 r.Frame(&ops) 45 r.Queue( 46 // Press. 47 pointer.Event{ 48 Type: pointer.Press, 49 Position: f32.Pt(50, 50), 50 }, 51 // Move outside the area. 52 pointer.Event{ 53 Type: pointer.Move, 54 Position: f32.Pt(150, 150), 55 }, 56 ) 57 assertEventSequence(t, r.Events(handler), pointer.Cancel, pointer.Enter, pointer.Press, pointer.Leave, pointer.Drag) 58 } 59 60 func TestPointerDragNegative(t *testing.T) { 61 handler := new(int) 62 var ops op.Ops 63 addPointerHandler(&ops, handler, image.Rect(-100, -100, 0, 0)) 64 65 var r Router 66 r.Frame(&ops) 67 r.Queue( 68 // Press. 69 pointer.Event{ 70 Type: pointer.Press, 71 Position: f32.Pt(-50, -50), 72 }, 73 // Move outside the area. 74 pointer.Event{ 75 Type: pointer.Move, 76 Position: f32.Pt(-150, -150), 77 }, 78 ) 79 assertEventSequence(t, r.Events(handler), pointer.Cancel, pointer.Enter, pointer.Press, pointer.Leave, pointer.Drag) 80 } 81 82 func TestPointerGrab(t *testing.T) { 83 handler1 := new(int) 84 handler2 := new(int) 85 handler3 := new(int) 86 var ops op.Ops 87 88 types := pointer.Press | pointer.Release 89 90 pointer.InputOp{Tag: handler1, Types: types, Grab: true}.Add(&ops) 91 pointer.InputOp{Tag: handler2, Types: types}.Add(&ops) 92 pointer.InputOp{Tag: handler3, Types: types}.Add(&ops) 93 94 var r Router 95 r.Frame(&ops) 96 r.Queue( 97 pointer.Event{ 98 Type: pointer.Press, 99 Position: f32.Pt(50, 50), 100 }, 101 ) 102 assertEventSequence(t, r.Events(handler1), pointer.Cancel, pointer.Press) 103 assertEventSequence(t, r.Events(handler2), pointer.Cancel, pointer.Press) 104 assertEventSequence(t, r.Events(handler3), pointer.Cancel, pointer.Press) 105 r.Frame(&ops) 106 r.Queue( 107 pointer.Event{ 108 Type: pointer.Release, 109 Position: f32.Pt(50, 50), 110 }, 111 ) 112 assertEventSequence(t, r.Events(handler1), pointer.Release) 113 assertEventSequence(t, r.Events(handler2), pointer.Cancel) 114 assertEventSequence(t, r.Events(handler3), pointer.Cancel) 115 } 116 117 func TestPointerMove(t *testing.T) { 118 handler1 := new(int) 119 handler2 := new(int) 120 var ops op.Ops 121 122 types := pointer.Move | pointer.Enter | pointer.Leave 123 124 // Handler 1 area: (0, 0) - (100, 100) 125 pointer.Rect(image.Rect(0, 0, 100, 100)).Add(&ops) 126 pointer.InputOp{Tag: handler1, Types: types}.Add(&ops) 127 // Handler 2 area: (50, 50) - (100, 100) (areas intersect). 128 pointer.Rect(image.Rect(50, 50, 200, 200)).Add(&ops) 129 pointer.InputOp{Tag: handler2, Types: types}.Add(&ops) 130 131 var r Router 132 r.Frame(&ops) 133 r.Queue( 134 // Hit both handlers. 135 pointer.Event{ 136 Type: pointer.Move, 137 Position: f32.Pt(50, 50), 138 }, 139 // Hit handler 1. 140 pointer.Event{ 141 Type: pointer.Move, 142 Position: f32.Pt(49, 50), 143 }, 144 // Hit no handlers. 145 pointer.Event{ 146 Type: pointer.Move, 147 Position: f32.Pt(100, 50), 148 }, 149 pointer.Event{ 150 Type: pointer.Cancel, 151 }, 152 ) 153 assertEventSequence(t, r.Events(handler1), pointer.Cancel, pointer.Enter, pointer.Move, pointer.Move, pointer.Leave, pointer.Cancel) 154 assertEventSequence(t, r.Events(handler2), pointer.Cancel, pointer.Enter, pointer.Move, pointer.Leave, pointer.Cancel) 155 } 156 157 func TestPointerTypes(t *testing.T) { 158 handler := new(int) 159 var ops op.Ops 160 pointer.Rect(image.Rect(0, 0, 100, 100)).Add(&ops) 161 pointer.InputOp{ 162 Tag: handler, 163 Types: pointer.Press | pointer.Release, 164 }.Add(&ops) 165 166 var r Router 167 r.Frame(&ops) 168 r.Queue( 169 pointer.Event{ 170 Type: pointer.Press, 171 Position: f32.Pt(50, 50), 172 }, 173 pointer.Event{ 174 Type: pointer.Move, 175 Position: f32.Pt(150, 150), 176 }, 177 pointer.Event{ 178 Type: pointer.Release, 179 Position: f32.Pt(150, 150), 180 }, 181 ) 182 assertEventSequence(t, r.Events(handler), pointer.Cancel, pointer.Press, pointer.Release) 183 } 184 185 func TestPointerPriority(t *testing.T) { 186 handler1 := new(int) 187 handler2 := new(int) 188 handler3 := new(int) 189 var ops op.Ops 190 191 st := op.Save(&ops) 192 pointer.Rect(image.Rect(0, 0, 100, 100)).Add(&ops) 193 pointer.InputOp{ 194 Tag: handler1, 195 Types: pointer.Scroll, 196 ScrollBounds: image.Rectangle{Max: image.Point{X: 100}}, 197 }.Add(&ops) 198 199 pointer.Rect(image.Rect(0, 0, 100, 50)).Add(&ops) 200 pointer.InputOp{ 201 Tag: handler2, 202 Types: pointer.Scroll, 203 ScrollBounds: image.Rectangle{Max: image.Point{X: 20}}, 204 }.Add(&ops) 205 st.Load() 206 207 pointer.Rect(image.Rect(0, 100, 100, 200)).Add(&ops) 208 pointer.InputOp{ 209 Tag: handler3, 210 Types: pointer.Scroll, 211 ScrollBounds: image.Rectangle{Min: image.Point{X: -20, Y: -40}}, 212 }.Add(&ops) 213 214 var r Router 215 r.Frame(&ops) 216 r.Queue( 217 // Hit handler 1 and 2. 218 pointer.Event{ 219 Type: pointer.Scroll, 220 Position: f32.Pt(50, 25), 221 Scroll: f32.Pt(50, 0), 222 }, 223 // Hit handler 1. 224 pointer.Event{ 225 Type: pointer.Scroll, 226 Position: f32.Pt(50, 75), 227 Scroll: f32.Pt(50, 50), 228 }, 229 // Hit handler 3. 230 pointer.Event{ 231 Type: pointer.Scroll, 232 Position: f32.Pt(50, 150), 233 Scroll: f32.Pt(-30, -30), 234 }, 235 // Hit no handlers. 236 pointer.Event{ 237 Type: pointer.Scroll, 238 Position: f32.Pt(50, 225), 239 }, 240 ) 241 242 hev1 := r.Events(handler1) 243 hev2 := r.Events(handler2) 244 hev3 := r.Events(handler3) 245 assertEventSequence(t, hev1, pointer.Cancel, pointer.Scroll, pointer.Scroll) 246 assertEventSequence(t, hev2, pointer.Cancel, pointer.Scroll) 247 assertEventSequence(t, hev3, pointer.Cancel, pointer.Scroll) 248 assertEventPriorities(t, hev1, pointer.Shared, pointer.Shared, pointer.Foremost) 249 assertEventPriorities(t, hev2, pointer.Shared, pointer.Foremost) 250 assertEventPriorities(t, hev3, pointer.Shared, pointer.Foremost) 251 assertScrollEvent(t, hev1[1], f32.Pt(30, 0)) 252 assertScrollEvent(t, hev2[1], f32.Pt(20, 0)) 253 assertScrollEvent(t, hev1[2], f32.Pt(50, 0)) 254 assertScrollEvent(t, hev3[1], f32.Pt(-20, -30)) 255 } 256 257 func TestPointerEnterLeave(t *testing.T) { 258 handler1 := new(int) 259 handler2 := new(int) 260 var ops op.Ops 261 262 // Handler 1 area: (0, 0) - (100, 100) 263 addPointerHandler(&ops, handler1, image.Rect(0, 0, 100, 100)) 264 265 // Handler 2 area: (50, 50) - (200, 200) (areas overlap). 266 addPointerHandler(&ops, handler2, image.Rect(50, 50, 200, 200)) 267 268 var r Router 269 r.Frame(&ops) 270 // Hit both handlers. 271 r.Queue( 272 pointer.Event{ 273 Type: pointer.Move, 274 Position: f32.Pt(50, 50), 275 }, 276 ) 277 // First event for a handler is always a Cancel. 278 // Only handler2 should receive the enter/move events because it is on top 279 // and handler1 is not an ancestor in the hit tree. 280 assertEventSequence(t, r.Events(handler1), pointer.Cancel) 281 assertEventSequence(t, r.Events(handler2), pointer.Cancel, pointer.Enter, pointer.Move) 282 283 // Leave the second area by moving into the first. 284 r.Queue( 285 pointer.Event{ 286 Type: pointer.Move, 287 Position: f32.Pt(45, 45), 288 }, 289 ) 290 // The cursor leaves handler2 and enters handler1. 291 assertEventSequence(t, r.Events(handler1), pointer.Enter, pointer.Move) 292 assertEventSequence(t, r.Events(handler2), pointer.Leave) 293 294 // Move, but stay within the same hit area. 295 r.Queue( 296 pointer.Event{ 297 Type: pointer.Move, 298 Position: f32.Pt(40, 40), 299 }, 300 ) 301 assertEventSequence(t, r.Events(handler1), pointer.Move) 302 assertEventSequence(t, r.Events(handler2)) 303 304 // Move outside of both inputs. 305 r.Queue( 306 pointer.Event{ 307 Type: pointer.Move, 308 Position: f32.Pt(300, 300), 309 }, 310 ) 311 assertEventSequence(t, r.Events(handler1), pointer.Leave) 312 assertEventSequence(t, r.Events(handler2)) 313 314 // Check that a Press event generates Enter Events. 315 r.Queue( 316 pointer.Event{ 317 Type: pointer.Press, 318 Position: f32.Pt(125, 125), 319 }, 320 ) 321 assertEventSequence(t, r.Events(handler1)) 322 assertEventSequence(t, r.Events(handler2), pointer.Enter, pointer.Press) 323 324 // Check that a drag only affects the participating handlers. 325 r.Queue( 326 // Leave 327 pointer.Event{ 328 Type: pointer.Move, 329 Position: f32.Pt(25, 25), 330 }, 331 // Enter 332 pointer.Event{ 333 Type: pointer.Move, 334 Position: f32.Pt(50, 50), 335 }, 336 ) 337 assertEventSequence(t, r.Events(handler1)) 338 assertEventSequence(t, r.Events(handler2), pointer.Leave, pointer.Drag, pointer.Enter, pointer.Drag) 339 340 // Check that a Release event generates Enter/Leave Events. 341 r.Queue( 342 pointer.Event{ 343 Type: pointer.Release, 344 Position: f32.Pt(25, 345 25), 346 }, 347 ) 348 assertEventSequence(t, r.Events(handler1), pointer.Enter) 349 // The second handler gets the release event because the press started inside it. 350 assertEventSequence(t, r.Events(handler2), pointer.Release, pointer.Leave) 351 352 } 353 354 func TestMultipleAreas(t *testing.T) { 355 handler := new(int) 356 357 var ops op.Ops 358 359 addPointerHandler(&ops, handler, image.Rect(0, 0, 100, 100)) 360 st := op.Save(&ops) 361 pointer.Rect(image.Rect(50, 50, 200, 200)).Add(&ops) 362 // Second area has no Types set, yet should receive events because 363 // Types for the same handles are or-ed together. 364 pointer.InputOp{Tag: handler}.Add(&ops) 365 st.Load() 366 367 var r Router 368 r.Frame(&ops) 369 // Hit first area, then second area, then both. 370 r.Queue( 371 pointer.Event{ 372 Type: pointer.Move, 373 Position: f32.Pt(25, 25), 374 }, 375 pointer.Event{ 376 Type: pointer.Move, 377 Position: f32.Pt(150, 150), 378 }, 379 pointer.Event{ 380 Type: pointer.Move, 381 Position: f32.Pt(50, 50), 382 }, 383 ) 384 assertEventSequence(t, r.Events(handler), pointer.Cancel, pointer.Enter, pointer.Move, pointer.Move, pointer.Move) 385 } 386 387 func TestPointerEnterLeaveNested(t *testing.T) { 388 handler1 := new(int) 389 handler2 := new(int) 390 var ops op.Ops 391 392 types := pointer.Press | pointer.Move | pointer.Release | pointer.Enter | pointer.Leave 393 394 // Handler 1 area: (0, 0) - (100, 100) 395 pointer.Rect(image.Rect(0, 0, 100, 100)).Add(&ops) 396 pointer.InputOp{Tag: handler1, Types: types}.Add(&ops) 397 398 // Handler 2 area: (25, 25) - (75, 75) (nested within first). 399 pointer.Rect(image.Rect(25, 25, 75, 75)).Add(&ops) 400 pointer.InputOp{Tag: handler2, Types: types}.Add(&ops) 401 402 var r Router 403 r.Frame(&ops) 404 // Hit both handlers. 405 r.Queue( 406 pointer.Event{ 407 Type: pointer.Move, 408 Position: f32.Pt(50, 50), 409 }, 410 ) 411 // First event for a handler is always a Cancel. 412 // Both handlers should receive the Enter and Move events because handler2 is a child of handler1. 413 assertEventSequence(t, r.Events(handler1), pointer.Cancel, pointer.Enter, pointer.Move) 414 assertEventSequence(t, r.Events(handler2), pointer.Cancel, pointer.Enter, pointer.Move) 415 416 // Leave the second area by moving into the first. 417 r.Queue( 418 pointer.Event{ 419 Type: pointer.Move, 420 Position: f32.Pt(20, 20), 421 }, 422 ) 423 assertEventSequence(t, r.Events(handler1), pointer.Move) 424 assertEventSequence(t, r.Events(handler2), pointer.Leave) 425 426 // Move, but stay within the same hit area. 427 r.Queue( 428 pointer.Event{ 429 Type: pointer.Move, 430 Position: f32.Pt(10, 10), 431 }, 432 ) 433 assertEventSequence(t, r.Events(handler1), pointer.Move) 434 assertEventSequence(t, r.Events(handler2)) 435 436 // Move outside of both inputs. 437 r.Queue( 438 pointer.Event{ 439 Type: pointer.Move, 440 Position: f32.Pt(200, 200), 441 }, 442 ) 443 assertEventSequence(t, r.Events(handler1), pointer.Leave) 444 assertEventSequence(t, r.Events(handler2)) 445 446 // Check that a Press event generates Enter Events. 447 r.Queue( 448 pointer.Event{ 449 Type: pointer.Press, 450 Position: f32.Pt(50, 50), 451 }, 452 ) 453 assertEventSequence(t, r.Events(handler1), pointer.Enter, pointer.Press) 454 assertEventSequence(t, r.Events(handler2), pointer.Enter, pointer.Press) 455 456 // Check that a Release event generates Enter/Leave Events. 457 r.Queue( 458 pointer.Event{ 459 Type: pointer.Release, 460 Position: f32.Pt(20, 20), 461 }, 462 ) 463 assertEventSequence(t, r.Events(handler1), pointer.Release) 464 assertEventSequence(t, r.Events(handler2), pointer.Release, pointer.Leave) 465 } 466 467 func TestPointerActiveInputDisappears(t *testing.T) { 468 handler1 := new(int) 469 var ops op.Ops 470 var r Router 471 472 // Draw handler. 473 ops.Reset() 474 addPointerHandler(&ops, handler1, image.Rect(0, 0, 100, 100)) 475 r.Frame(&ops) 476 r.Queue( 477 pointer.Event{ 478 Type: pointer.Move, 479 Position: f32.Pt(25, 25), 480 }, 481 ) 482 assertEventSequence(t, r.Events(handler1), pointer.Cancel, pointer.Enter, pointer.Move) 483 484 // Re-render with handler missing. 485 ops.Reset() 486 r.Frame(&ops) 487 r.Queue( 488 pointer.Event{ 489 Type: pointer.Move, 490 Position: f32.Pt(25, 25), 491 }, 492 ) 493 assertEventSequence(t, r.Events(handler1)) 494 } 495 496 func TestMultitouch(t *testing.T) { 497 var ops op.Ops 498 499 // Add two separate handlers. 500 h1, h2 := new(int), new(int) 501 addPointerHandler(&ops, h1, image.Rect(0, 0, 100, 100)) 502 addPointerHandler(&ops, h2, image.Rect(0, 100, 100, 200)) 503 504 h1pt, h2pt := f32.Pt(0, 0), f32.Pt(0, 100) 505 var p1, p2 pointer.ID = 0, 1 506 507 var r Router 508 r.Frame(&ops) 509 r.Queue( 510 pointer.Event{ 511 Type: pointer.Press, 512 Position: h1pt, 513 PointerID: p1, 514 }, 515 ) 516 r.Queue( 517 pointer.Event{ 518 Type: pointer.Press, 519 Position: h2pt, 520 PointerID: p2, 521 }, 522 ) 523 r.Queue( 524 pointer.Event{ 525 Type: pointer.Release, 526 Position: h2pt, 527 PointerID: p2, 528 }, 529 ) 530 assertEventSequence(t, r.Events(h1), pointer.Cancel, pointer.Enter, pointer.Press) 531 assertEventSequence(t, r.Events(h2), pointer.Cancel, pointer.Enter, pointer.Press, pointer.Release) 532 } 533 534 func TestCursorNameOp(t *testing.T) { 535 ops := new(op.Ops) 536 var r Router 537 var h, h2 int 538 var widget2 func() 539 widget := func() { 540 // This is the area where the cursor is changed to CursorPointer. 541 pointer.Rect(image.Rectangle{Max: image.Pt(100, 100)}).Add(ops) 542 // The cursor is checked and changed upon cursor movement. 543 pointer.InputOp{Tag: &h}.Add(ops) 544 pointer.CursorNameOp{Name: pointer.CursorPointer}.Add(ops) 545 if widget2 != nil { 546 widget2() 547 } 548 } 549 // Register the handlers. 550 widget() 551 // No cursor change as the mouse has not moved yet. 552 if got, want := r.Cursor(), pointer.CursorDefault; got != want { 553 t.Errorf("got %q; want %q", got, want) 554 } 555 556 _at := func(x, y float32) pointer.Event { 557 return pointer.Event{ 558 Type: pointer.Move, 559 Source: pointer.Mouse, 560 Buttons: pointer.ButtonPrimary, 561 Position: f32.Pt(x, y), 562 } 563 } 564 for _, tc := range []struct { 565 label string 566 event interface{} 567 want pointer.CursorName 568 }{ 569 {label: "move inside", 570 event: _at(50, 50), 571 want: pointer.CursorPointer, 572 }, 573 {label: "move outside", 574 event: _at(200, 200), 575 want: pointer.CursorDefault, 576 }, 577 {label: "move back inside", 578 event: _at(50, 50), 579 want: pointer.CursorPointer, 580 }, 581 {label: "send key events while inside", 582 event: []event.Event{ 583 key.Event{Name: "A", State: key.Press}, 584 key.Event{Name: "A", State: key.Release}, 585 }, 586 want: pointer.CursorPointer, 587 }, 588 {label: "send key events while outside", 589 event: []event.Event{ 590 _at(200, 200), 591 key.Event{Name: "A", State: key.Press}, 592 key.Event{Name: "A", State: key.Release}, 593 }, 594 want: pointer.CursorDefault, 595 }, 596 {label: "add new input on top while inside", 597 event: func() []event.Event { 598 widget2 = func() { 599 pointer.InputOp{Tag: &h2}.Add(ops) 600 pointer.CursorNameOp{Name: pointer.CursorCrossHair}.Add(ops) 601 } 602 return []event.Event{ 603 _at(50, 50), 604 key.Event{ 605 Name: "A", 606 State: key.Press, 607 }, 608 } 609 }, 610 want: pointer.CursorCrossHair, 611 }, 612 {label: "remove input on top while inside", 613 event: func() []event.Event { 614 widget2 = nil 615 return []event.Event{ 616 _at(50, 50), 617 key.Event{ 618 Name: "A", 619 State: key.Press, 620 }, 621 } 622 }, 623 want: pointer.CursorPointer, 624 }, 625 } { 626 t.Run(tc.label, func(t *testing.T) { 627 ops.Reset() 628 widget() 629 r.Frame(ops) 630 switch ev := tc.event.(type) { 631 case event.Event: 632 r.Queue(ev) 633 case []event.Event: 634 r.Queue(ev...) 635 case func() event.Event: 636 r.Queue(ev()) 637 case func() []event.Event: 638 r.Queue(ev()...) 639 default: 640 panic(fmt.Sprintf("unkown event %T", ev)) 641 } 642 widget() 643 r.Frame(ops) 644 // The cursor should now have been changed if the mouse moved over the declared area. 645 if got, want := r.Cursor(), tc.want; got != want { 646 t.Errorf("got %q; want %q", got, want) 647 } 648 }) 649 } 650 } 651 652 // addPointerHandler adds a pointer.InputOp for the tag in a 653 // rectangular area. 654 func addPointerHandler(ops *op.Ops, tag event.Tag, area image.Rectangle) { 655 defer op.Save(ops).Load() 656 pointer.Rect(area).Add(ops) 657 pointer.InputOp{ 658 Tag: tag, 659 Types: pointer.Press | pointer.Release | pointer.Move | pointer.Drag | pointer.Enter | pointer.Leave, 660 }.Add(ops) 661 } 662 663 // pointerTypes converts a sequence of event.Event to their pointer.Types. It assumes 664 // that all input events are of underlying type pointer.Event, and thus will 665 // panic if some are not. 666 func pointerTypes(events []event.Event) []pointer.Type { 667 var types []pointer.Type 668 for _, e := range events { 669 if e, ok := e.(pointer.Event); ok { 670 types = append(types, e.Type) 671 } 672 } 673 return types 674 } 675 676 // assertEventSequence checks that the provided events match the expected pointer event types 677 // in the provided order. 678 func assertEventSequence(t *testing.T, events []event.Event, expected ...pointer.Type) { 679 t.Helper() 680 got := pointerTypes(events) 681 if !reflect.DeepEqual(got, expected) { 682 t.Errorf("expected %v events, got %v", expected, got) 683 } 684 } 685 686 // assertEventPriorities checks that the pointer.Event priorities of events match prios. 687 func assertEventPriorities(t *testing.T, events []event.Event, prios ...pointer.Priority) { 688 t.Helper() 689 var got []pointer.Priority 690 for _, e := range events { 691 if e, ok := e.(pointer.Event); ok { 692 got = append(got, e.Priority) 693 } 694 } 695 if !reflect.DeepEqual(got, prios) { 696 t.Errorf("expected priorities %v, got %v", prios, got) 697 } 698 } 699 700 // assertScrollEvent checks that the event scrolling amount matches the supplied value. 701 func assertScrollEvent(t *testing.T, ev event.Event, scroll f32.Point) { 702 t.Helper() 703 if got, want := ev.(pointer.Event).Scroll, scroll; got != want { 704 t.Errorf("got %v; want %v", got, want) 705 } 706 } 707 708 func BenchmarkRouterAdd(b *testing.B) { 709 // Set this to the number of overlapping handlers that you want to 710 // evaluate performance for. Typical values for the example applications 711 // are 1-3, though checking highers values helps evaluate performance for 712 // more complex applications. 713 const startingHandlerCount = 3 714 const maxHandlerCount = 100 715 for i := startingHandlerCount; i < maxHandlerCount; i *= 3 { 716 handlerCount := i 717 b.Run(fmt.Sprintf("%d-handlers", i), func(b *testing.B) { 718 handlers := make([]event.Tag, handlerCount) 719 for i := 0; i < handlerCount; i++ { 720 h := new(int) 721 *h = i 722 handlers[i] = h 723 } 724 var ops op.Ops 725 726 for i := range handlers { 727 pointer.Rect(image.Rectangle{ 728 Max: image.Point{ 729 X: 100, 730 Y: 100, 731 }, 732 }).Add(&ops) 733 pointer.InputOp{ 734 Tag: handlers[i], 735 Types: pointer.Move, 736 }.Add(&ops) 737 } 738 var r Router 739 r.Frame(&ops) 740 b.ReportAllocs() 741 b.ResetTimer() 742 for i := 0; i < b.N; i++ { 743 r.Queue( 744 pointer.Event{ 745 Type: pointer.Move, 746 Position: f32.Pt(50, 50), 747 }, 748 ) 749 } 750 }) 751 } 752 } 753 754 var benchAreaOp areaOp 755 756 func BenchmarkAreaOp_Decode(b *testing.B) { 757 ops := new(op.Ops) 758 pointer.Rect(image.Rectangle{Max: image.Pt(100, 100)}).Add(ops) 759 for i := 0; i < b.N; i++ { 760 benchAreaOp.Decode(ops.Data()) 761 } 762 } 763 764 func BenchmarkAreaOp_Hit(b *testing.B) { 765 ops := new(op.Ops) 766 pointer.Rect(image.Rectangle{Max: image.Pt(100, 100)}).Add(ops) 767 benchAreaOp.Decode(ops.Data()) 768 for i := 0; i < b.N; i++ { 769 benchAreaOp.Hit(f32.Pt(50, 50)) 770 } 771 }