pointer_test.go raw
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/p9c/p9/pkg/gel/gio/f32"
12 "github.com/p9c/p9/pkg/gel/gio/io/event"
13 "github.com/p9c/p9/pkg/gel/gio/io/key"
14 "github.com/p9c/p9/pkg/gel/gio/io/pointer"
15 "github.com/p9c/p9/pkg/gel/gio/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 }
772