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
// We convert pointer data to logical pixels so that e.g. the touch slop can be
// defined in a device-independent manner.
try {
_pendingPointerEvents.addAll(PointerEventConverter.expand(packet.data, platformDispatcher.implicitView!.devicePixelRatio));
_pendingPointerEvents.addAll(PointerEventConverter.expand(packet.data, _devicePixelRatioForView));
if (!locked) {
_flushPointerEventQueue();
}
......@@ -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.
///
/// The pointer event will be dispatched before the next pointer event and
......@@ -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) {
assert(!_hitTests.containsKey(event.pointer), 'Pointer of ${event.toString(minLevel: DiagnosticLevel.debug)} unexpectedly has a HitTestResult associated with it.');
hitTestResult = HitTestResult();
hitTest(hitTestResult, event.position);
hitTestInView(hitTestResult, event.position, event.viewId);
if (event is PointerDownEvent || event is PointerPanZoomStartEvent) {
_hitTests[event.pointer] = hitTestResult;
}
......@@ -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
void hitTest(HitTestResult result, Offset position) {
void hitTestInView(HitTestResult result, Offset position, int viewId) {
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.
///
/// The `event` is routed to [pointerRouter]. If the `hitTestResult` is not
......
......@@ -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.
///
/// This takes [PointerDataPacket] objects, as received from the engine via
......@@ -45,10 +57,15 @@ abstract final class PointerEventConverter {
/// [dart:ui.FlutterView.devicePixelRatio]) is used to convert the incoming data
/// from physical coordinates to logical pixels. See the discussion at
/// [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
.where((ui.PointerData datum) => datum.signalKind != ui.PointerSignalKind.unknown)
.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 delta = Offset(datum.physicalDeltaX, datum.physicalDeltaY) / devicePixelRatio;
final double radiusMinor = _toLogicalPixels(datum.radiusMinor, devicePixelRatio);
......@@ -62,6 +79,7 @@ abstract final class PointerEventConverter {
switch (datum.change) {
case ui.PointerChange.add:
return PointerAddedEvent(
viewId: datum.viewId,
timeStamp: timeStamp,
kind: kind,
device: datum.device,
......@@ -79,6 +97,7 @@ abstract final class PointerEventConverter {
);
case ui.PointerChange.hover:
return PointerHoverEvent(
viewId: datum.viewId,
timeStamp: timeStamp,
kind: kind,
device: datum.device,
......@@ -102,6 +121,7 @@ abstract final class PointerEventConverter {
);
case ui.PointerChange.down:
return PointerDownEvent(
viewId: datum.viewId,
timeStamp: timeStamp,
pointer: datum.pointerIdentifier,
kind: kind,
......@@ -124,6 +144,7 @@ abstract final class PointerEventConverter {
);
case ui.PointerChange.move:
return PointerMoveEvent(
viewId: datum.viewId,
timeStamp: timeStamp,
pointer: datum.pointerIdentifier,
kind: kind,
......@@ -149,6 +170,7 @@ abstract final class PointerEventConverter {
);
case ui.PointerChange.up:
return PointerUpEvent(
viewId: datum.viewId,
timeStamp: timeStamp,
pointer: datum.pointerIdentifier,
kind: kind,
......@@ -172,6 +194,7 @@ abstract final class PointerEventConverter {
);
case ui.PointerChange.cancel:
return PointerCancelEvent(
viewId: datum.viewId,
timeStamp: timeStamp,
pointer: datum.pointerIdentifier,
kind: kind,
......@@ -194,6 +217,7 @@ abstract final class PointerEventConverter {
);
case ui.PointerChange.remove:
return PointerRemovedEvent(
viewId: datum.viewId,
timeStamp: timeStamp,
kind: kind,
device: datum.device,
......@@ -208,6 +232,7 @@ abstract final class PointerEventConverter {
);
case ui.PointerChange.panZoomStart:
return PointerPanZoomStartEvent(
viewId: datum.viewId,
timeStamp: timeStamp,
pointer: datum.pointerIdentifier,
device: datum.device,
......@@ -221,6 +246,7 @@ abstract final class PointerEventConverter {
final Offset panDelta =
Offset(datum.panDeltaX, datum.panDeltaY) / devicePixelRatio;
return PointerPanZoomUpdateEvent(
viewId: datum.viewId,
timeStamp: timeStamp,
pointer: datum.pointerIdentifier,
device: datum.device,
......@@ -234,6 +260,7 @@ abstract final class PointerEventConverter {
);
case ui.PointerChange.panZoomEnd:
return PointerPanZoomEndEvent(
viewId: datum.viewId,
timeStamp: timeStamp,
pointer: datum.pointerIdentifier,
device: datum.device,
......@@ -249,6 +276,7 @@ abstract final class PointerEventConverter {
final Offset scrollDelta =
Offset(datum.scrollDeltaX, datum.scrollDeltaY) / devicePixelRatio;
return PointerScrollEvent(
viewId: datum.viewId,
timeStamp: timeStamp,
kind: kind,
device: datum.device,
......@@ -258,6 +286,7 @@ abstract final class PointerEventConverter {
);
case ui.PointerSignalKind.scrollInertiaCancel:
return PointerScrollInertiaCancelEvent(
viewId: datum.viewId,
timeStamp: timeStamp,
kind: kind,
device: datum.device,
......@@ -266,6 +295,7 @@ abstract final class PointerEventConverter {
);
case ui.PointerSignalKind.scale:
return PointerScaleEvent(
viewId: datum.viewId,
timeStamp: timeStamp,
kind: kind,
device: datum.device,
......
......@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui' show Offset, PointerDeviceKind;
import 'package:flutter/foundation.dart';
......@@ -245,6 +244,7 @@ abstract class PointerEvent with Diagnosticable {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const PointerEvent({
this.viewId = 0,
this.embedderId = 0,
this.timeStamp = Duration.zero,
this.pointer = 0,
......@@ -273,6 +273,9 @@ abstract class PointerEvent with Diagnosticable {
this.original,
});
/// The ID of the [FlutterView] which this event originated from.
final int viewId;
/// Unique identifier that ties the [PointerEvent] to the embedder event that created it.
///
/// No two pointer events can have the same [embedderId] on platforms that set it.
......@@ -536,6 +539,7 @@ abstract class PointerEvent with Diagnosticable {
/// Calling this method on a transformed event will return a new transformed
/// event based on the current [transform] and the provided properties.
PointerEvent copyWith({
int? viewId,
Duration? timeStamp,
int? pointer,
PointerDeviceKind? kind,
......@@ -619,7 +623,6 @@ abstract class PointerEvent with Diagnosticable {
// A mixin that adds implementation for [debugFillProperties] and [toStringFull]
// to [PointerEvent].
mixin _PointerEventDescription on PointerEvent {
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
......@@ -650,6 +653,7 @@ mixin _PointerEventDescription on PointerEvent {
properties.add(FlagProperty('obscured', value: obscured, ifTrue: 'obscured', level: DiagnosticLevel.debug));
properties.add(FlagProperty('synthesized', value: synthesized, ifTrue: 'synthesized', level: DiagnosticLevel.debug));
properties.add(IntProperty('embedderId', embedderId, defaultValue: 0, level: DiagnosticLevel.debug));
properties.add(IntProperty('viewId', viewId, defaultValue: 0, level: DiagnosticLevel.debug));
}
/// Returns a complete textual description of this event.
......@@ -666,7 +670,6 @@ abstract class _AbstractPointerEvent implements PointerEvent { }
// matrix. It defers all field getters to the original event, except for
// [localPosition] and [localDelta], which are calculated when first used.
abstract class _TransformedPointerEvent extends _AbstractPointerEvent with Diagnosticable, _PointerEventDescription {
@override
PointerEvent get original;
......@@ -758,11 +761,15 @@ abstract class _TransformedPointerEvent extends _AbstractPointerEvent with Diagn
untransformedEndPosition: position,
transformedEndPosition: localPosition,
);
@override
int get viewId => original.viewId;
}
mixin _CopyPointerAddedEvent on PointerEvent {
@override
PointerAddedEvent copyWith({
int? viewId,
Duration? timeStamp,
int? pointer,
PointerDeviceKind? kind,
......@@ -787,6 +794,7 @@ mixin _CopyPointerAddedEvent on PointerEvent {
int? embedderId,
}) {
return PointerAddedEvent(
viewId: viewId ?? this.viewId,
timeStamp: timeStamp ?? this.timeStamp,
kind: kind ?? this.kind,
device: device ?? this.device,
......@@ -814,6 +822,7 @@ class PointerAddedEvent extends PointerEvent with _PointerEventDescription, _Cop
///
/// All of the arguments must be non-null.
const PointerAddedEvent({
super.viewId,
super.timeStamp,
super.pointer,
super.kind,
......@@ -858,6 +867,7 @@ class _TransformedPointerAddedEvent extends _TransformedPointerEvent with _CopyP
mixin _CopyPointerRemovedEvent on PointerEvent {
@override
PointerRemovedEvent copyWith({
int? viewId,
Duration? timeStamp,
int? pointer,
PointerDeviceKind? kind,
......@@ -882,6 +892,7 @@ mixin _CopyPointerRemovedEvent on PointerEvent {
int? embedderId,
}) {
return PointerRemovedEvent(
viewId: viewId ?? this.viewId,
timeStamp: timeStamp ?? this.timeStamp,
kind: kind ?? this.kind,
device: device ?? this.device,
......@@ -906,6 +917,7 @@ class PointerRemovedEvent extends PointerEvent with _PointerEventDescription, _C
///
/// All of the arguments must be non-null.
const PointerRemovedEvent({
super.viewId,
super.timeStamp,
super.pointer,
super.kind,
......@@ -948,6 +960,7 @@ class _TransformedPointerRemovedEvent extends _TransformedPointerEvent with _Cop
mixin _CopyPointerHoverEvent on PointerEvent {
@override
PointerHoverEvent copyWith({
int? viewId,
Duration? timeStamp,
int? pointer,
PointerDeviceKind? kind,
......@@ -972,6 +985,7 @@ mixin _CopyPointerHoverEvent on PointerEvent {
int? embedderId,
}) {
return PointerHoverEvent(
viewId: viewId ?? this.viewId,
timeStamp: timeStamp ?? this.timeStamp,
kind: kind ?? this.kind,
device: device ?? this.device,
......@@ -1013,6 +1027,7 @@ class PointerHoverEvent extends PointerEvent with _PointerEventDescription, _Cop
///
/// All of the arguments must be non-null.
const PointerHoverEvent({
super.viewId,
super.timeStamp,
super.kind,
super.pointer,
......@@ -1064,6 +1079,7 @@ class _TransformedPointerHoverEvent extends _TransformedPointerEvent with _CopyP
mixin _CopyPointerEnterEvent on PointerEvent {
@override
PointerEnterEvent copyWith({
int? viewId,
Duration? timeStamp,
int? pointer,
PointerDeviceKind? kind,
......@@ -1088,6 +1104,7 @@ mixin _CopyPointerEnterEvent on PointerEvent {
int? embedderId,
}) {
return PointerEnterEvent(
viewId: viewId ?? this.viewId,
timeStamp: timeStamp ?? this.timeStamp,
kind: kind ?? this.kind,
device: device ?? this.device,
......@@ -1129,6 +1146,7 @@ class PointerEnterEvent extends PointerEvent with _PointerEventDescription, _Cop
///
/// All of the arguments must be non-null.
const PointerEnterEvent({
super.viewId,
super.timeStamp,
super.pointer,
super.kind,
......@@ -1162,6 +1180,7 @@ class PointerEnterEvent extends PointerEvent with _PointerEventDescription, _Cop
///
/// This is used by the [MouseTracker] to synthesize enter events.
factory PointerEnterEvent.fromMouseEvent(PointerEvent event) => PointerEnterEvent(
viewId: event.viewId,
timeStamp: event.timeStamp,
pointer: event.pointer,
kind: event.kind,
......@@ -1210,6 +1229,7 @@ class _TransformedPointerEnterEvent extends _TransformedPointerEvent with _CopyP
mixin _CopyPointerExitEvent on PointerEvent {
@override
PointerExitEvent copyWith({
int? viewId,
Duration? timeStamp,
int? pointer,
PointerDeviceKind? kind,
......@@ -1234,6 +1254,7 @@ mixin _CopyPointerExitEvent on PointerEvent {
int? embedderId,
}) {
return PointerExitEvent(
viewId: viewId ?? this.viewId,
timeStamp: timeStamp ?? this.timeStamp,
kind: kind ?? this.kind,
device: device ?? this.device,
......@@ -1275,6 +1296,7 @@ class PointerExitEvent extends PointerEvent with _PointerEventDescription, _Copy
///
/// All of the arguments must be non-null.
const PointerExitEvent({
super.viewId,
super.timeStamp,
super.kind,
super.pointer,
......@@ -1306,6 +1328,7 @@ class PointerExitEvent extends PointerEvent with _PointerEventDescription, _Copy
///
/// This is used by the [MouseTracker] to synthesize exit events.
factory PointerExitEvent.fromMouseEvent(PointerEvent event) => PointerExitEvent(
viewId: event.viewId,
timeStamp: event.timeStamp,
pointer: event.pointer,
kind: event.kind,
......@@ -1355,6 +1378,7 @@ class _TransformedPointerExitEvent extends _TransformedPointerEvent with _CopyPo
mixin _CopyPointerDownEvent on PointerEvent {
@override
PointerDownEvent copyWith({
int? viewId,
Duration? timeStamp,
int? pointer,
PointerDeviceKind? kind,
......@@ -1379,6 +1403,7 @@ mixin _CopyPointerDownEvent on PointerEvent {
int? embedderId,
}) {
return PointerDownEvent(
viewId: viewId ?? this.viewId,
timeStamp: timeStamp ?? this.timeStamp,
pointer: pointer ?? this.pointer,
kind: kind ?? this.kind,
......@@ -1413,6 +1438,7 @@ class PointerDownEvent extends PointerEvent with _PointerEventDescription, _Copy
///
/// All of the arguments must be non-null.
const PointerDownEvent({
super.viewId,
super.timeStamp,
super.pointer,
super.kind,
......@@ -1463,6 +1489,7 @@ class _TransformedPointerDownEvent extends _TransformedPointerEvent with _CopyPo
mixin _CopyPointerMoveEvent on PointerEvent {
@override
PointerMoveEvent copyWith({
int? viewId,
Duration? timeStamp,
int? pointer,
PointerDeviceKind? kind,
......@@ -1487,6 +1514,7 @@ mixin _CopyPointerMoveEvent on PointerEvent {
int? embedderId,
}) {
return PointerMoveEvent(
viewId: viewId ?? this.viewId,
timeStamp: timeStamp ?? this.timeStamp,
pointer: pointer ?? this.pointer,
kind: kind ?? this.kind,
......@@ -1526,6 +1554,7 @@ class PointerMoveEvent extends PointerEvent with _PointerEventDescription, _Copy
///
/// All of the arguments must be non-null.
const PointerMoveEvent({
super.viewId,
super.timeStamp,
super.pointer,
super.kind,
......@@ -1580,6 +1609,7 @@ class _TransformedPointerMoveEvent extends _TransformedPointerEvent with _CopyPo
mixin _CopyPointerUpEvent on PointerEvent {
@override
PointerUpEvent copyWith({
int? viewId,
Duration? timeStamp,
int? pointer,
PointerDeviceKind? kind,
......@@ -1605,6 +1635,7 @@ mixin _CopyPointerUpEvent on PointerEvent {
int? embedderId,
}) {
return PointerUpEvent(
viewId: viewId ?? this.viewId,
timeStamp: timeStamp ?? this.timeStamp,
pointer: pointer ?? this.pointer,
kind: kind ?? this.kind,
......@@ -1640,6 +1671,7 @@ class PointerUpEvent extends PointerEvent with _PointerEventDescription, _CopyPo
///
/// All of the arguments must be non-null.
const PointerUpEvent({
super.viewId,
super.timeStamp,
super.pointer,
super.kind,
......@@ -1705,6 +1737,7 @@ abstract class PointerSignalEvent extends PointerEvent {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const PointerSignalEvent({
super.viewId,
super.timeStamp,
super.pointer,
super.kind = PointerDeviceKind.mouse,
......@@ -1720,6 +1753,7 @@ mixin _CopyPointerScrollEvent on PointerEvent {
@override
PointerScrollEvent copyWith({
int? viewId,
Duration? timeStamp,
int? pointer,
PointerDeviceKind? kind,
......@@ -1744,6 +1778,7 @@ mixin _CopyPointerScrollEvent on PointerEvent {
int? embedderId,
}) {
return PointerScrollEvent(
viewId: viewId ?? this.viewId,
timeStamp: timeStamp ?? this.timeStamp,
kind: kind ?? this.kind,
device: device ?? this.device,
......@@ -1770,6 +1805,7 @@ class PointerScrollEvent extends PointerSignalEvent with _PointerEventDescriptio
///
/// All of the arguments must be non-null.
const PointerScrollEvent({
super.viewId,
super.timeStamp,
super.kind,
super.device,
......@@ -1821,6 +1857,7 @@ class _TransformedPointerScrollEvent extends _TransformedPointerEvent with _Copy
mixin _CopyPointerScrollInertiaCancelEvent on PointerEvent {
@override
PointerScrollInertiaCancelEvent copyWith({
int? viewId,
Duration? timeStamp,
int? pointer,
PointerDeviceKind? kind,
......@@ -1845,6 +1882,7 @@ mixin _CopyPointerScrollInertiaCancelEvent on PointerEvent {
int? embedderId,
}) {
return PointerScrollInertiaCancelEvent(
viewId: viewId ?? this.viewId,
timeStamp: timeStamp ?? this.timeStamp,
kind: kind ?? this.kind,
device: device ?? this.device,
......@@ -1870,6 +1908,7 @@ class PointerScrollInertiaCancelEvent extends PointerSignalEvent with _PointerEv
///
/// All of the arguments must be non-null.
const PointerScrollInertiaCancelEvent({
super.viewId,
super.timeStamp,
super.kind,
super.device,
......@@ -1905,6 +1944,7 @@ mixin _CopyPointerScaleEvent on PointerEvent {
@override
PointerScaleEvent copyWith({
int? viewId,
Duration? timeStamp,
int? pointer,
PointerDeviceKind? kind,
......@@ -1930,6 +1970,7 @@ mixin _CopyPointerScaleEvent on PointerEvent {
double? scale,
}) {
return PointerScaleEvent(
viewId: viewId ?? this.viewId,
timeStamp: timeStamp ?? this.timeStamp,
kind: kind ?? this.kind,
device: device ?? this.device,
......@@ -1956,6 +1997,7 @@ class PointerScaleEvent extends PointerSignalEvent with _PointerEventDescription
///
/// All of the arguments must be non-null.
const PointerScaleEvent({
super.viewId,
super.timeStamp,
super.kind,
super.device,
......@@ -1995,6 +2037,7 @@ class _TransformedPointerScaleEvent extends _TransformedPointerEvent with _CopyP
mixin _CopyPointerPanZoomStartEvent on PointerEvent {
@override
PointerPanZoomStartEvent copyWith({
int? viewId,
Duration? timeStamp,
int? pointer,
PointerDeviceKind? kind,
......@@ -2020,6 +2063,7 @@ mixin _CopyPointerPanZoomStartEvent on PointerEvent {
}) {
assert(kind == null || identical(kind, PointerDeviceKind.trackpad));
return PointerPanZoomStartEvent(
viewId: viewId ?? this.viewId,
timeStamp: timeStamp ?? this.timeStamp,
device: device ?? this.device,
position: position ?? this.position,
......@@ -2039,6 +2083,7 @@ class PointerPanZoomStartEvent extends PointerEvent with _PointerEventDescriptio
///
/// All of the arguments must be non-null.
const PointerPanZoomStartEvent({
super.viewId,
super.timeStamp,
super.device,
super.pointer,
......@@ -2085,6 +2130,7 @@ mixin _CopyPointerPanZoomUpdateEvent on PointerEvent {
@override
PointerPanZoomUpdateEvent copyWith({
int? viewId,
Duration? timeStamp,
int? pointer,
PointerDeviceKind? kind,
......@@ -2116,6 +2162,7 @@ mixin _CopyPointerPanZoomUpdateEvent on PointerEvent {
}) {
assert(kind == null || identical(kind, PointerDeviceKind.trackpad));
return PointerPanZoomUpdateEvent(
viewId: viewId ?? this.viewId,
timeStamp: timeStamp ?? this.timeStamp,
device: device ?? this.device,
position: position ?? this.position,
......@@ -2139,6 +2186,7 @@ class PointerPanZoomUpdateEvent extends PointerEvent with _PointerEventDescripti
///
/// All of the arguments must be non-null.
const PointerPanZoomUpdateEvent({
super.viewId,
super.timeStamp,
super.device,
super.pointer,
......@@ -2212,6 +2260,7 @@ class _TransformedPointerPanZoomUpdateEvent extends _TransformedPointerEvent wit
mixin _CopyPointerPanZoomEndEvent on PointerEvent {
@override
PointerPanZoomEndEvent copyWith({
int? viewId,
Duration? timeStamp,
int? pointer,
PointerDeviceKind? kind,
......@@ -2237,6 +2286,7 @@ mixin _CopyPointerPanZoomEndEvent on PointerEvent {
}) {
assert(kind == null || identical(kind, PointerDeviceKind.trackpad));
return PointerPanZoomEndEvent(
viewId: viewId ?? this.viewId,
timeStamp: timeStamp ?? this.timeStamp,
device: device ?? this.device,
position: position ?? this.position,
......@@ -2256,6 +2306,7 @@ class PointerPanZoomEndEvent extends PointerEvent with _PointerEventDescription,
///
/// All of the arguments must be non-null.
const PointerPanZoomEndEvent({
super.viewId,
super.timeStamp,
super.device,
super.pointer,
......@@ -2289,6 +2340,7 @@ class _TransformedPointerPanZoomEndEvent extends _TransformedPointerEvent with _
mixin _CopyPointerCancelEvent on PointerEvent {
@override
PointerCancelEvent copyWith({
int? viewId,
Duration? timeStamp,
int? pointer,
PointerDeviceKind? kind,
......@@ -2313,6 +2365,7 @@ mixin _CopyPointerCancelEvent on PointerEvent {
int? embedderId,
}) {
return PointerCancelEvent(
viewId: viewId ?? this.viewId,
timeStamp: timeStamp ?? this.timeStamp,
pointer: pointer ?? this.pointer,
kind: kind ?? this.kind,
......@@ -2347,6 +2400,7 @@ class PointerCancelEvent extends PointerEvent with _PointerEventDescription, _Co
///
/// All of the arguments must be non-null.
const PointerCancelEvent({
super.viewId,
super.timeStamp,
super.pointer,
super.kind,
......
......@@ -16,11 +16,16 @@ export 'events.dart' show PointerEvent;
/// An object that can hit-test pointers.
abstract interface class HitTestable {
/// Check whether the given position hits this object.
///
/// If this given position hits this object, consider adding a [HitTestEntry]
/// to the given hit test result.
/// Deprecated. Use [hitTestInView] instead.
@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);
/// 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.
......
......@@ -54,6 +54,7 @@ class PointerEventResampler {
int buttons,
) {
return PointerHoverEvent(
viewId: event.viewId,
timeStamp: timeStamp,
kind: event.kind,
device: event.device,
......@@ -86,6 +87,7 @@ class PointerEventResampler {
int buttons,
) {
return PointerMoveEvent(
viewId: event.viewId,
timeStamp: timeStamp,
pointer: pointerIdentifier,
kind: event.kind,
......
......@@ -519,9 +519,10 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture
}
@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);
super.hitTest(result, position);
super.hitTestInView(result, position, viewId);
}
Future<void> _forceRepaint() {
......
......@@ -103,6 +103,8 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
markNeedsLayout();
}
/// The [FlutterView] into which this [RenderView] will render.
ui.FlutterView get flutterView => _view;
final ui.FlutterView _view;
/// Whether Flutter should automatically compute the desired system UI.
......
......@@ -13,6 +13,7 @@ import 'debug.dart';
import 'framework.dart';
import 'media_query.dart';
import 'overlay.dart';
import 'view.dart';
/// 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>> {
feedbackOffset: widget.feedbackOffset,
ignoringFeedbackSemantics: widget.ignoringFeedbackSemantics,
ignoringFeedbackPointer: widget.ignoringFeedbackPointer,
viewId: View.of(context).viewId,
onDragUpdate: (DragUpdateDetails details) {
if (mounted && widget.onDragUpdate != null) {
widget.onDragUpdate!(details);
......@@ -756,6 +758,7 @@ class _DragAvatar<T extends Object> extends Drag {
this.onDragEnd,
required this.ignoringFeedbackSemantics,
required this.ignoringFeedbackPointer,
required this.viewId,
}) : _position = initialPosition {
_entry = OverlayEntry(builder: _build);
overlayState.insert(_entry!);
......@@ -772,6 +775,7 @@ class _DragAvatar<T extends Object> extends Drag {
final OverlayState overlayState;
final bool ignoringFeedbackSemantics;
final bool ignoringFeedbackPointer;
final int viewId;
_DragTargetState<Object>? _activeTarget;
final List<_DragTargetState<Object>> _enteredTargets = <_DragTargetState<Object>>[];
......@@ -804,7 +808,7 @@ class _DragAvatar<T extends Object> extends Drag {
_lastOffset = globalPosition - dragStartPoint;
_entry!.markNeedsBuild();
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();
......
......@@ -5018,7 +5018,7 @@ class _ScribbleFocusableState extends State<_ScribbleFocusable> implements Scrib
}
final Rect intersection = calculatedBounds.intersect(rect);
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);
}
......
......@@ -39,39 +39,47 @@ void main() {
final ui.PointerDataPacket packet = ui.PointerDataPacket(
data: <ui.PointerData>[
ui.PointerData(
viewId: tester.view.viewId,
change: ui.PointerChange.add,
timeStamp: epoch,
),
ui.PointerData(
viewId: tester.view.viewId,
change: ui.PointerChange.down,
timeStamp: epoch,
),
ui.PointerData(
viewId: tester.view.viewId,
change: ui.PointerChange.move,
physicalX: 15.0,
timeStamp: epoch + const Duration(milliseconds: 10),
),
ui.PointerData(
viewId: tester.view.viewId,
change: ui.PointerChange.move,
physicalX: 30.0,
timeStamp: epoch + const Duration(milliseconds: 20),
),
ui.PointerData(
viewId: tester.view.viewId,
change: ui.PointerChange.move,
physicalX: 45.0,
timeStamp: epoch + const Duration(milliseconds: 30),
),
ui.PointerData(
viewId: tester.view.viewId,
change: ui.PointerChange.move,
physicalX: 50.0,
timeStamp: epoch + const Duration(milliseconds: 40),
),
ui.PointerData(
viewId: tester.view.viewId,
change: ui.PointerChange.up,
physicalX: 60.0,
timeStamp: epoch + const Duration(milliseconds: 40),
),
ui.PointerData(
viewId: tester.view.viewId,
change: ui.PointerChange.remove,
physicalX: 60.0,
timeStamp: epoch + const Duration(milliseconds: 40),
......
......@@ -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[0], isA<PointerAddedEvent>());
......@@ -191,7 +191,7 @@ void main() {
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[0], isA<PointerAddedEvent>());
......@@ -207,7 +207,7 @@ void main() {
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);
// Send packet with a valid scroll event.
......@@ -217,12 +217,12 @@ void main() {
],
);
// 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[0], isA<PointerScrollEvent>());
// 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);
});
......@@ -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[0], isA<PointerAddedEvent>());
......@@ -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[0], isA<PointerAddedEvent>());
......@@ -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[0], isA<PointerAddedEvent>());
......@@ -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[0], isA<PointerAddedEvent>());
......@@ -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[0], isA<PointerAddedEvent>());
......@@ -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[0], isA<PointerAddedEvent>());
......@@ -429,4 +429,33 @@ void main() {
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 {
/// Forwards the given location to the binding's hitTest logic.
HitTestResult hitTestOnBinding(Offset location) {
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;
}
......@@ -1313,7 +1314,8 @@ abstract class WidgetController {
final Offset location = box.localToGlobal(sizeToPoint(box.size));
if (warnIfMissed) {
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;
for (final HitTestEntry entry in result.path) {
if (entry.target == box) {
......
......@@ -642,7 +642,8 @@ class _HitTestableFinder extends ChainedFinder {
final RenderBox box = candidate.renderObject! as RenderBox;
final Offset absoluteOffset = box.localToGlobal(alignment.alongSize(box.size));
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) {
if (entry.target == candidate.renderObject) {
yield candidate;
......
......@@ -167,8 +167,8 @@ class TestPlatformDispatcher implements PlatformDispatcher {
: null;
}
final Map<Object, TestFlutterView> _testViews = <Object, TestFlutterView>{};
final Map<Object, TestDisplay> _testDisplays = <Object, TestDisplay>{};
final Map<int, TestFlutterView> _testViews = <int, TestFlutterView>{};
final Map<int, TestDisplay> _testDisplays = <int, TestDisplay>{};
@override
VoidCallback? get onMetricsChanged => _platformDispatcher.onMetricsChanged;
......@@ -510,6 +510,9 @@ class TestPlatformDispatcher implements PlatformDispatcher {
@override
Iterable<TestFlutterView> get views => _testViews.values;
@override
FlutterView? view({required int id}) => _testViews[id];
@override
Iterable<TestDisplay> get displays => _testDisplays.values;
......
......@@ -153,6 +153,10 @@ void main() {
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.
group('TestPlatformDispatcher with unsupported Display API', () {
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