Adam Barth 51b1550df7 Delay win-by-default in gesture arena (#3552)
Wait until the end of the microtask to tell gesture recognizers that
they've won in the gesture arena. This lets recognizers dispose reject
themselves at arbitrary times without triggering gestures in awkward
call stacks.

Fixes #3183
2016-04-26 14:52:31 -07:00

263 lines
6.6 KiB
Dart

// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/gestures.dart';
import 'package:test/test.dart';
import 'gesture_tester.dart';
class TestGestureArenaMember extends GestureArenaMember {
@override
void acceptGesture(int key) {}
@override
void rejectGesture(int key) {}
}
void main() {
setUp(ensureGestureBinding);
// Down/up pair 1: normal tap sequence
const PointerDownEvent down1 = const PointerDownEvent(
pointer: 1,
position: const Point(10.0, 10.0)
);
const PointerUpEvent up1 = const PointerUpEvent(
pointer: 1,
position: const Point(11.0, 9.0)
);
// Down/up pair 2: normal tap sequence far away from pair 1
const PointerDownEvent down2 = const PointerDownEvent(
pointer: 2,
position: const Point(30.0, 30.0)
);
const PointerUpEvent up2 = const PointerUpEvent(
pointer: 2,
position: const Point(31.0, 29.0)
);
// Down/move/up sequence 3: intervening motion
const PointerDownEvent down3 = const PointerDownEvent(
pointer: 3,
position: const Point(10.0, 10.0)
);
const PointerMoveEvent move3 = const PointerMoveEvent(
pointer: 3,
position: const Point(25.0, 25.0)
);
const PointerUpEvent up3 = const PointerUpEvent(
pointer: 3,
position: const Point(25.0, 25.0)
);
testGesture('Should recognize tap', (GestureTester tester) {
TapGestureRecognizer tap = new TapGestureRecognizer();
bool tapRecognized = false;
tap.onTap = () {
tapRecognized = true;
};
tap.addPointer(down1);
tester.closeArena(1);
expect(tapRecognized, isFalse);
tester.route(down1);
expect(tapRecognized, isFalse);
tester.route(up1);
expect(tapRecognized, isTrue);
GestureBinding.instance.gestureArena.sweep(1);
expect(tapRecognized, isTrue);
tap.dispose();
});
testGesture('No duplicate tap events', (GestureTester tester) {
TapGestureRecognizer tap = new TapGestureRecognizer();
int tapsRecognized = 0;
tap.onTap = () {
tapsRecognized++;
};
tap.addPointer(down1);
tester.closeArena(1);
expect(tapsRecognized, 0);
tester.route(down1);
expect(tapsRecognized, 0);
tester.route(up1);
expect(tapsRecognized, 1);
GestureBinding.instance.gestureArena.sweep(1);
expect(tapsRecognized, 1);
tap.addPointer(down1);
tester.closeArena(1);
expect(tapsRecognized, 1);
tester.route(down1);
expect(tapsRecognized, 1);
tester.route(up1);
expect(tapsRecognized, 2);
GestureBinding.instance.gestureArena.sweep(1);
expect(tapsRecognized, 2);
tap.dispose();
});
testGesture('Should not recognize two overlapping taps', (GestureTester tester) {
TapGestureRecognizer tap = new TapGestureRecognizer();
int tapsRecognized = 0;
tap.onTap = () {
tapsRecognized++;
};
tap.addPointer(down1);
tester.closeArena(1);
expect(tapsRecognized, 0);
tester.route(down1);
expect(tapsRecognized, 0);
tap.addPointer(down2);
tester.closeArena(2);
expect(tapsRecognized, 0);
tester.route(down1);
expect(tapsRecognized, 0);
tester.route(up1);
expect(tapsRecognized, 1);
GestureBinding.instance.gestureArena.sweep(1);
expect(tapsRecognized, 1);
tester.route(up2);
expect(tapsRecognized, 1);
GestureBinding.instance.gestureArena.sweep(2);
expect(tapsRecognized, 1);
tap.dispose();
});
testGesture('Distance cancels tap', (GestureTester tester) {
TapGestureRecognizer tap = new TapGestureRecognizer();
bool tapRecognized = false;
tap.onTap = () {
tapRecognized = true;
};
bool tapCanceled = false;
tap.onTapCancel = () {
tapCanceled = true;
};
tap.addPointer(down3);
tester.closeArena(3);
expect(tapRecognized, isFalse);
expect(tapCanceled, isFalse);
tester.route(down3);
expect(tapRecognized, isFalse);
expect(tapCanceled, isFalse);
tester.route(move3);
expect(tapRecognized, isFalse);
expect(tapCanceled, isTrue);
tester.route(up3);
expect(tapRecognized, isFalse);
expect(tapCanceled, isTrue);
GestureBinding.instance.gestureArena.sweep(3);
expect(tapRecognized, isFalse);
expect(tapCanceled, isTrue);
tap.dispose();
});
testGesture('Timeout does not cancel tap', (GestureTester tester) {
TapGestureRecognizer tap = new TapGestureRecognizer();
bool tapRecognized = false;
tap.onTap = () {
tapRecognized = true;
};
tap.addPointer(down1);
tester.closeArena(1);
expect(tapRecognized, isFalse);
tester.route(down1);
expect(tapRecognized, isFalse);
tester.async.elapse(new Duration(milliseconds: 500));
expect(tapRecognized, isFalse);
tester.route(up1);
expect(tapRecognized, isTrue);
GestureBinding.instance.gestureArena.sweep(1);
expect(tapRecognized, isTrue);
tap.dispose();
});
testGesture('Should yield to other arena members', (GestureTester tester) {
TapGestureRecognizer tap = new TapGestureRecognizer();
bool tapRecognized = false;
tap.onTap = () {
tapRecognized = true;
};
tap.addPointer(down1);
TestGestureArenaMember member = new TestGestureArenaMember();
GestureArenaEntry entry = GestureBinding.instance.gestureArena.add(1, member);
GestureBinding.instance.gestureArena.hold(1);
tester.closeArena(1);
expect(tapRecognized, isFalse);
tester.route(down1);
expect(tapRecognized, isFalse);
tester.route(up1);
expect(tapRecognized, isFalse);
GestureBinding.instance.gestureArena.sweep(1);
expect(tapRecognized, isFalse);
entry.resolve(GestureDisposition.accepted);
expect(tapRecognized, isFalse);
tap.dispose();
});
testGesture('Should trigger on release of held arena', (GestureTester tester) {
TapGestureRecognizer tap = new TapGestureRecognizer();
bool tapRecognized = false;
tap.onTap = () {
tapRecognized = true;
};
tap.addPointer(down1);
TestGestureArenaMember member = new TestGestureArenaMember();
GestureArenaEntry entry = GestureBinding.instance.gestureArena.add(1, member);
GestureBinding.instance.gestureArena.hold(1);
tester.closeArena(1);
expect(tapRecognized, isFalse);
tester.route(down1);
expect(tapRecognized, isFalse);
tester.route(up1);
expect(tapRecognized, isFalse);
GestureBinding.instance.gestureArena.sweep(1);
expect(tapRecognized, isFalse);
entry.resolve(GestureDisposition.rejected);
tester.async.flushMicrotasks();
expect(tapRecognized, isTrue);
tap.dispose();
});
}