Unverified Commit 371667ec authored by David Reveman's avatar David Reveman Committed by GitHub

Fix resampling of down, up, and remove events. (#67080)

This adds the neccessary synthetic move/hover events to
resampler, which is needed for position to not change as
part of a down, up, or remove event.
Co-authored-by: 's avatarDavid Reveman <reveman@google.com>
parent e45157fc
......@@ -106,6 +106,18 @@ class PointerEventResampler {
);
}
PointerEvent _toMoveOrHoverEvent(
PointerEvent event,
Offset position,
Offset delta,
int pointerIdentifier,
Duration timeStamp,
bool isDown,
) {
return isDown ? _toMoveEvent(event, position, delta, pointerIdentifier, timeStamp)
: _toHoverEvent(event, position, delta, timeStamp);
}
Offset _positionAt(Duration sampleTime) {
// Use `next` position by default.
double x = _next?.position.dx ?? 0.0;
......@@ -224,13 +236,21 @@ class PointerEventResampler {
// Skip `move` and `hover` events as they are automatically
// generated when the position has changed.
if (event is! PointerMoveEvent && event is! PointerHoverEvent) {
// Add synthetics `move` or `hover` event if position has changed.
// Note: Devices without `hover` events are expected to always have
// `add` and `down` events with the same position and this logic will
// therefor never produce `hover` events.
if (position != _position) {
final Offset delta = position - _position;
callback(_toMoveOrHoverEvent(event, position, delta, _pointerIdentifier, sampleTime, wasDown));
_position = position;
}
callback(event.copyWith(
position: position,
delta: position - _position,
delta: Offset.zero,
pointer: pointerIdentifier,
timeStamp: sampleTime,
));
_position = position;
}
_queuedEvents.removeFirst();
......@@ -248,10 +268,7 @@ class PointerEventResampler {
final PointerEvent? next = _next;
if (position != _position && next != null) {
final Offset delta = position - _position;
final PointerEvent event = _isDown
? _toMoveEvent(next, position, delta, _pointerIdentifier, sampleTime)
: _toHoverEvent(next, position, delta, sampleTime);
callback(event);
callback(_toMoveOrHoverEvent(next, position, delta, _pointerIdentifier, sampleTime, _isDown));
_position = position;
}
}
......
......@@ -97,9 +97,13 @@ void main() {
// Now the system time is epoch + 11ms
await tester.pump(const Duration(milliseconds: 2));
expect(events.length, 3);
expect(events.length, 4);
expect(events[2].timeStamp, currentTestFrameTime() + kSamplingOffset);
expect(events[2].runtimeType, equals(PointerUpEvent));
expect(events[2].runtimeType, equals(PointerMoveEvent));
expect(events[2].position, Offset(40.0 / ui.window.devicePixelRatio, 0.0));
expect(events[2].delta, Offset(15.0 / ui.window.devicePixelRatio, 0.0));
expect(events[3].timeStamp, currentTestFrameTime() + kSamplingOffset);
expect(events[3].runtimeType, equals(PointerUpEvent));
expect(events[3].position, Offset(40.0 / ui.window.devicePixelRatio, 0.0));
});
}
......@@ -104,46 +104,64 @@ void main() {
resampler.sample(const Duration(microseconds: 2500), result.add);
// Down pointer event should have been returned.
expect(result.length, 2);
// Hover and down pointer events should have been returned.
expect(result.length, 3);
expect(result[1].timeStamp, const Duration(microseconds: 2500));
expect(result[1] is PointerDownEvent, true);
expect(result[1] is PointerHoverEvent, true);
expect(result[1].position.dx, 15.0);
expect(result[1].position.dy, 35.0);
expect(result[1].delta.dx, 10.0);
expect(result[1].delta.dy, -10.0);
expect(result[2].timeStamp, const Duration(microseconds: 2500));
expect(result[2] is PointerDownEvent, true);
expect(result[2].position.dx, 15.0);
expect(result[2].position.dy, 35.0);
resampler.sample(const Duration(microseconds: 3500), result.add);
// Move pointer event should have been returned.
expect(result.length, 3);
expect(result[2].timeStamp, const Duration(microseconds: 3500));
expect(result[2] is PointerMoveEvent, true);
expect(result[2].position.dx, 25.0);
expect(result[2].position.dy, 25.0);
expect(result[2].delta.dx, 10.0);
expect(result[2].delta.dy, -10.0);
expect(result.length, 4);
expect(result[3].timeStamp, const Duration(microseconds: 3500));
expect(result[3] is PointerMoveEvent, true);
expect(result[3].position.dx, 25.0);
expect(result[3].position.dy, 25.0);
expect(result[3].delta.dx, 10.0);
expect(result[3].delta.dy, -10.0);
resampler.sample(const Duration(microseconds: 4500), result.add);
// Up pointer event should have been returned.
expect(result.length, 4);
expect(result[3].timeStamp, const Duration(microseconds: 4500));
expect(result[3] is PointerUpEvent, true);
expect(result[3].position.dx, 35.0);
expect(result[3].position.dy, 15.0);
// Move and up pointer events should have been returned.
expect(result.length, 6);
expect(result[4].timeStamp, const Duration(microseconds: 4500));
expect(result[4] is PointerMoveEvent, true);
expect(result[4].position.dx, 35.0);
expect(result[4].position.dy, 15.0);
expect(result[4].delta.dx, 10.0);
expect(result[4].delta.dy, -10.0);
expect(result[5].timeStamp, const Duration(microseconds: 4500));
expect(result[5] is PointerUpEvent, true);
expect(result[5].position.dx, 35.0);
expect(result[5].position.dy, 15.0);
resampler.sample(const Duration(microseconds: 5500), result.add);
// Remove pointer event should have been returned.
expect(result.length, 5);
expect(result[4].timeStamp, const Duration(microseconds: 5500));
expect(result[4] is PointerRemovedEvent, true);
expect(result[4].position.dx, 45.0);
expect(result[4].position.dy, 5.0);
// Hover and remove pointer events should have been returned.
expect(result.length, 8);
expect(result[6].timeStamp, const Duration(microseconds: 5500));
expect(result[6] is PointerHoverEvent, true);
expect(result[6].position.dx, 45.0);
expect(result[6].position.dy, 5.0);
expect(result[6].delta.dx, 10.0);
expect(result[6].delta.dy, -10.0);
expect(result[7].timeStamp, const Duration(microseconds: 5500));
expect(result[7] is PointerRemovedEvent, true);
expect(result[7].position.dx, 45.0);
expect(result[7].position.dy, 5.0);
resampler.sample(const Duration(microseconds: 6500), result.add);
// No pointer event should have been returned.
expect(result.length, 5);
expect(result.length, 8);
});
test('stream', () {
......@@ -201,19 +219,25 @@ void main() {
resampler.sample(const Duration(microseconds: 2500), result.add);
// Down pointer event should have been returned.
expect(result.length, 2);
// Hover and down pointer events should have been returned.
expect(result.length, 3);
expect(result[1].timeStamp, const Duration(microseconds: 2500));
expect(result[1] is PointerDownEvent, true);
expect(result[1] is PointerHoverEvent, true);
expect(result[1].position.dx, 15.0);
expect(result[1].position.dy, 35.0);
expect(result[1].delta.dx, 10.0);
expect(result[1].delta.dy, -10.0);
expect(result[2].timeStamp, const Duration(microseconds: 2500));
expect(result[2] is PointerDownEvent, true);
expect(result[2].position.dx, 15.0);
expect(result[2].position.dy, 35.0);
resampler.addEvent(event3);
resampler.sample(const Duration(microseconds: 2500), result.add);
// No more pointer events should have been returned.
expect(result.length, 2);
expect(result.length, 3);
//
// Advance sample time to 3.5 ms.
......@@ -222,20 +246,20 @@ void main() {
resampler.sample(const Duration(microseconds: 3500), result.add);
// Move pointer event should have been returned.
expect(result.length, 3);
expect(result[2].timeStamp, const Duration(microseconds: 3500));
expect(result[2] is PointerMoveEvent, true);
expect(result[2].position.dx, 25.0);
expect(result[2].position.dy, 25.0);
expect(result[2].delta.dx, 10.0);
expect(result[2].delta.dy, -10.0);
expect(result.length, 4);
expect(result[3].timeStamp, const Duration(microseconds: 3500));
expect(result[3] is PointerMoveEvent, true);
expect(result[3].position.dx, 25.0);
expect(result[3].position.dy, 25.0);
expect(result[3].delta.dx, 10.0);
expect(result[3].delta.dy, -10.0);
resampler.addEvent(event4);
resampler.sample(const Duration(microseconds: 3500), result.add);
// No more pointer events should have been returned.
expect(result.length, 3);
expect(result.length, 4);
//
// Advance sample time to 4.5 ms.
......@@ -243,19 +267,25 @@ void main() {
resampler.sample(const Duration(microseconds: 4500), result.add);
// Up pointer event should have been returned.
expect(result.length, 4);
expect(result[3].timeStamp, const Duration(microseconds: 4500));
expect(result[3] is PointerUpEvent, true);
expect(result[3].position.dx, 35.0);
expect(result[3].position.dy, 15.0);
// Move and up pointer events should have been returned.
expect(result.length, 6);
expect(result[4].timeStamp, const Duration(microseconds: 4500));
expect(result[4] is PointerMoveEvent, true);
expect(result[4].position.dx, 35.0);
expect(result[4].position.dy, 15.0);
expect(result[4].delta.dx, 10.0);
expect(result[4].delta.dy, -10.0);
expect(result[5].timeStamp, const Duration(microseconds: 4500));
expect(result[5] is PointerUpEvent, true);
expect(result[5].position.dx, 35.0);
expect(result[5].position.dy, 15.0);
resampler.addEvent(event5);
resampler.sample(const Duration(microseconds: 4500), result.add);
// No more pointer events should have been returned.
expect(result.length, 4);
expect(result.length, 6);
//
// Advance sample time to 5.5 ms.
......@@ -263,12 +293,18 @@ void main() {
resampler.sample(const Duration(microseconds: 5500), result.add);
// Remove pointer event should have been returned.
expect(result.length, 5);
expect(result[4].timeStamp, const Duration(microseconds: 5500));
expect(result[4] is PointerRemovedEvent, true);
expect(result[4].position.dx, 45.0);
expect(result[4].position.dy, 5.0);
// Hover and remove pointer event should have been returned.
expect(result.length, 8);
expect(result[6].timeStamp, const Duration(microseconds: 5500));
expect(result[6] is PointerHoverEvent, true);
expect(result[6].position.dx, 45.0);
expect(result[6].position.dy, 5.0);
expect(result[6].delta.dx, 10.0);
expect(result[6].delta.dy, -10.0);
expect(result[7].timeStamp, const Duration(microseconds: 5500));
expect(result[7] is PointerRemovedEvent, true);
expect(result[7].position.dx, 45.0);
expect(result[7].position.dy, 5.0);
//
// Advance sample time to 6.5 ms.
......@@ -277,7 +313,7 @@ void main() {
resampler.sample(const Duration(microseconds: 6500), result.add);
// No pointer events should have been returned.
expect(result.length, 5);
expect(result.length, 8);
});
test('quick tap', () {
......@@ -376,16 +412,22 @@ void main() {
resampler.sample(const Duration(microseconds: 2500), result.add);
// Last two pointer events should have been returned.
expect(result.length, 6);
// Move, up and removed pointer events should have been returned.
expect(result.length, 7);
expect(result[4].timeStamp, const Duration(microseconds: 2500));
expect(result[4] is PointerUpEvent, true);
expect(result[4] is PointerMoveEvent, true);
expect(result[4].position.dx, 15.0);
expect(result[4].position.dy, 0.0);
expect(result[4].delta.dx, 5.0);
expect(result[4].delta.dy, 0.0);
expect(result[5].timeStamp, const Duration(microseconds: 2500));
expect(result[5] is PointerRemovedEvent, true);
expect(result[5] is PointerUpEvent, true);
expect(result[5].position.dx, 15.0);
expect(result[5].position.dy, 0.0);
expect(result[6].timeStamp, const Duration(microseconds: 2500));
expect(result[6] is PointerRemovedEvent, true);
expect(result[6].position.dx, 15.0);
expect(result[6].position.dy, 0.0);
});
test('advance fast', () {
......@@ -422,21 +464,27 @@ void main() {
resampler.sample(const Duration(microseconds: 5500), result.add);
// Up and removed pointer events should have been returned.
expect(result.length, 4);
// Move, up and removed pointer events should have been returned.
expect(result.length, 5);
expect(result[2].timeStamp, const Duration(microseconds: 5500));
expect(result[2] is PointerUpEvent, true);
expect(result[2] is PointerMoveEvent, true);
expect(result[2].position.dx, 30.0);
expect(result[2].position.dy, 0.0);
expect(result[2].delta.dx, 17.5);
expect(result[2].delta.dy, 0.0);
expect(result[3].timeStamp, const Duration(microseconds: 5500));
expect(result[3] is PointerRemovedEvent, true);
expect(result[3] is PointerUpEvent, true);
expect(result[3].position.dx, 30.0);
expect(result[3].position.dy, 0.0);
expect(result[4].timeStamp, const Duration(microseconds: 5500));
expect(result[4] is PointerRemovedEvent, true);
expect(result[4].position.dx, 30.0);
expect(result[4].position.dy, 0.0);
resampler.sample(const Duration(microseconds: 6500), result.add);
// No pointer events should have been returned.
expect(result.length, 4);
expect(result.length, 5);
});
test('skip', () {
......@@ -476,28 +524,34 @@ void main() {
resampler.sample(const Duration(microseconds: 4500), result.add);
// All remaining pointer events should have been returned.
expect(result.length, 6);
expect(result.length, 7);
expect(result[2].timeStamp, const Duration(microseconds: 4500));
expect(result[2] is PointerUpEvent, true);
expect(result[2] is PointerMoveEvent, true);
expect(result[2].position.dx, 25.0);
expect(result[2].position.dy, 0.0);
expect(result[2].delta.dx, 20.0);
expect(result[2].delta.dy, 0.0);
expect(result[3].timeStamp, const Duration(microseconds: 4500));
expect(result[3] is PointerDownEvent, true);
expect(result[3] is PointerUpEvent, true);
expect(result[3].position.dx, 25.0);
expect(result[3].position.dy, 0.0);
expect(result[4].timeStamp, const Duration(microseconds: 4500));
expect(result[4] is PointerUpEvent, true);
expect(result[4] is PointerDownEvent, true);
expect(result[4].position.dx, 25.0);
expect(result[4].position.dy, 0.0);
expect(result[5].timeStamp, const Duration(microseconds: 4500));
expect(result[5] is PointerRemovedEvent, true);
expect(result[5] is PointerUpEvent, true);
expect(result[5].position.dx, 25.0);
expect(result[5].position.dy, 0.0);
expect(result[6].timeStamp, const Duration(microseconds: 4500));
expect(result[6] is PointerRemovedEvent, true);
expect(result[6].position.dx, 25.0);
expect(result[6].position.dy, 0.0);
resampler.sample(const Duration(microseconds: 5500), result.add);
// No pointer events should have been returned.
expect(result.length, 6);
expect(result.length, 7);
});
test('skip all', () {
......@@ -602,4 +656,65 @@ void main() {
// No pointer events should have been returned.
expect(result.length, 5);
});
test('synthetic move', () {
final PointerEventResampler resampler = PointerEventResampler();
final PointerEvent event0 = _createSimulatedPointerAddedEvent(1000, 0.0, 0.0);
final PointerEvent event1 = _createSimulatedPointerDownEvent(2000, 0.0, 0.0);
final PointerEvent event2 = _createSimulatedPointerMoveEvent(3000, 10.0, 0.0, 10.0, 0.0);
final PointerEvent event3 = _createSimulatedPointerUpEvent(4000, 10.0, 0.0);
final PointerEvent event4 = _createSimulatedPointerRemovedEvent(5000, 10.0, 0.0);
resampler
..addEvent(event0)
..addEvent(event1)
..addEvent(event2)
..addEvent(event3)
..addEvent(event4);
final List<PointerEvent> result = <PointerEvent>[];
resampler.sample(const Duration(microseconds: 500), result.add);
// No pointer events should have been returned.
expect(result.isEmpty, true);
resampler.sample(const Duration(microseconds: 2000), result.add);
// Added and down pointer events should have been returned.
expect(result.length, 2);
expect(result[0].timeStamp, const Duration(microseconds: 2000));
expect(result[0] is PointerAddedEvent, true);
expect(result[0].position.dx, 0.0);
expect(result[0].position.dy, 0.0);
expect(result[1].timeStamp, const Duration(microseconds: 2000));
expect(result[1] is PointerDownEvent, true);
expect(result[1].position.dx, 0.0);
expect(result[1].position.dy, 0.0);
resampler.sample(const Duration(microseconds: 5000), result.add);
// All remaining pointer events and a synthetic move event should
// have been returned.
expect(result.length, 5);
expect(result[2].timeStamp, const Duration(microseconds: 5000));
expect(result[2] is PointerMoveEvent, true);
expect(result[2].position.dx, 10.0);
expect(result[2].position.dy, 0.0);
expect(result[2].delta.dx, 10.0);
expect(result[2].delta.dy, 0.0);
expect(result[3].timeStamp, const Duration(microseconds: 5000));
expect(result[3] is PointerUpEvent, true);
expect(result[3].position.dx, 10.0);
expect(result[3].position.dy, 0.0);
expect(result[4].timeStamp, const Duration(microseconds: 5000));
expect(result[4] is PointerRemovedEvent, true);
expect(result[4].position.dx, 10.0);
expect(result[4].position.dy, 0.0);
resampler.sample(const Duration(microseconds: 10000), result.add);
// No pointer events should have been returned.
expect(result.length, 5);
});
}
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