Unverified Commit cff67336 authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

Add viewId to PointerEvents (#128287)

Follow-up to https://github.com/flutter/engine/pull/42493.
parent 31e3ae89
...@@ -288,7 +288,7 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H ...@@ -288,7 +288,7 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H
// We convert pointer data to logical pixels so that e.g. the touch slop can be // We convert pointer data to logical pixels so that e.g. the touch slop can be
// defined in a device-independent manner. // defined in a device-independent manner.
try { try {
_pendingPointerEvents.addAll(PointerEventConverter.expand(packet.data, platformDispatcher.implicitView!.devicePixelRatio)); _pendingPointerEvents.addAll(PointerEventConverter.expand(packet.data, _devicePixelRatioForView));
if (!locked) { if (!locked) {
_flushPointerEventQueue(); _flushPointerEventQueue();
} }
...@@ -302,6 +302,10 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H ...@@ -302,6 +302,10 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H
} }
} }
double? _devicePixelRatioForView(int viewId) {
return platformDispatcher.view(id: viewId)?.devicePixelRatio;
}
/// Dispatch a [PointerCancelEvent] for the given pointer soon. /// Dispatch a [PointerCancelEvent] for the given pointer soon.
/// ///
/// The pointer event will be dispatched before the next pointer event and /// The pointer event will be dispatched before the next pointer event and
...@@ -368,7 +372,7 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H ...@@ -368,7 +372,7 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H
if (event is PointerDownEvent || event is PointerSignalEvent || event is PointerHoverEvent || event is PointerPanZoomStartEvent) { if (event is PointerDownEvent || event is PointerSignalEvent || event is PointerHoverEvent || event is PointerPanZoomStartEvent) {
assert(!_hitTests.containsKey(event.pointer), 'Pointer of ${event.toString(minLevel: DiagnosticLevel.debug)} unexpectedly has a HitTestResult associated with it.'); assert(!_hitTests.containsKey(event.pointer), 'Pointer of ${event.toString(minLevel: DiagnosticLevel.debug)} unexpectedly has a HitTestResult associated with it.');
hitTestResult = HitTestResult(); hitTestResult = HitTestResult();
hitTest(hitTestResult, event.position); hitTestInView(hitTestResult, event.position, event.viewId);
if (event is PointerDownEvent || event is PointerPanZoomStartEvent) { if (event is PointerDownEvent || event is PointerPanZoomStartEvent) {
_hitTests[event.pointer] = hitTestResult; _hitTests[event.pointer] = hitTestResult;
} }
...@@ -401,12 +405,22 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H ...@@ -401,12 +405,22 @@ mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, H
} }
} }
/// Determine which [HitTestTarget] objects are located at a given position. /// Determine which [HitTestTarget] objects are located at a given position in
/// the specified view.
@override // from HitTestable @override // from HitTestable
void hitTest(HitTestResult result, Offset position) { void hitTestInView(HitTestResult result, Offset position, int viewId) {
result.add(HitTestEntry(this)); result.add(HitTestEntry(this));
} }
@override // from HitTestable
@Deprecated(
'Use hitTestInView and specify the view to hit test. '
'This feature was deprecated after v3.11.0-20.0.pre.',
)
void hitTest(HitTestResult result, Offset position) {
hitTestInView(result, position, platformDispatcher.implicitView!.viewId);
}
/// Dispatch an event to [pointerRouter] and the path of a hit test result. /// Dispatch an event to [pointerRouter] and the path of a hit test result.
/// ///
/// The `event` is routed to [pointerRouter]. If the `hitTestResult` is not /// The `event` is routed to [pointerRouter]. If the `hitTestResult` is not
......
...@@ -32,6 +32,18 @@ int _synthesiseDownButtons(int buttons, PointerDeviceKind kind) { ...@@ -32,6 +32,18 @@ int _synthesiseDownButtons(int buttons, PointerDeviceKind kind) {
} }
} }
/// Signature for a callback that returns the device pixel ratio of a
/// [FlutterView] identified by the provided `viewId`.
///
/// Returns null if no view with the provided ID exists.
///
/// Used by [PointerEventConverter.expand].
///
/// See also:
///
/// * [FlutterView.devicePixelRatio] for an explanation of device pixel ratio.
typedef DevicePixelRatioGetter = double? Function(int viewId);
/// Converts from engine pointer data to framework pointer events. /// Converts from engine pointer data to framework pointer events.
/// ///
/// This takes [PointerDataPacket] objects, as received from the engine via /// This takes [PointerDataPacket] objects, as received from the engine via
...@@ -45,10 +57,15 @@ abstract final class PointerEventConverter { ...@@ -45,10 +57,15 @@ abstract final class PointerEventConverter {
/// [dart:ui.FlutterView.devicePixelRatio]) is used to convert the incoming data /// [dart:ui.FlutterView.devicePixelRatio]) is used to convert the incoming data
/// from physical coordinates to logical pixels. See the discussion at /// from physical coordinates to logical pixels. See the discussion at
/// [PointerEvent] for more details on the [PointerEvent] coordinate space. /// [PointerEvent] for more details on the [PointerEvent] coordinate space.
static Iterable<PointerEvent> expand(Iterable<ui.PointerData> data, double devicePixelRatio) { static Iterable<PointerEvent> expand(Iterable<ui.PointerData> data, DevicePixelRatioGetter devicePixelRatioForView) {
return data return data
.where((ui.PointerData datum) => datum.signalKind != ui.PointerSignalKind.unknown) .where((ui.PointerData datum) => datum.signalKind != ui.PointerSignalKind.unknown)
.map<PointerEvent?>((ui.PointerData datum) { .map<PointerEvent?>((ui.PointerData datum) {
final double? devicePixelRatio = devicePixelRatioForView(datum.viewId);
if (devicePixelRatio == null) {
// View doesn't exist anymore.
return null;
}
final Offset position = Offset(datum.physicalX, datum.physicalY) / devicePixelRatio; final Offset position = Offset(datum.physicalX, datum.physicalY) / devicePixelRatio;
final Offset delta = Offset(datum.physicalDeltaX, datum.physicalDeltaY) / devicePixelRatio; final Offset delta = Offset(datum.physicalDeltaX, datum.physicalDeltaY) / devicePixelRatio;
final double radiusMinor = _toLogicalPixels(datum.radiusMinor, devicePixelRatio); final double radiusMinor = _toLogicalPixels(datum.radiusMinor, devicePixelRatio);
...@@ -62,6 +79,7 @@ abstract final class PointerEventConverter { ...@@ -62,6 +79,7 @@ abstract final class PointerEventConverter {
switch (datum.change) { switch (datum.change) {
case ui.PointerChange.add: case ui.PointerChange.add:
return PointerAddedEvent( return PointerAddedEvent(
viewId: datum.viewId,
timeStamp: timeStamp, timeStamp: timeStamp,
kind: kind, kind: kind,
device: datum.device, device: datum.device,
...@@ -79,6 +97,7 @@ abstract final class PointerEventConverter { ...@@ -79,6 +97,7 @@ abstract final class PointerEventConverter {
); );
case ui.PointerChange.hover: case ui.PointerChange.hover:
return PointerHoverEvent( return PointerHoverEvent(
viewId: datum.viewId,
timeStamp: timeStamp, timeStamp: timeStamp,
kind: kind, kind: kind,
device: datum.device, device: datum.device,
...@@ -102,6 +121,7 @@ abstract final class PointerEventConverter { ...@@ -102,6 +121,7 @@ abstract final class PointerEventConverter {
); );
case ui.PointerChange.down: case ui.PointerChange.down:
return PointerDownEvent( return PointerDownEvent(
viewId: datum.viewId,
timeStamp: timeStamp, timeStamp: timeStamp,
pointer: datum.pointerIdentifier, pointer: datum.pointerIdentifier,
kind: kind, kind: kind,
...@@ -124,6 +144,7 @@ abstract final class PointerEventConverter { ...@@ -124,6 +144,7 @@ abstract final class PointerEventConverter {
); );
case ui.PointerChange.move: case ui.PointerChange.move:
return PointerMoveEvent( return PointerMoveEvent(
viewId: datum.viewId,
timeStamp: timeStamp, timeStamp: timeStamp,
pointer: datum.pointerIdentifier, pointer: datum.pointerIdentifier,
kind: kind, kind: kind,
...@@ -149,6 +170,7 @@ abstract final class PointerEventConverter { ...@@ -149,6 +170,7 @@ abstract final class PointerEventConverter {
); );
case ui.PointerChange.up: case ui.PointerChange.up:
return PointerUpEvent( return PointerUpEvent(
viewId: datum.viewId,
timeStamp: timeStamp, timeStamp: timeStamp,
pointer: datum.pointerIdentifier, pointer: datum.pointerIdentifier,
kind: kind, kind: kind,
...@@ -172,6 +194,7 @@ abstract final class PointerEventConverter { ...@@ -172,6 +194,7 @@ abstract final class PointerEventConverter {
); );
case ui.PointerChange.cancel: case ui.PointerChange.cancel:
return PointerCancelEvent( return PointerCancelEvent(
viewId: datum.viewId,
timeStamp: timeStamp, timeStamp: timeStamp,
pointer: datum.pointerIdentifier, pointer: datum.pointerIdentifier,
kind: kind, kind: kind,
...@@ -194,6 +217,7 @@ abstract final class PointerEventConverter { ...@@ -194,6 +217,7 @@ abstract final class PointerEventConverter {
); );
case ui.PointerChange.remove: case ui.PointerChange.remove:
return PointerRemovedEvent( return PointerRemovedEvent(
viewId: datum.viewId,
timeStamp: timeStamp, timeStamp: timeStamp,
kind: kind, kind: kind,
device: datum.device, device: datum.device,
...@@ -208,6 +232,7 @@ abstract final class PointerEventConverter { ...@@ -208,6 +232,7 @@ abstract final class PointerEventConverter {
); );
case ui.PointerChange.panZoomStart: case ui.PointerChange.panZoomStart:
return PointerPanZoomStartEvent( return PointerPanZoomStartEvent(
viewId: datum.viewId,
timeStamp: timeStamp, timeStamp: timeStamp,
pointer: datum.pointerIdentifier, pointer: datum.pointerIdentifier,
device: datum.device, device: datum.device,
...@@ -221,6 +246,7 @@ abstract final class PointerEventConverter { ...@@ -221,6 +246,7 @@ abstract final class PointerEventConverter {
final Offset panDelta = final Offset panDelta =
Offset(datum.panDeltaX, datum.panDeltaY) / devicePixelRatio; Offset(datum.panDeltaX, datum.panDeltaY) / devicePixelRatio;
return PointerPanZoomUpdateEvent( return PointerPanZoomUpdateEvent(
viewId: datum.viewId,
timeStamp: timeStamp, timeStamp: timeStamp,
pointer: datum.pointerIdentifier, pointer: datum.pointerIdentifier,
device: datum.device, device: datum.device,
...@@ -234,6 +260,7 @@ abstract final class PointerEventConverter { ...@@ -234,6 +260,7 @@ abstract final class PointerEventConverter {
); );
case ui.PointerChange.panZoomEnd: case ui.PointerChange.panZoomEnd:
return PointerPanZoomEndEvent( return PointerPanZoomEndEvent(
viewId: datum.viewId,
timeStamp: timeStamp, timeStamp: timeStamp,
pointer: datum.pointerIdentifier, pointer: datum.pointerIdentifier,
device: datum.device, device: datum.device,
...@@ -249,6 +276,7 @@ abstract final class PointerEventConverter { ...@@ -249,6 +276,7 @@ abstract final class PointerEventConverter {
final Offset scrollDelta = final Offset scrollDelta =
Offset(datum.scrollDeltaX, datum.scrollDeltaY) / devicePixelRatio; Offset(datum.scrollDeltaX, datum.scrollDeltaY) / devicePixelRatio;
return PointerScrollEvent( return PointerScrollEvent(
viewId: datum.viewId,
timeStamp: timeStamp, timeStamp: timeStamp,
kind: kind, kind: kind,
device: datum.device, device: datum.device,
...@@ -258,6 +286,7 @@ abstract final class PointerEventConverter { ...@@ -258,6 +286,7 @@ abstract final class PointerEventConverter {
); );
case ui.PointerSignalKind.scrollInertiaCancel: case ui.PointerSignalKind.scrollInertiaCancel:
return PointerScrollInertiaCancelEvent( return PointerScrollInertiaCancelEvent(
viewId: datum.viewId,
timeStamp: timeStamp, timeStamp: timeStamp,
kind: kind, kind: kind,
device: datum.device, device: datum.device,
...@@ -266,6 +295,7 @@ abstract final class PointerEventConverter { ...@@ -266,6 +295,7 @@ abstract final class PointerEventConverter {
); );
case ui.PointerSignalKind.scale: case ui.PointerSignalKind.scale:
return PointerScaleEvent( return PointerScaleEvent(
viewId: datum.viewId,
timeStamp: timeStamp, timeStamp: timeStamp,
kind: kind, kind: kind,
device: datum.device, device: datum.device,
......
...@@ -16,11 +16,16 @@ export 'events.dart' show PointerEvent; ...@@ -16,11 +16,16 @@ export 'events.dart' show PointerEvent;
/// An object that can hit-test pointers. /// An object that can hit-test pointers.
abstract interface class HitTestable { abstract interface class HitTestable {
/// Check whether the given position hits this object. /// Deprecated. Use [hitTestInView] instead.
/// @Deprecated(
/// If this given position hits this object, consider adding a [HitTestEntry] 'Use hitTestInView and specify the view to hit test. '
/// to the given hit test result. 'This feature was deprecated after v3.11.0-20.0.pre.',
)
void hitTest(HitTestResult result, Offset position); void hitTest(HitTestResult result, Offset position);
/// Fills the provided [HitTestResult] with [HitTestEntry]s for objects that
/// are hit at the given `position` in the view identified by `viewId`.
void hitTestInView(HitTestResult result, Offset position, int viewId);
} }
/// An object that can dispatch events. /// An object that can dispatch events.
......
...@@ -54,6 +54,7 @@ class PointerEventResampler { ...@@ -54,6 +54,7 @@ class PointerEventResampler {
int buttons, int buttons,
) { ) {
return PointerHoverEvent( return PointerHoverEvent(
viewId: event.viewId,
timeStamp: timeStamp, timeStamp: timeStamp,
kind: event.kind, kind: event.kind,
device: event.device, device: event.device,
...@@ -86,6 +87,7 @@ class PointerEventResampler { ...@@ -86,6 +87,7 @@ class PointerEventResampler {
int buttons, int buttons,
) { ) {
return PointerMoveEvent( return PointerMoveEvent(
viewId: event.viewId,
timeStamp: timeStamp, timeStamp: timeStamp,
pointer: pointerIdentifier, pointer: pointerIdentifier,
kind: event.kind, kind: event.kind,
......
...@@ -519,9 +519,10 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture ...@@ -519,9 +519,10 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture
} }
@override @override
void hitTest(HitTestResult result, Offset position) { void hitTestInView(HitTestResult result, Offset position, int viewId) {
assert(viewId == renderView.flutterView.viewId);
renderView.hitTest(result, position: position); renderView.hitTest(result, position: position);
super.hitTest(result, position); super.hitTestInView(result, position, viewId);
} }
Future<void> _forceRepaint() { Future<void> _forceRepaint() {
......
...@@ -103,6 +103,8 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox> ...@@ -103,6 +103,8 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
markNeedsLayout(); markNeedsLayout();
} }
/// The [FlutterView] into which this [RenderView] will render.
ui.FlutterView get flutterView => _view;
final ui.FlutterView _view; final ui.FlutterView _view;
/// Whether Flutter should automatically compute the desired system UI. /// Whether Flutter should automatically compute the desired system UI.
......
...@@ -13,6 +13,7 @@ import 'debug.dart'; ...@@ -13,6 +13,7 @@ import 'debug.dart';
import 'framework.dart'; import 'framework.dart';
import 'media_query.dart'; import 'media_query.dart';
import 'overlay.dart'; import 'overlay.dart';
import 'view.dart';
/// Signature for determining whether the given data will be accepted by a [DragTarget]. /// Signature for determining whether the given data will be accepted by a [DragTarget].
/// ///
...@@ -497,6 +498,7 @@ class _DraggableState<T extends Object> extends State<Draggable<T>> { ...@@ -497,6 +498,7 @@ class _DraggableState<T extends Object> extends State<Draggable<T>> {
feedbackOffset: widget.feedbackOffset, feedbackOffset: widget.feedbackOffset,
ignoringFeedbackSemantics: widget.ignoringFeedbackSemantics, ignoringFeedbackSemantics: widget.ignoringFeedbackSemantics,
ignoringFeedbackPointer: widget.ignoringFeedbackPointer, ignoringFeedbackPointer: widget.ignoringFeedbackPointer,
viewId: View.of(context).viewId,
onDragUpdate: (DragUpdateDetails details) { onDragUpdate: (DragUpdateDetails details) {
if (mounted && widget.onDragUpdate != null) { if (mounted && widget.onDragUpdate != null) {
widget.onDragUpdate!(details); widget.onDragUpdate!(details);
...@@ -756,6 +758,7 @@ class _DragAvatar<T extends Object> extends Drag { ...@@ -756,6 +758,7 @@ class _DragAvatar<T extends Object> extends Drag {
this.onDragEnd, this.onDragEnd,
required this.ignoringFeedbackSemantics, required this.ignoringFeedbackSemantics,
required this.ignoringFeedbackPointer, required this.ignoringFeedbackPointer,
required this.viewId,
}) : _position = initialPosition { }) : _position = initialPosition {
_entry = OverlayEntry(builder: _build); _entry = OverlayEntry(builder: _build);
overlayState.insert(_entry!); overlayState.insert(_entry!);
...@@ -772,6 +775,7 @@ class _DragAvatar<T extends Object> extends Drag { ...@@ -772,6 +775,7 @@ class _DragAvatar<T extends Object> extends Drag {
final OverlayState overlayState; final OverlayState overlayState;
final bool ignoringFeedbackSemantics; final bool ignoringFeedbackSemantics;
final bool ignoringFeedbackPointer; final bool ignoringFeedbackPointer;
final int viewId;
_DragTargetState<Object>? _activeTarget; _DragTargetState<Object>? _activeTarget;
final List<_DragTargetState<Object>> _enteredTargets = <_DragTargetState<Object>>[]; final List<_DragTargetState<Object>> _enteredTargets = <_DragTargetState<Object>>[];
...@@ -804,7 +808,7 @@ class _DragAvatar<T extends Object> extends Drag { ...@@ -804,7 +808,7 @@ class _DragAvatar<T extends Object> extends Drag {
_lastOffset = globalPosition - dragStartPoint; _lastOffset = globalPosition - dragStartPoint;
_entry!.markNeedsBuild(); _entry!.markNeedsBuild();
final HitTestResult result = HitTestResult(); final HitTestResult result = HitTestResult();
WidgetsBinding.instance.hitTest(result, globalPosition + feedbackOffset); WidgetsBinding.instance.hitTestInView(result, globalPosition + feedbackOffset, viewId);
final List<_DragTargetState<Object>> targets = _getDragTargets(result.path).toList(); final List<_DragTargetState<Object>> targets = _getDragTargets(result.path).toList();
......
...@@ -5018,7 +5018,7 @@ class _ScribbleFocusableState extends State<_ScribbleFocusable> implements Scrib ...@@ -5018,7 +5018,7 @@ class _ScribbleFocusableState extends State<_ScribbleFocusable> implements Scrib
} }
final Rect intersection = calculatedBounds.intersect(rect); final Rect intersection = calculatedBounds.intersect(rect);
final HitTestResult result = HitTestResult(); final HitTestResult result = HitTestResult();
WidgetsBinding.instance.hitTest(result, intersection.center); WidgetsBinding.instance.hitTestInView(result, intersection.center, View.of(context).viewId);
return result.path.any((HitTestEntry entry) => entry.target == renderEditable); return result.path.any((HitTestEntry entry) => entry.target == renderEditable);
} }
......
...@@ -39,39 +39,47 @@ void main() { ...@@ -39,39 +39,47 @@ void main() {
final ui.PointerDataPacket packet = ui.PointerDataPacket( final ui.PointerDataPacket packet = ui.PointerDataPacket(
data: <ui.PointerData>[ data: <ui.PointerData>[
ui.PointerData( ui.PointerData(
viewId: tester.view.viewId,
change: ui.PointerChange.add, change: ui.PointerChange.add,
timeStamp: epoch, timeStamp: epoch,
), ),
ui.PointerData( ui.PointerData(
viewId: tester.view.viewId,
change: ui.PointerChange.down, change: ui.PointerChange.down,
timeStamp: epoch, timeStamp: epoch,
), ),
ui.PointerData( ui.PointerData(
viewId: tester.view.viewId,
change: ui.PointerChange.move, change: ui.PointerChange.move,
physicalX: 15.0, physicalX: 15.0,
timeStamp: epoch + const Duration(milliseconds: 10), timeStamp: epoch + const Duration(milliseconds: 10),
), ),
ui.PointerData( ui.PointerData(
viewId: tester.view.viewId,
change: ui.PointerChange.move, change: ui.PointerChange.move,
physicalX: 30.0, physicalX: 30.0,
timeStamp: epoch + const Duration(milliseconds: 20), timeStamp: epoch + const Duration(milliseconds: 20),
), ),
ui.PointerData( ui.PointerData(
viewId: tester.view.viewId,
change: ui.PointerChange.move, change: ui.PointerChange.move,
physicalX: 45.0, physicalX: 45.0,
timeStamp: epoch + const Duration(milliseconds: 30), timeStamp: epoch + const Duration(milliseconds: 30),
), ),
ui.PointerData( ui.PointerData(
viewId: tester.view.viewId,
change: ui.PointerChange.move, change: ui.PointerChange.move,
physicalX: 50.0, physicalX: 50.0,
timeStamp: epoch + const Duration(milliseconds: 40), timeStamp: epoch + const Duration(milliseconds: 40),
), ),
ui.PointerData( ui.PointerData(
viewId: tester.view.viewId,
change: ui.PointerChange.up, change: ui.PointerChange.up,
physicalX: 60.0, physicalX: 60.0,
timeStamp: epoch + const Duration(milliseconds: 40), timeStamp: epoch + const Duration(milliseconds: 40),
), ),
ui.PointerData( ui.PointerData(
viewId: tester.view.viewId,
change: ui.PointerChange.remove, change: ui.PointerChange.remove,
physicalX: 60.0, physicalX: 60.0,
timeStamp: epoch + const Duration(milliseconds: 40), timeStamp: epoch + const Duration(milliseconds: 40),
......
...@@ -175,7 +175,7 @@ void main() { ...@@ -175,7 +175,7 @@ void main() {
], ],
); );
final List<PointerEvent> events = PointerEventConverter.expand(packet.data, devicePixelRatio).toList(); final List<PointerEvent> events = PointerEventConverter.expand(packet.data, (int viewId) => devicePixelRatio).toList();
expect(events.length, 5); expect(events.length, 5);
expect(events[0], isA<PointerAddedEvent>()); expect(events[0], isA<PointerAddedEvent>());
...@@ -191,7 +191,7 @@ void main() { ...@@ -191,7 +191,7 @@ void main() {
ui.PointerData(change: ui.PointerChange.add, device: 24), ui.PointerData(change: ui.PointerChange.add, device: 24),
], ],
); );
List<PointerEvent> events = PointerEventConverter.expand(packet.data, devicePixelRatio).toList(); List<PointerEvent> events = PointerEventConverter.expand(packet.data, (int viewId) => devicePixelRatio).toList();
expect(events.length, 1); expect(events.length, 1);
expect(events[0], isA<PointerAddedEvent>()); expect(events[0], isA<PointerAddedEvent>());
...@@ -207,7 +207,7 @@ void main() { ...@@ -207,7 +207,7 @@ void main() {
ui.PointerData(signalKind: ui.PointerSignalKind.scroll, device: 24, scrollDeltaY: double.negativeInfinity, scrollDeltaX: 10), ui.PointerData(signalKind: ui.PointerSignalKind.scroll, device: 24, scrollDeltaY: double.negativeInfinity, scrollDeltaX: 10),
], ],
); );
events = PointerEventConverter.expand(packet.data, devicePixelRatio).toList(); events = PointerEventConverter.expand(packet.data, (int viewId) => devicePixelRatio).toList();
expect(events.length, 0); expect(events.length, 0);
// Send packet with a valid scroll event. // Send packet with a valid scroll event.
...@@ -217,12 +217,12 @@ void main() { ...@@ -217,12 +217,12 @@ void main() {
], ],
); );
// Make sure PointerEventConverter can expand when device pixel ratio is valid. // Make sure PointerEventConverter can expand when device pixel ratio is valid.
events = PointerEventConverter.expand(packet.data, devicePixelRatio).toList(); events = PointerEventConverter.expand(packet.data, (int viewId) => devicePixelRatio).toList();
expect(events.length, 1); expect(events.length, 1);
expect(events[0], isA<PointerScrollEvent>()); expect(events[0], isA<PointerScrollEvent>());
// Make sure PointerEventConverter returns none when device pixel ratio is invalid. // Make sure PointerEventConverter returns none when device pixel ratio is invalid.
events = PointerEventConverter.expand(packet.data, 0).toList(); events = PointerEventConverter.expand(packet.data, (int viewId) => 0).toList();
expect(events.length, 0); expect(events.length, 0);
}); });
...@@ -234,7 +234,7 @@ void main() { ...@@ -234,7 +234,7 @@ void main() {
], ],
); );
final List<PointerEvent> events = PointerEventConverter.expand(packet.data, devicePixelRatio).toList(); final List<PointerEvent> events = PointerEventConverter.expand(packet.data, (int viewId) => devicePixelRatio).toList();
expect(events.length, 2); expect(events.length, 2);
expect(events[0], isA<PointerAddedEvent>()); expect(events[0], isA<PointerAddedEvent>());
...@@ -253,7 +253,7 @@ void main() { ...@@ -253,7 +253,7 @@ void main() {
], ],
); );
final List<PointerEvent> events = PointerEventConverter.expand(packet.data, devicePixelRatio).toList(); final List<PointerEvent> events = PointerEventConverter.expand(packet.data, (int viewId) => devicePixelRatio).toList();
expect(events.length, 5); expect(events.length, 5);
expect(events[0], isA<PointerAddedEvent>()); expect(events[0], isA<PointerAddedEvent>());
...@@ -280,7 +280,7 @@ void main() { ...@@ -280,7 +280,7 @@ void main() {
], ],
); );
final List<PointerEvent> events = PointerEventConverter.expand(packet.data, devicePixelRatio).toList(); final List<PointerEvent> events = PointerEventConverter.expand(packet.data, (int viewId) => devicePixelRatio).toList();
expect(events.length, 5); expect(events.length, 5);
expect(events[0], isA<PointerAddedEvent>()); expect(events[0], isA<PointerAddedEvent>());
...@@ -312,7 +312,7 @@ void main() { ...@@ -312,7 +312,7 @@ void main() {
], ],
); );
final List<PointerEvent> events = PointerEventConverter.expand(packet.data, devicePixelRatio).toList(); final List<PointerEvent> events = PointerEventConverter.expand(packet.data, (int viewId) => devicePixelRatio).toList();
expect(events.length, 5); expect(events.length, 5);
expect(events[0], isA<PointerAddedEvent>()); expect(events[0], isA<PointerAddedEvent>());
...@@ -341,7 +341,7 @@ void main() { ...@@ -341,7 +341,7 @@ void main() {
], ],
); );
final List<PointerEvent> events = PointerEventConverter.expand(packet.data, devicePixelRatio).toList(); final List<PointerEvent> events = PointerEventConverter.expand(packet.data, (int viewId) => devicePixelRatio).toList();
expect(events.length, 5); expect(events.length, 5);
expect(events[0], isA<PointerAddedEvent>()); expect(events[0], isA<PointerAddedEvent>());
...@@ -371,7 +371,7 @@ void main() { ...@@ -371,7 +371,7 @@ void main() {
], ],
); );
final List<PointerEvent> events = PointerEventConverter.expand(packet.data, devicePixelRatio).toList(); final List<PointerEvent> events = PointerEventConverter.expand(packet.data, (int viewId) => devicePixelRatio).toList();
expect(events.length, 5); expect(events.length, 5);
expect(events[0], isA<PointerAddedEvent>()); expect(events[0], isA<PointerAddedEvent>());
...@@ -429,4 +429,33 @@ void main() { ...@@ -429,4 +429,33 @@ void main() {
FlutterError.onError = FlutterError.presentError; FlutterError.onError = FlutterError.presentError;
} }
}); });
test('PointerEventConverter processes view IDs', () {
const int startID = 987654;
const List<ui.PointerData> data = <ui.PointerData>[
ui.PointerData(viewId: startID + 0, change: ui.PointerChange.cancel), // ignore: avoid_redundant_argument_values
ui.PointerData(viewId: startID + 1, change: ui.PointerChange.add),
ui.PointerData(viewId: startID + 2, change: ui.PointerChange.remove),
ui.PointerData(viewId: startID + 3, change: ui.PointerChange.hover),
ui.PointerData(viewId: startID + 4, change: ui.PointerChange.down),
ui.PointerData(viewId: startID + 5, change: ui.PointerChange.move),
ui.PointerData(viewId: startID + 6, change: ui.PointerChange.up),
ui.PointerData(viewId: startID + 7, change: ui.PointerChange.panZoomStart),
ui.PointerData(viewId: startID + 8, change: ui.PointerChange.panZoomUpdate),
ui.PointerData(viewId: startID + 9, change: ui.PointerChange.panZoomEnd),
];
final List<int> viewIds = <int>[];
double devicePixelRatioGetter(int viewId) {
viewIds.add(viewId);
return viewId / 10.0;
}
final List<PointerEvent> events = PointerEventConverter.expand(data, devicePixelRatioGetter).toList();
final List<int> expectedViewIds = List<int>.generate(10, (int index) => startID + index);
expect(viewIds, expectedViewIds);
expect(events, hasLength(10));
expect(events.map((PointerEvent event) => event.viewId), expectedViewIds);
});
} }
...@@ -1192,7 +1192,8 @@ abstract class WidgetController { ...@@ -1192,7 +1192,8 @@ abstract class WidgetController {
/// Forwards the given location to the binding's hitTest logic. /// Forwards the given location to the binding's hitTest logic.
HitTestResult hitTestOnBinding(Offset location) { HitTestResult hitTestOnBinding(Offset location) {
final HitTestResult result = HitTestResult(); final HitTestResult result = HitTestResult();
binding.hitTest(result, location); // TODO(goderbauer): Support multiple views in flutter_test pointer event handling, https://github.com/flutter/flutter/issues/128281
binding.hitTest(result, location); // ignore: deprecated_member_use
return result; return result;
} }
...@@ -1313,7 +1314,8 @@ abstract class WidgetController { ...@@ -1313,7 +1314,8 @@ abstract class WidgetController {
final Offset location = box.localToGlobal(sizeToPoint(box.size)); final Offset location = box.localToGlobal(sizeToPoint(box.size));
if (warnIfMissed) { if (warnIfMissed) {
final HitTestResult result = HitTestResult(); final HitTestResult result = HitTestResult();
binding.hitTest(result, location); // TODO(goderbauer): Support multiple views in flutter_test pointer event handling, https://github.com/flutter/flutter/issues/128281
binding.hitTest(result, location); // ignore: deprecated_member_use
bool found = false; bool found = false;
for (final HitTestEntry entry in result.path) { for (final HitTestEntry entry in result.path) {
if (entry.target == box) { if (entry.target == box) {
......
...@@ -642,7 +642,8 @@ class _HitTestableFinder extends ChainedFinder { ...@@ -642,7 +642,8 @@ class _HitTestableFinder extends ChainedFinder {
final RenderBox box = candidate.renderObject! as RenderBox; final RenderBox box = candidate.renderObject! as RenderBox;
final Offset absoluteOffset = box.localToGlobal(alignment.alongSize(box.size)); final Offset absoluteOffset = box.localToGlobal(alignment.alongSize(box.size));
final HitTestResult hitResult = HitTestResult(); final HitTestResult hitResult = HitTestResult();
WidgetsBinding.instance.hitTest(hitResult, absoluteOffset); // TODO(goderbauer): Support multiple views in flutter_test pointer event handling, https://github.com/flutter/flutter/issues/128281
WidgetsBinding.instance.hitTest(hitResult, absoluteOffset); // ignore: deprecated_member_use
for (final HitTestEntry entry in hitResult.path) { for (final HitTestEntry entry in hitResult.path) {
if (entry.target == candidate.renderObject) { if (entry.target == candidate.renderObject) {
yield candidate; yield candidate;
......
...@@ -167,8 +167,8 @@ class TestPlatformDispatcher implements PlatformDispatcher { ...@@ -167,8 +167,8 @@ class TestPlatformDispatcher implements PlatformDispatcher {
: null; : null;
} }
final Map<Object, TestFlutterView> _testViews = <Object, TestFlutterView>{}; final Map<int, TestFlutterView> _testViews = <int, TestFlutterView>{};
final Map<Object, TestDisplay> _testDisplays = <Object, TestDisplay>{}; final Map<int, TestDisplay> _testDisplays = <int, TestDisplay>{};
@override @override
VoidCallback? get onMetricsChanged => _platformDispatcher.onMetricsChanged; VoidCallback? get onMetricsChanged => _platformDispatcher.onMetricsChanged;
...@@ -510,6 +510,9 @@ class TestPlatformDispatcher implements PlatformDispatcher { ...@@ -510,6 +510,9 @@ class TestPlatformDispatcher implements PlatformDispatcher {
@override @override
Iterable<TestFlutterView> get views => _testViews.values; Iterable<TestFlutterView> get views => _testViews.values;
@override
FlutterView? view({required int id}) => _testViews[id];
@override @override
Iterable<TestDisplay> get displays => _testDisplays.values; Iterable<TestDisplay> get displays => _testDisplays.values;
......
...@@ -153,6 +153,10 @@ void main() { ...@@ -153,6 +153,10 @@ void main() {
retrieveTestBinding(tester).platformDispatcher.localesTestValue = defaultLocales; retrieveTestBinding(tester).platformDispatcher.localesTestValue = defaultLocales;
}); });
testWidgets('TestPlatformDispatcher.view getter returns the implicit view', (WidgetTester tester) async {
expect(WidgetsBinding.instance.platformDispatcher.view(id: tester.view.viewId), same(tester.view));
});
// TODO(pdblasi-google): Removed this group of tests when the Display API is stable and supported on all platforms. // TODO(pdblasi-google): Removed this group of tests when the Display API is stable and supported on all platforms.
group('TestPlatformDispatcher with unsupported Display API', () { group('TestPlatformDispatcher with unsupported Display API', () {
testWidgets('can initialize with empty displays', (WidgetTester tester) async { testWidgets('can initialize with empty displays', (WidgetTester tester) async {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment