Unverified Commit 7f02566c authored by Tong Mu's avatar Tong Mu Committed by GitHub

Add "OneSequenceRecognizer.resolvePointer". Fix DragGestureRecognizer crash on...

Add "OneSequenceRecognizer.resolvePointer". Fix DragGestureRecognizer crash on multiple pointers (#39017)

* Add drag tests

* Add resolvePointer

* Correct monodrag implementation

* Fix some addTearDown
parent 74076958
...@@ -251,8 +251,7 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer { ...@@ -251,8 +251,7 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer {
if (event is PointerMoveEvent) { if (event is PointerMoveEvent) {
if (event.buttons != _initialButtons) { if (event.buttons != _initialButtons) {
resolve(GestureDisposition.rejected); _giveUpPointer(event.pointer);
stopTrackingPointer(event.pointer);
return; return;
} }
if (_state == _DragState.accepted) { if (_state == _DragState.accepted) {
...@@ -278,7 +277,12 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer { ...@@ -278,7 +277,12 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer {
resolve(GestureDisposition.accepted); resolve(GestureDisposition.accepted);
} }
} }
stopTrackingIfPointerNoLongerDown(event); if (event is PointerUpEvent || event is PointerCancelEvent) {
_giveUpPointer(
event.pointer,
reject: event is PointerCancelEvent || _state ==_DragState.possible,
);
}
} }
@override @override
...@@ -325,7 +329,7 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer { ...@@ -325,7 +329,7 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer {
@override @override
void rejectGesture(int pointer) { void rejectGesture(int pointer) {
stopTrackingPointer(pointer); _giveUpPointer(pointer);
} }
@override @override
...@@ -349,6 +353,16 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer { ...@@ -349,6 +353,16 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer {
_state = _DragState.ready; _state = _DragState.ready;
} }
void _giveUpPointer(int pointer, {bool reject = true}) {
stopTrackingPointer(pointer);
if (reject) {
if (_velocityTrackers.containsKey(pointer)) {
_velocityTrackers.remove(pointer);
resolvePointer(pointer, GestureDisposition.rejected);
}
}
}
void _checkDown() { void _checkDown() {
assert(_initialButtons == kPrimaryButton); assert(_initialButtons == kPrimaryButton);
final DragDownDetails details = DragDownDetails( final DragDownDetails details = DragDownDetails(
......
...@@ -255,6 +255,18 @@ abstract class OneSequenceGestureRecognizer extends GestureRecognizer { ...@@ -255,6 +255,18 @@ abstract class OneSequenceGestureRecognizer extends GestureRecognizer {
entry.resolve(disposition); entry.resolve(disposition);
} }
/// Resolves this recognizer's participation in the given gesture arena with
/// the given disposition.
@protected
@mustCallSuper
void resolvePointer(int pointer, GestureDisposition disposition) {
final GestureArenaEntry entry = _entries[pointer];
if (entry != null) {
entry.resolve(disposition);
_entries.remove(pointer);
}
}
@override @override
void dispose() { void dispose() {
resolve(GestureDisposition.rejected); resolve(GestureDisposition.rejected);
......
...@@ -14,6 +14,8 @@ void main() { ...@@ -14,6 +14,8 @@ void main() {
testGesture('Should recognize pan', (GestureTester tester) { testGesture('Should recognize pan', (GestureTester tester) {
final PanGestureRecognizer pan = PanGestureRecognizer(); final PanGestureRecognizer pan = PanGestureRecognizer();
final TapGestureRecognizer tap = TapGestureRecognizer()..onTap = () {}; final TapGestureRecognizer tap = TapGestureRecognizer()..onTap = () {};
addTearDown(pan.dispose);
addTearDown(tap.dispose);
bool didStartPan = false; bool didStartPan = false;
pan.onStart = (_) { pan.onStart = (_) {
...@@ -74,15 +76,14 @@ void main() { ...@@ -74,15 +76,14 @@ void main() {
expect(didEndPan, isTrue); expect(didEndPan, isTrue);
didEndPan = false; didEndPan = false;
expect(didTap, isFalse); expect(didTap, isFalse);
pan.dispose();
tap.dispose();
}); });
testGesture('Should report most recent point to onStart by default', (GestureTester tester) { testGesture('Should report most recent point to onStart by default', (GestureTester tester) {
final HorizontalDragGestureRecognizer drag = HorizontalDragGestureRecognizer(); HorizontalDragGestureRecognizer drag = HorizontalDragGestureRecognizer();
final VerticalDragGestureRecognizer competingDrag = VerticalDragGestureRecognizer() VerticalDragGestureRecognizer competingDrag = VerticalDragGestureRecognizer()
..onStart = (_) {}; ..onStart = (_) {};
addTearDown(() => drag?.dispose);
addTearDown(() => competingDrag?.dispose);
Offset positionAtOnStart; Offset positionAtOnStart;
drag.onStart = (DragStartDetails details) { drag.onStart = (DragStartDetails details) {
...@@ -98,15 +99,19 @@ void main() { ...@@ -98,15 +99,19 @@ void main() {
tester.route(pointer.move(const Offset(30.0, 0.0))); tester.route(pointer.move(const Offset(30.0, 0.0)));
drag.dispose(); drag.dispose();
drag = null;
competingDrag.dispose(); competingDrag.dispose();
competingDrag = null;
expect(positionAtOnStart, const Offset(30.0, 00.0)); expect(positionAtOnStart, const Offset(30.0, 00.0));
}); });
testGesture('Should report most recent point to onStart with a start configuration', (GestureTester tester) { testGesture('Should report most recent point to onStart with a start configuration', (GestureTester tester) {
final HorizontalDragGestureRecognizer drag = HorizontalDragGestureRecognizer(); HorizontalDragGestureRecognizer drag = HorizontalDragGestureRecognizer();
final VerticalDragGestureRecognizer competingDrag = VerticalDragGestureRecognizer() VerticalDragGestureRecognizer competingDrag = VerticalDragGestureRecognizer()
..onStart = (_) {}; ..onStart = (_) {};
addTearDown(() => drag?.dispose);
addTearDown(() => competingDrag?.dispose);
Offset positionAtOnStart; Offset positionAtOnStart;
drag.onStart = (DragStartDetails details) { drag.onStart = (DragStartDetails details) {
...@@ -126,7 +131,9 @@ void main() { ...@@ -126,7 +131,9 @@ void main() {
tester.route(pointer.move(const Offset(30.0, 0.0))); tester.route(pointer.move(const Offset(30.0, 0.0)));
drag.dispose(); drag.dispose();
drag = null;
competingDrag.dispose(); competingDrag.dispose();
competingDrag = null;
expect(positionAtOnStart, const Offset(30.0, 0.0)); expect(positionAtOnStart, const Offset(30.0, 0.0));
expect(updateOffset, null); expect(updateOffset, null);
...@@ -134,6 +141,7 @@ void main() { ...@@ -134,6 +141,7 @@ void main() {
testGesture('Should recognize drag', (GestureTester tester) { testGesture('Should recognize drag', (GestureTester tester) {
final HorizontalDragGestureRecognizer drag = HorizontalDragGestureRecognizer() ..dragStartBehavior = DragStartBehavior.down; final HorizontalDragGestureRecognizer drag = HorizontalDragGestureRecognizer() ..dragStartBehavior = DragStartBehavior.down;
addTearDown(drag.dispose);
bool didStartDrag = false; bool didStartDrag = false;
drag.onStart = (_) { drag.onStart = (_) {
...@@ -181,12 +189,11 @@ void main() { ...@@ -181,12 +189,11 @@ void main() {
expect(updatedDelta, isNull); expect(updatedDelta, isNull);
expect(didEndDrag, isTrue); expect(didEndDrag, isTrue);
didEndDrag = false; didEndDrag = false;
drag.dispose();
}); });
testGesture('Should report original timestamps', (GestureTester tester) { testGesture('Should report original timestamps', (GestureTester tester) {
final HorizontalDragGestureRecognizer drag = HorizontalDragGestureRecognizer() ..dragStartBehavior = DragStartBehavior.down; final HorizontalDragGestureRecognizer drag = HorizontalDragGestureRecognizer() ..dragStartBehavior = DragStartBehavior.down;
addTearDown(drag.dispose);
Duration startTimestamp; Duration startTimestamp;
drag.onStart = (DragStartDetails details) { drag.onStart = (DragStartDetails details) {
...@@ -212,18 +219,16 @@ void main() { ...@@ -212,18 +219,16 @@ void main() {
tester.route(pointer.move(const Offset(20.0, 25.0), timeStamp: const Duration(milliseconds: 300))); tester.route(pointer.move(const Offset(20.0, 25.0), timeStamp: const Duration(milliseconds: 300)));
expect(updatedTimestamp, const Duration(milliseconds: 300)); expect(updatedTimestamp, const Duration(milliseconds: 300));
drag.dispose();
}); });
// TODO(jslavitz): Revert these tests.
testGesture('Should report initial down point to onStart with a down configuration', (GestureTester tester) { testGesture('Should report initial down point to onStart with a down configuration', (GestureTester tester) {
final HorizontalDragGestureRecognizer drag = HorizontalDragGestureRecognizer() HorizontalDragGestureRecognizer drag = HorizontalDragGestureRecognizer()
..dragStartBehavior = DragStartBehavior.down; ..dragStartBehavior = DragStartBehavior.down;
final VerticalDragGestureRecognizer competingDrag = VerticalDragGestureRecognizer() VerticalDragGestureRecognizer competingDrag = VerticalDragGestureRecognizer()
..dragStartBehavior = DragStartBehavior.down ..dragStartBehavior = DragStartBehavior.down
..onStart = (_) {}; ..onStart = (_) {};
addTearDown(() => drag?.dispose);
addTearDown(() => competingDrag?.dispose);
Offset positionAtOnStart; Offset positionAtOnStart;
drag.onStart = (DragStartDetails details) { drag.onStart = (DragStartDetails details) {
...@@ -245,7 +250,9 @@ void main() { ...@@ -245,7 +250,9 @@ void main() {
tester.route(pointer.move(const Offset(30.0, 0.0))); tester.route(pointer.move(const Offset(30.0, 0.0)));
drag.dispose(); drag.dispose();
drag = null;
competingDrag.dispose(); competingDrag.dispose();
competingDrag = null;
expect(positionAtOnStart, const Offset(10.0, 10.0)); expect(positionAtOnStart, const Offset(10.0, 10.0));
...@@ -256,10 +263,12 @@ void main() { ...@@ -256,10 +263,12 @@ void main() {
}); });
testGesture('Drag with multiple pointers in down behavior', (GestureTester tester) { testGesture('Drag with multiple pointers in down behavior', (GestureTester tester) {
final HorizontalDragGestureRecognizer drag1 = HorizontalDragGestureRecognizer drag1 =
HorizontalDragGestureRecognizer() ..dragStartBehavior = DragStartBehavior.down; HorizontalDragGestureRecognizer() ..dragStartBehavior = DragStartBehavior.down;
final VerticalDragGestureRecognizer drag2 = VerticalDragGestureRecognizer drag2 =
VerticalDragGestureRecognizer() ..dragStartBehavior = DragStartBehavior.down; VerticalDragGestureRecognizer() ..dragStartBehavior = DragStartBehavior.down;
addTearDown(() => drag1?.dispose);
addTearDown(() => drag2?.dispose);
final List<String> log = <String>[]; final List<String> log = <String>[];
drag1.onDown = (_) { log.add('drag1-down'); }; drag1.onDown = (_) { log.add('drag1-down'); };
...@@ -303,7 +312,9 @@ void main() { ...@@ -303,7 +312,9 @@ void main() {
tester.route(pointer6.up()); tester.route(pointer6.up());
drag1.dispose(); drag1.dispose();
drag1 = null;
drag2.dispose(); drag2.dispose();
drag2 = null;
expect(log, <String>[ expect(log, <String>[
'drag1-down', 'drag1-down',
...@@ -328,6 +339,7 @@ void main() { ...@@ -328,6 +339,7 @@ void main() {
testGesture('Clamp max velocity', (GestureTester tester) { testGesture('Clamp max velocity', (GestureTester tester) {
final HorizontalDragGestureRecognizer drag = HorizontalDragGestureRecognizer() ..dragStartBehavior = DragStartBehavior.down; final HorizontalDragGestureRecognizer drag = HorizontalDragGestureRecognizer() ..dragStartBehavior = DragStartBehavior.down;
addTearDown(drag.dispose);
Velocity velocity; Velocity velocity;
double primaryVelocity; double primaryVelocity;
...@@ -356,12 +368,11 @@ void main() { ...@@ -356,12 +368,11 @@ void main() {
expect(velocity.pixelsPerSecond.dx, inInclusiveRange(0.99 * kMaxFlingVelocity, kMaxFlingVelocity)); expect(velocity.pixelsPerSecond.dx, inInclusiveRange(0.99 * kMaxFlingVelocity, kMaxFlingVelocity));
expect(velocity.pixelsPerSecond.dy, moreOrLessEquals(0.0)); expect(velocity.pixelsPerSecond.dy, moreOrLessEquals(0.0));
expect(primaryVelocity, velocity.pixelsPerSecond.dx); expect(primaryVelocity, velocity.pixelsPerSecond.dx);
drag.dispose();
}); });
testGesture('Synthesized pointer events are ignored for velocity tracking', (GestureTester tester) { testGesture('Synthesized pointer events are ignored for velocity tracking', (GestureTester tester) {
final HorizontalDragGestureRecognizer drag = HorizontalDragGestureRecognizer() ..dragStartBehavior = DragStartBehavior.down; final HorizontalDragGestureRecognizer drag = HorizontalDragGestureRecognizer() ..dragStartBehavior = DragStartBehavior.down;
addTearDown(drag.dispose);
Velocity velocity; Velocity velocity;
drag.onEnd = (DragEndDetails details) { drag.onEnd = (DragEndDetails details) {
...@@ -388,14 +399,13 @@ void main() { ...@@ -388,14 +399,13 @@ void main() {
tester.route(pointer.up(timeStamp: const Duration(milliseconds: 70))); tester.route(pointer.up(timeStamp: const Duration(milliseconds: 70)));
expect(velocity.pixelsPerSecond.dx, moreOrLessEquals(1000.0)); expect(velocity.pixelsPerSecond.dx, moreOrLessEquals(1000.0));
expect(velocity.pixelsPerSecond.dy, moreOrLessEquals(0.0)); expect(velocity.pixelsPerSecond.dy, moreOrLessEquals(0.0));
drag.dispose();
}); });
/// Checks that quick flick gestures with 1 down, 2 move and 1 up pointer /// Checks that quick flick gestures with 1 down, 2 move and 1 up pointer
/// events still have a velocity /// events still have a velocity
testGesture('Quick flicks have velocity', (GestureTester tester) { testGesture('Quick flicks have velocity', (GestureTester tester) {
final HorizontalDragGestureRecognizer drag = HorizontalDragGestureRecognizer() ..dragStartBehavior = DragStartBehavior.down; final HorizontalDragGestureRecognizer drag = HorizontalDragGestureRecognizer() ..dragStartBehavior = DragStartBehavior.down;
addTearDown(drag.dispose);
Velocity velocity; Velocity velocity;
drag.onEnd = (DragEndDetails details) { drag.onEnd = (DragEndDetails details) {
...@@ -413,8 +423,6 @@ void main() { ...@@ -413,8 +423,6 @@ void main() {
// 3 events moving by 10px every 10ms = 1000px/s. // 3 events moving by 10px every 10ms = 1000px/s.
expect(velocity.pixelsPerSecond.dx, moreOrLessEquals(1000.0)); expect(velocity.pixelsPerSecond.dx, moreOrLessEquals(1000.0));
expect(velocity.pixelsPerSecond.dy, moreOrLessEquals(0.0)); expect(velocity.pixelsPerSecond.dy, moreOrLessEquals(0.0));
drag.dispose();
}); });
testGesture('Drag details', (GestureTester tester) { testGesture('Drag details', (GestureTester tester) {
...@@ -426,6 +434,7 @@ void main() { ...@@ -426,6 +434,7 @@ void main() {
testGesture('Should recognize drag', (GestureTester tester) { testGesture('Should recognize drag', (GestureTester tester) {
final HorizontalDragGestureRecognizer drag = HorizontalDragGestureRecognizer() ..dragStartBehavior = DragStartBehavior.down; final HorizontalDragGestureRecognizer drag = HorizontalDragGestureRecognizer() ..dragStartBehavior = DragStartBehavior.down;
addTearDown(drag.dispose);
bool didStartDrag = false; bool didStartDrag = false;
drag.onStart = (_) { drag.onStart = (_) {
...@@ -483,12 +492,11 @@ void main() { ...@@ -483,12 +492,11 @@ void main() {
expect(updatePrimaryDelta, isNull); expect(updatePrimaryDelta, isNull);
expect(didEndDrag, isTrue); expect(didEndDrag, isTrue);
didEndDrag = false; didEndDrag = false;
drag.dispose();
}); });
testGesture('Should recognize drag', (GestureTester tester) { testGesture('Should recognize drag', (GestureTester tester) {
final HorizontalDragGestureRecognizer drag = HorizontalDragGestureRecognizer() ..dragStartBehavior = DragStartBehavior.down; final HorizontalDragGestureRecognizer drag = HorizontalDragGestureRecognizer() ..dragStartBehavior = DragStartBehavior.down;
addTearDown(drag.dispose);
Offset latestGlobalPosition; Offset latestGlobalPosition;
drag.onStart = (DragStartDetails details) { drag.onStart = (DragStartDetails details) {
...@@ -518,7 +526,6 @@ void main() { ...@@ -518,7 +526,6 @@ void main() {
expect(latestDelta, const Offset(-20.0, 0.0)); expect(latestDelta, const Offset(-20.0, 0.0));
tester.route(pointer.up()); tester.route(pointer.up());
drag.dispose();
}); });
testGesture('Can filter drags based on device kind', (GestureTester tester) { testGesture('Can filter drags based on device kind', (GestureTester tester) {
...@@ -527,6 +534,7 @@ void main() { ...@@ -527,6 +534,7 @@ void main() {
kind: PointerDeviceKind.mouse, kind: PointerDeviceKind.mouse,
) )
..dragStartBehavior = DragStartBehavior.down; ..dragStartBehavior = DragStartBehavior.down;
addTearDown(drag.dispose);
bool didStartDrag = false; bool didStartDrag = false;
drag.onStart = (_) { drag.onStart = (_) {
...@@ -596,8 +604,6 @@ void main() { ...@@ -596,8 +604,6 @@ void main() {
expect(updatedDelta, isNull); expect(updatedDelta, isNull);
expect(didEndDrag, isTrue); expect(didEndDrag, isTrue);
didEndDrag = false; didEndDrag = false;
drag.dispose();
}); });
group('Enforce consistent-button restriction:', () { group('Enforce consistent-button restriction:', () {
...@@ -822,6 +828,8 @@ void main() { ...@@ -822,6 +828,8 @@ void main() {
..onCancel = () { ..onCancel = () {
recognized.add('primaryCancel'); recognized.add('primaryCancel');
}; };
addTearDown(pan.dispose);
addTearDown(tap.dispose);
final TestPointer pointer = TestPointer( final TestPointer pointer = TestPointer(
5, 5,
...@@ -840,9 +848,312 @@ void main() { ...@@ -840,9 +848,312 @@ void main() {
tester.route(pointer.up()); tester.route(pointer.up());
expect(recognized, <String>[]); expect(recognized, <String>[]);
recognized.clear(); recognized.clear();
});
pan.dispose(); testGesture('A secondary drag should not trigger primary', (GestureTester tester) {
tap.dispose(); final List<String> recognized = <String>[];
final TapGestureRecognizer tap = TapGestureRecognizer()
..onTap = () {}; // Need a listener to enable competition.
final PanGestureRecognizer pan = PanGestureRecognizer()
..onDown = (DragDownDetails details) {
recognized.add('primaryDown');
}
..onStart = (DragStartDetails details) {
recognized.add('primaryStart');
}
..onUpdate = (DragUpdateDetails details) {
recognized.add('primaryUpdate');
}
..onEnd = (DragEndDetails details) {
recognized.add('primaryEnd');
}
..onCancel = () {
recognized.add('primaryCancel');
};
final TestPointer pointer = TestPointer(
5,
PointerDeviceKind.touch,
0,
kSecondaryButton,
);
final PointerDownEvent down = pointer.down(const Offset(10.0, 10.0));
pan.addPointer(down);
tap.addPointer(down);
tester.closeArena(5);
tester.route(down);
tester.route(pointer.move(const Offset(20.0, 30.0)));
tester.route(pointer.move(const Offset(20.0, 25.0)));
tester.route(pointer.up());
expect(recognized, <String>[]);
recognized.clear(); recognized.clear();
addTearDown(pan.dispose);
addTearDown(tap.dispose);
recognized.clear();
});
testGesture('On multiple pointers, DragGestureRecognizer is canceled '
'when all pointers are canceled (FIFO)', (GestureTester tester) {
// This test simulates the following scenario:
// P1 down, P2 down, P1 up, P2 up
final List<String> logs = <String>[];
final HorizontalDragGestureRecognizer hori = HorizontalDragGestureRecognizer()
..onDown = (DragDownDetails details) { logs.add('downH'); }
..onStart = (DragStartDetails details) { logs.add('startH'); }
..onUpdate = (DragUpdateDetails details) { logs.add('updateH'); }
..onEnd = (DragEndDetails details) { logs.add('endH'); }
..onCancel = () { logs.add('cancelH'); };
// Competitor
final TapGestureRecognizer vert = TapGestureRecognizer()
..onTapDown = (TapDownDetails details) { logs.add('downT'); }
..onTapUp = (TapUpDetails details) { logs.add('upT'); }
..onTapCancel = () {};
addTearDown(hori.dispose);
addTearDown(vert.dispose);
final TestPointer pointer1 = TestPointer(4, PointerDeviceKind.touch);
final TestPointer pointer2 = TestPointer(5, PointerDeviceKind.touch);
final PointerDownEvent down1 = pointer1.down(const Offset(10.0, 10.0));
final PointerDownEvent down2 = pointer2.down(const Offset(11.0, 10.0));
hori.addPointer(down1);
vert.addPointer(down1);
tester.route(down1);
tester.closeArena(pointer1.pointer);
expect(logs, <String>['downH']);
logs.clear();
hori.addPointer(down2);
vert.addPointer(down2);
tester.route(down2);
tester.closeArena(pointer2.pointer);
expect(logs, <String>[]);
logs.clear();
tester.route(pointer1.up());
GestureBinding.instance.gestureArena.sweep(pointer1.pointer);
expect(logs, <String>['downT', 'upT']);
logs.clear();
tester.route(pointer2.up());
GestureBinding.instance.gestureArena.sweep(pointer2.pointer);
expect(logs, <String>['cancelH']);
logs.clear();
});
testGesture('On multiple pointers, DragGestureRecognizer is canceled '
'when all pointers are canceled (FILO)', (GestureTester tester) {
// This test simulates the following scenario:
// P1 down, P2 down, P1 up, P2 up
final List<String> logs = <String>[];
final HorizontalDragGestureRecognizer hori = HorizontalDragGestureRecognizer()
..onDown = (DragDownDetails details) { logs.add('downH'); }
..onStart = (DragStartDetails details) { logs.add('startH'); }
..onUpdate = (DragUpdateDetails details) { logs.add('updateH'); }
..onEnd = (DragEndDetails details) { logs.add('endH'); }
..onCancel = () { logs.add('cancelH'); };
// Competitor
final TapGestureRecognizer vert = TapGestureRecognizer()
..onTapDown = (TapDownDetails details) { logs.add('downT'); }
..onTapUp = (TapUpDetails details) { logs.add('upT'); }
..onTapCancel = () {};
addTearDown(hori.dispose);
addTearDown(vert.dispose);
final TestPointer pointer1 = TestPointer(4, PointerDeviceKind.touch);
final TestPointer pointer2 = TestPointer(5, PointerDeviceKind.touch);
final PointerDownEvent down1 = pointer1.down(const Offset(10.0, 10.0));
final PointerDownEvent down2 = pointer2.down(const Offset(11.0, 10.0));
hori.addPointer(down1);
vert.addPointer(down1);
tester.route(down1);
tester.closeArena(pointer1.pointer);
expect(logs, <String>['downH']);
logs.clear();
hori.addPointer(down2);
vert.addPointer(down2);
tester.route(down2);
tester.closeArena(pointer2.pointer);
expect(logs, <String>[]);
logs.clear();
tester.route(pointer2.up());
GestureBinding.instance.gestureArena.sweep(pointer2.pointer);
// Tap is not triggered because pointer2 is not its primary pointer
expect(logs, <String>[]);
logs.clear();
tester.route(pointer1.up());
GestureBinding.instance.gestureArena.sweep(pointer1.pointer);
expect(logs, <String>['cancelH', 'downT', 'upT']);
logs.clear();
});
testGesture('On multiple pointers, DragGestureRecognizer is accepted when the '
'first pointer is accepted', (GestureTester tester) {
// This test simulates the following scenario:
// P1 down, P2 down, P1 moves away, P2 up
final List<String> logs = <String>[];
final HorizontalDragGestureRecognizer hori = HorizontalDragGestureRecognizer()
..onDown = (DragDownDetails details) { logs.add('downH'); }
..onStart = (DragStartDetails details) { logs.add('startH'); }
..onUpdate = (DragUpdateDetails details) { logs.add('updateH'); }
..onEnd = (DragEndDetails details) { logs.add('endH'); }
..onCancel = () { logs.add('cancelH'); };
// Competitor
final TapGestureRecognizer vert = TapGestureRecognizer()
..onTapDown = (TapDownDetails details) { logs.add('downT'); }
..onTapUp = (TapUpDetails details) { logs.add('upT'); }
..onTapCancel = () {};
addTearDown(hori.dispose);
addTearDown(vert.dispose);
final TestPointer pointer1 = TestPointer(4, PointerDeviceKind.touch);
final TestPointer pointer2 = TestPointer(5, PointerDeviceKind.touch);
final PointerDownEvent down1 = pointer1.down(const Offset(10.0, 10.0));
final PointerDownEvent down2 = pointer2.down(const Offset(11.0, 10.0));
hori.addPointer(down1);
vert.addPointer(down1);
tester.route(down1);
tester.closeArena(pointer1.pointer);
expect(logs, <String>['downH']);
logs.clear();
hori.addPointer(down2);
vert.addPointer(down2);
tester.route(down2);
tester.closeArena(pointer2.pointer);
expect(logs, <String>[]);
logs.clear();
tester.route(pointer1.move(const Offset(100, 100)));
expect(logs, <String>['startH']);
logs.clear();
tester.route(pointer2.up());
GestureBinding.instance.gestureArena.sweep(pointer2.pointer);
expect(logs, <String>[]);
logs.clear();
tester.route(pointer1.up());
GestureBinding.instance.gestureArena.sweep(pointer1.pointer);
expect(logs, <String>['endH']);
logs.clear();
});
testGesture('On multiple pointers, canceled pointers (due to up) do not '
'prevent later pointers getting accepted', (GestureTester tester) {
// This test simulates the following scenario:
// P1 down, P2 down, P1 Up, P2 moves away
final List<String> logs = <String>[];
final HorizontalDragGestureRecognizer hori = HorizontalDragGestureRecognizer()
..onDown = (DragDownDetails details) { logs.add('downH'); }
..onStart = (DragStartDetails details) { logs.add('startH'); }
..onUpdate = (DragUpdateDetails details) { logs.add('updateH'); }
..onEnd = (DragEndDetails details) { logs.add('endH'); }
..onCancel = () { logs.add('cancelH'); };
// Competitor
final TapGestureRecognizer vert = TapGestureRecognizer()
..onTapDown = (TapDownDetails details) { logs.add('downT'); }
..onTapUp = (TapUpDetails details) { logs.add('upT'); }
..onTapCancel = () {};
addTearDown(hori.dispose);
addTearDown(vert.dispose);
final TestPointer pointer1 = TestPointer(4, PointerDeviceKind.touch);
final TestPointer pointer2 = TestPointer(5, PointerDeviceKind.touch);
final PointerDownEvent down1 = pointer1.down(const Offset(10.0, 10.0));
final PointerDownEvent down2 = pointer2.down(const Offset(11.0, 10.0));
hori.addPointer(down1);
vert.addPointer(down1);
tester.route(down1);
tester.closeArena(pointer1.pointer);
expect(logs, <String>['downH']);
logs.clear();
hori.addPointer(down2);
vert.addPointer(down2);
tester.route(down2);
tester.closeArena(pointer2.pointer);
expect(logs, <String>[]);
logs.clear();
tester.route(pointer1.up());
GestureBinding.instance.gestureArena.sweep(pointer1.pointer);
expect(logs, <String>['downT', 'upT']);
logs.clear();
tester.route(pointer2.move(const Offset(100, 100)));
expect(logs, <String>['startH']);
logs.clear();
tester.route(pointer2.up());
GestureBinding.instance.gestureArena.sweep(pointer2.pointer);
expect(logs, <String>['endH']);
logs.clear();
});
testGesture('On multiple pointers, canceled pointers (due to buttons) do not '
'prevent later pointers getting accepted', (GestureTester tester) {
// This test simulates the following scenario:
// P1 down, P2 down, P1 change buttons, P2 moves away
final List<String> logs = <String>[];
final HorizontalDragGestureRecognizer hori = HorizontalDragGestureRecognizer()
..onDown = (DragDownDetails details) { logs.add('downH'); }
..onStart = (DragStartDetails details) { logs.add('startH'); }
..onUpdate = (DragUpdateDetails details) { logs.add('updateH'); }
..onEnd = (DragEndDetails details) { logs.add('endH'); }
..onCancel = () { logs.add('cancelH'); };
// Competitor
final TapGestureRecognizer vert = TapGestureRecognizer()
..onTapDown = (TapDownDetails details) { logs.add('downT'); }
..onTapUp = (TapUpDetails details) { logs.add('upT'); }
..onTapCancel = () {};
addTearDown(hori.dispose);
addTearDown(vert.dispose);
final TestPointer pointer1 = TestPointer(1, PointerDeviceKind.touch);
final TestPointer pointer2 = TestPointer(2, PointerDeviceKind.touch);
final PointerDownEvent down1 = pointer1.down(const Offset(10.0, 10.0));
final PointerDownEvent down2 = pointer2.down(const Offset(11.0, 10.0));
hori.addPointer(down1);
vert.addPointer(down1);
tester.route(down1);
tester.closeArena(pointer1.pointer);
hori.addPointer(down2);
vert.addPointer(down2);
tester.route(down2);
tester.closeArena(pointer2.pointer);
expect(logs, <String>['downH']);
logs.clear();
// Pointer 1 changes buttons, which cancel tap, leaving drag the only
// remaining member of arena 1, therefore drag is accepted.
tester.route(pointer1.move(const Offset(9.9, 9.9), buttons: kSecondaryButton));
expect(logs, <String>['startH']);
logs.clear();
tester.route(pointer2.move(const Offset(100, 100)));
expect(logs, <String>['updateH']);
logs.clear();
tester.route(pointer2.up());
GestureBinding.instance.gestureArena.sweep(pointer2.pointer);
expect(logs, <String>['endH']);
logs.clear();
}); });
} }
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