Skip to content

Commit 971122b

Browse files
authored
[web] Reduce the usage of unnecessary lists in pointer binding (flutter#16745)
1 parent 92abb22 commit 971122b

File tree

3 files changed

+117
-110
lines changed

3 files changed

+117
-110
lines changed

lib/web_ui/lib/src/engine/pointer_binding.dart

Lines changed: 83 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ class _ButtonSanitizer {
308308
return _htmlButtonsToFlutterButtons(buttons);
309309
}
310310

311-
List<_SanitizedDetails> sanitizeDownEvent({
311+
_SanitizedDetails sanitizeDownEvent({
312312
@required int button,
313313
@required int buttons,
314314
}) {
@@ -319,72 +319,64 @@ class _ButtonSanitizer {
319319
}
320320

321321
_pressedButtons = _inferDownFlutterButtons(button, buttons);
322-
return <_SanitizedDetails>[
323-
_SanitizedDetails(
324-
change: ui.PointerChange.down,
325-
buttons: _pressedButtons,
326-
)
327-
];
322+
return _SanitizedDetails(
323+
change: ui.PointerChange.down,
324+
buttons: _pressedButtons,
325+
);
328326
}
329327

330-
List<_SanitizedDetails> sanitizeMoveEvent({@required int buttons}) {
328+
_SanitizedDetails sanitizeMoveEvent({@required int buttons}) {
331329
final int newPressedButtons = _htmlButtonsToFlutterButtons(buttons);
332330
// This could happen when the context menu is active and the user clicks
333331
// RMB somewhere else. The browser sends a down event with `buttons:0`.
334332
//
335333
// In this case, we keep the old `buttons` value so we don't confuse the
336334
// framework.
337335
if (_pressedButtons != 0 && newPressedButtons == 0) {
338-
return <_SanitizedDetails>[
339-
_SanitizedDetails(
340-
change: ui.PointerChange.move,
341-
buttons: _pressedButtons,
342-
)
343-
];
336+
return _SanitizedDetails(
337+
change: ui.PointerChange.move,
338+
buttons: _pressedButtons,
339+
);
344340
}
345341

346342
// This could happen when the user clicks RMB then moves the mouse quickly.
347343
// The brower sends a move event with `buttons:2` even though there's no
348344
// buttons down yet.
349345
if (_pressedButtons == 0 && newPressedButtons != 0) {
350-
return <_SanitizedDetails>[
351-
_SanitizedDetails(
352-
change: ui.PointerChange.hover,
353-
buttons: _pressedButtons,
354-
)
355-
];
346+
return _SanitizedDetails(
347+
change: ui.PointerChange.hover,
348+
buttons: _pressedButtons,
349+
);
356350
}
357351

358352
_pressedButtons = newPressedButtons;
359-
return <_SanitizedDetails>[
360-
_SanitizedDetails(
361-
change: _pressedButtons == 0
362-
? ui.PointerChange.hover
363-
: ui.PointerChange.move,
364-
buttons: _pressedButtons,
365-
)
366-
];
353+
return _SanitizedDetails(
354+
change: _pressedButtons == 0
355+
? ui.PointerChange.hover
356+
: ui.PointerChange.move,
357+
buttons: _pressedButtons,
358+
);
367359
}
368360

369-
List<_SanitizedDetails> sanitizeUpEvent() {
361+
_SanitizedDetails sanitizeUpEvent() {
370362
// The pointer could have been released by a `pointerout` event, in which
371363
// case `pointerup` should have no effect.
372364
if (_pressedButtons == 0) {
373-
return <_SanitizedDetails>[];
365+
return null;
374366
}
375367
_pressedButtons = 0;
376-
return <_SanitizedDetails>[_SanitizedDetails(
368+
return _SanitizedDetails(
377369
change: ui.PointerChange.up,
378370
buttons: _pressedButtons,
379-
)];
371+
);
380372
}
381373

382-
List<_SanitizedDetails> sanitizeCancelEvent() {
374+
_SanitizedDetails sanitizeCancelEvent() {
383375
_pressedButtons = 0;
384-
return <_SanitizedDetails>[_SanitizedDetails(
376+
return _SanitizedDetails(
385377
change: ui.PointerChange.cancel,
386378
buttons: _pressedButtons,
387-
)];
379+
);
388380
}
389381
}
390382

@@ -415,13 +407,9 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin {
415407
return sanitizer;
416408
}
417409

418-
void _removePointerIfUnhoverable(List<_SanitizedDetails> details, html.PointerEvent event) {
410+
void _removePointerIfUnhoverable(html.PointerEvent event) {
419411
if (event.pointerType == 'touch') {
420412
_sanitizers.remove(event.pointerId);
421-
details.add(_SanitizedDetails(
422-
buttons: 0,
423-
change: ui.PointerChange.remove,
424-
));
425413
}
426414
}
427415

@@ -437,32 +425,36 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin {
437425
_addPointerEventListener('pointerdown', (html.PointerEvent event) {
438426
final int device = event.pointerId;
439427
final List<ui.PointerData> pointerData = <ui.PointerData>[];
440-
final List<_SanitizedDetails> detailsList =
428+
final _SanitizedDetails details =
441429
_ensureSanitizer(device).sanitizeDownEvent(
442430
button: event.button,
443431
buttons: event.buttons,
444432
);
445-
_convertEventsToPointerData(data: pointerData, event: event, detailsList: detailsList);
433+
_convertEventsToPointerData(data: pointerData, event: event, details: details);
446434
_callback(pointerData);
447435
});
448436

449437
_addPointerEventListener('pointermove', (html.PointerEvent event) {
450438
final int device = event.pointerId;
451439
final _ButtonSanitizer sanitizer = _ensureSanitizer(device);
452440
final List<ui.PointerData> pointerData = <ui.PointerData>[];
453-
final Iterable<_SanitizedDetails> detailsList = _expandEvents(event).expand(
441+
final Iterable<_SanitizedDetails> detailsList = _expandEvents(event).map(
454442
(html.PointerEvent expandedEvent) => sanitizer.sanitizeMoveEvent(buttons: expandedEvent.buttons),
455443
);
456-
_convertEventsToPointerData(data: pointerData, event: event, detailsList: detailsList);
444+
for (_SanitizedDetails details in detailsList) {
445+
_convertEventsToPointerData(data: pointerData, event: event, details: details);
446+
}
457447
_callback(pointerData);
458448
});
459449

460450
_addPointerEventListener('pointerup', (html.PointerEvent event) {
461451
final int device = event.pointerId;
462452
final List<ui.PointerData> pointerData = <ui.PointerData>[];
463-
final List<_SanitizedDetails> detailsList = _getSanitizer(device).sanitizeUpEvent();
464-
_removePointerIfUnhoverable(detailsList, event);
465-
_convertEventsToPointerData(data: pointerData, event: event, detailsList: detailsList);
453+
final _SanitizedDetails details = _getSanitizer(device).sanitizeUpEvent();
454+
_removePointerIfUnhoverable(event);
455+
if (details != null) {
456+
_convertEventsToPointerData(data: pointerData, event: event, details: details);
457+
}
466458
_callback(pointerData);
467459
});
468460

@@ -471,9 +463,9 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin {
471463
_addPointerEventListener('pointercancel', (html.PointerEvent event) {
472464
final int device = event.pointerId;
473465
final List<ui.PointerData> pointerData = <ui.PointerData>[];
474-
final List<_SanitizedDetails> detailsList = _getSanitizer(device).sanitizeCancelEvent();
475-
_removePointerIfUnhoverable(detailsList, event);
476-
_convertEventsToPointerData(data: pointerData, event: event, detailsList: detailsList);
466+
final _SanitizedDetails details = _getSanitizer(device).sanitizeCancelEvent();
467+
_removePointerIfUnhoverable(event);
468+
_convertEventsToPointerData(data: pointerData, event: event, details: details);
477469
_callback(pointerData);
478470
});
479471

@@ -490,47 +482,45 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin {
490482
}
491483

492484
// For each event that is de-coalesced from `event` and described in
493-
// `detailsList`, convert it to pointer data and store in `data`.
485+
// `details`, convert it to pointer data and store in `data`.
494486
void _convertEventsToPointerData({
495487
@required List<ui.PointerData> data,
496488
@required html.PointerEvent event,
497-
@required Iterable<_SanitizedDetails> detailsList,
489+
@required _SanitizedDetails details,
498490
}) {
499491
assert(data != null);
500492
assert(event != null);
501-
assert(detailsList != null);
493+
assert(details != null);
502494
final ui.PointerDeviceKind kind = _pointerTypeToDeviceKind(event.pointerType);
503495
// We force `device: _mouseDeviceId` on mouse pointers because Wheel events
504496
// might come before any PointerEvents, and since wheel events don't contain
505497
// pointerId we always assign `device: _mouseDeviceId` to them.
506498
final int device = kind == ui.PointerDeviceKind.mouse ? _mouseDeviceId : event.pointerId;
507499
final double tilt = _computeHighestTilt(event);
508500
final Duration timeStamp = _BaseAdapter._eventTimeStampToDuration(event.timeStamp);
509-
for (_SanitizedDetails details in detailsList) {
510-
_pointerDataConverter.convert(
511-
data,
512-
change: details.change,
513-
timeStamp: timeStamp,
514-
kind: kind,
515-
signalKind: ui.PointerSignalKind.none,
516-
device: device,
517-
physicalX: event.client.x * ui.window.devicePixelRatio,
518-
physicalY: event.client.y * ui.window.devicePixelRatio,
519-
buttons: details.buttons,
520-
pressure: event.pressure,
521-
pressureMin: 0.0,
522-
pressureMax: 1.0,
523-
tilt: tilt,
524-
);
525-
}
501+
_pointerDataConverter.convert(
502+
data,
503+
change: details.change,
504+
timeStamp: timeStamp,
505+
kind: kind,
506+
signalKind: ui.PointerSignalKind.none,
507+
device: device,
508+
physicalX: event.client.x * ui.window.devicePixelRatio,
509+
physicalY: event.client.y * ui.window.devicePixelRatio,
510+
buttons: details.buttons,
511+
pressure: event.pressure,
512+
pressureMin: 0.0,
513+
pressureMax: 1.0,
514+
tilt: tilt,
515+
);
526516
}
527517

528518
List<html.PointerEvent> _expandEvents(html.PointerEvent event) {
529519
// For browsers that don't support `getCoalescedEvents`, we fallback to
530520
// using the original event.
531521
if (js_util.hasProperty(event, 'getCoalescedEvents')) {
532522
final List<html.PointerEvent> coalescedEvents =
533-
event.getCoalescedEvents();
523+
event.getCoalescedEvents().cast<html.PointerEvent>();
534524
// Some events don't perform coalescing, so they return an empty list. In
535525
// that case, we also fallback to using the original event.
536526
if (coalescedEvents.isNotEmpty) {
@@ -639,13 +629,6 @@ class _TouchAdapter extends _BaseAdapter {
639629
pressed: false,
640630
timeStamp: timeStamp,
641631
);
642-
_convertEventToPointerData(
643-
data: pointerData,
644-
change: ui.PointerChange.remove,
645-
touch: touch,
646-
pressed: false,
647-
timeStamp: timeStamp,
648-
);
649632
}
650633
}
651634
_callback(pointerData);
@@ -665,13 +648,6 @@ class _TouchAdapter extends _BaseAdapter {
665648
pressed: false,
666649
timeStamp: timeStamp,
667650
);
668-
_convertEventToPointerData(
669-
data: pointerData,
670-
change: ui.PointerChange.remove,
671-
touch: touch,
672-
pressed: false,
673-
timeStamp: timeStamp,
674-
);
675651
}
676652
}
677653
_callback(pointerData);
@@ -742,29 +718,29 @@ class _MouseAdapter extends _BaseAdapter with _WheelEventListenerMixin {
742718
void setup() {
743719
_addMouseEventListener('mousedown', (html.MouseEvent event) {
744720
final List<ui.PointerData> pointerData = <ui.PointerData>[];
745-
final List<_SanitizedDetails> sanitizedDetails =
721+
final _SanitizedDetails sanitizedDetails =
746722
_sanitizer.sanitizeDownEvent(
747723
button: event.button,
748724
buttons: event.buttons,
749725
);
750-
_convertEventsToPointerData(data: pointerData, event: event, detailsList: sanitizedDetails);
726+
_convertEventsToPointerData(data: pointerData, event: event, details: sanitizedDetails);
751727
_callback(pointerData);
752728
});
753729

754730
_addMouseEventListener('mousemove', (html.MouseEvent event) {
755731
final List<ui.PointerData> pointerData = <ui.PointerData>[];
756-
final List<_SanitizedDetails> sanitizedDetails = _sanitizer.sanitizeMoveEvent(buttons: event.buttons);
757-
_convertEventsToPointerData(data: pointerData, event: event, detailsList: sanitizedDetails);
732+
final _SanitizedDetails sanitizedDetails = _sanitizer.sanitizeMoveEvent(buttons: event.buttons);
733+
_convertEventsToPointerData(data: pointerData, event: event, details: sanitizedDetails);
758734
_callback(pointerData);
759735
});
760736

761737
_addMouseEventListener('mouseup', (html.MouseEvent event) {
762738
final List<ui.PointerData> pointerData = <ui.PointerData>[];
763739
final bool isEndOfDrag = event.buttons == 0;
764-
final List<_SanitizedDetails> sanitizedDetails = isEndOfDrag ?
740+
final _SanitizedDetails sanitizedDetails = isEndOfDrag ?
765741
_sanitizer.sanitizeUpEvent() :
766742
_sanitizer.sanitizeMoveEvent(buttons: event.buttons);
767-
_convertEventsToPointerData(data: pointerData, event: event, detailsList: sanitizedDetails);
743+
_convertEventsToPointerData(data: pointerData, event: event, details: sanitizedDetails);
768744
_callback(pointerData);
769745
});
770746

@@ -785,26 +761,24 @@ class _MouseAdapter extends _BaseAdapter with _WheelEventListenerMixin {
785761
void _convertEventsToPointerData({
786762
@required List<ui.PointerData> data,
787763
@required html.MouseEvent event,
788-
@required Iterable<_SanitizedDetails> detailsList,
764+
@required _SanitizedDetails details,
789765
}) {
790766
assert(data != null);
791767
assert(event != null);
792-
assert(detailsList != null);
793-
for (_SanitizedDetails details in detailsList) {
794-
_pointerDataConverter.convert(
795-
data,
796-
change: details.change,
797-
timeStamp: _BaseAdapter._eventTimeStampToDuration(event.timeStamp),
798-
kind: ui.PointerDeviceKind.mouse,
799-
signalKind: ui.PointerSignalKind.none,
800-
device: _mouseDeviceId,
801-
physicalX: event.client.x * ui.window.devicePixelRatio,
802-
physicalY: event.client.y * ui.window.devicePixelRatio,
803-
buttons: details.buttons,
804-
pressure: 1.0,
805-
pressureMin: 0.0,
806-
pressureMax: 1.0,
807-
);
808-
}
768+
assert(details != null);
769+
_pointerDataConverter.convert(
770+
data,
771+
change: details.change,
772+
timeStamp: _BaseAdapter._eventTimeStampToDuration(event.timeStamp),
773+
kind: ui.PointerDeviceKind.mouse,
774+
signalKind: ui.PointerSignalKind.none,
775+
device: _mouseDeviceId,
776+
physicalX: event.client.x * ui.window.devicePixelRatio,
777+
physicalY: event.client.y * ui.window.devicePixelRatio,
778+
buttons: details.buttons,
779+
pressure: 1.0,
780+
pressureMin: 0.0,
781+
pressureMax: 1.0,
782+
);
809783
}
810784
}

lib/web_ui/lib/src/engine/pointer_converter.dart

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,39 @@ class PointerDataConverter {
534534
scrollDeltaY: scrollDeltaY,
535535
)
536536
);
537+
if (kind == ui.PointerDeviceKind.touch) {
538+
// The browser sends a new device ID for each touch gesture. To
539+
// avoid memory leaks, we send a "remove" event when the gesture is
540+
// over (i.e. when "up" or "cancel" is received).
541+
result.add(
542+
_synthesizePointerData(
543+
timeStamp: timeStamp,
544+
change: ui.PointerChange.remove,
545+
kind: kind,
546+
device: device,
547+
physicalX: physicalX,
548+
physicalY: physicalY,
549+
buttons: 0,
550+
obscured: obscured,
551+
pressure: 0.0,
552+
pressureMin: pressureMin,
553+
pressureMax: pressureMax,
554+
distance: distance,
555+
distanceMax: distanceMax,
556+
size: size,
557+
radiusMajor: radiusMajor,
558+
radiusMinor: radiusMinor,
559+
radiusMin: radiusMin,
560+
radiusMax: radiusMax,
561+
orientation: orientation,
562+
tilt: tilt,
563+
platformData: platformData,
564+
scrollDeltaX: scrollDeltaX,
565+
scrollDeltaY: scrollDeltaY,
566+
)
567+
);
568+
_pointers.remove(device);
569+
}
537570
break;
538571
case ui.PointerChange.remove:
539572
assert(_pointers.containsKey(device));

0 commit comments

Comments
 (0)