mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
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
263 lines
6.6 KiB
Dart
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();
|
|
});
|
|
|
|
}
|