Merge pull request #1743 from krisgiesing/doubletap
Fix #1471 Add double tap gesture
This commit is contained in:
@@ -8,6 +8,7 @@ library gestures;
|
||||
export 'src/gestures/arena.dart';
|
||||
export 'src/gestures/constants.dart';
|
||||
export 'src/gestures/drag.dart';
|
||||
export 'src/gestures/double_tap.dart';
|
||||
export 'src/gestures/events.dart';
|
||||
export 'src/gestures/long_press.dart';
|
||||
export 'src/gestures/pointer_router.dart';
|
||||
|
||||
163
packages/flutter/lib/src/gestures/double_tap.dart
Normal file
163
packages/flutter/lib/src/gestures/double_tap.dart
Normal file
@@ -0,0 +1,163 @@
|
||||
// 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 'dart:async';
|
||||
|
||||
import 'arena.dart';
|
||||
import 'constants.dart';
|
||||
import 'events.dart';
|
||||
import 'recognizer.dart';
|
||||
import 'tap.dart';
|
||||
|
||||
class DoubleTapGestureRecognizer extends DisposableArenaMember {
|
||||
|
||||
DoubleTapGestureRecognizer({ this.router, this.onDoubleTap });
|
||||
|
||||
// Implementation notes:
|
||||
// The double tap recognizer can be in one of four states. There's no
|
||||
// explicit enum for the states, because they are already captured by
|
||||
// the state of existing fields. Specifically:
|
||||
// Waiting on first tap: In this state, the _trackers list is empty, and
|
||||
// _firstTap is null.
|
||||
// First tap in progress: In this state, the _trackers list contains all
|
||||
// the states for taps that have begun but not completed. This list can
|
||||
// have more than one entry if two pointers begin to tap.
|
||||
// Waiting on second tap: In this state, one of the in-progress taps has
|
||||
// completed successfully. The _trackers list is again empty, and
|
||||
// _firstTap records the successful tap.
|
||||
// Second tap in progress: Much like the "first tap in progress" state, but
|
||||
// _firstTap is non-null. If a tap completes successfully while in this
|
||||
// state, the callback is invoked and the state is reset.
|
||||
// There are various other scenarios that cause the state to reset:
|
||||
// - All in-progress taps are rejected (by time, distance, pointercancel, etc)
|
||||
// - The long timer between taps expires
|
||||
// - The gesture arena decides we have been rejected wholesale
|
||||
|
||||
PointerRouter router;
|
||||
GestureTapCallback onDoubleTap;
|
||||
|
||||
Timer _doubleTapTimer;
|
||||
TapTracker _firstTap;
|
||||
final Map<int, TapTracker> _trackers = new Map<int, TapTracker>();
|
||||
|
||||
void addPointer(PointerInputEvent event) {
|
||||
// Ignore out-of-bounds second taps
|
||||
if (_firstTap != null &&
|
||||
!_firstTap.isWithinTolerance(event, kDoubleTapTouchSlop))
|
||||
return;
|
||||
_stopDoubleTapTimer();
|
||||
TapTracker tracker = new TapTracker(
|
||||
event: event,
|
||||
entry: GestureArena.instance.add(event.pointer, this)
|
||||
);
|
||||
_trackers[event.pointer] = tracker;
|
||||
tracker.startTimer(() => _reject(tracker));
|
||||
tracker.startTrackingPointer(router, handleEvent);
|
||||
}
|
||||
|
||||
void handleEvent(PointerInputEvent event) {
|
||||
TapTracker tracker = _trackers[event.pointer];
|
||||
assert(tracker != null);
|
||||
if (event.type == 'pointerup') {
|
||||
if (_firstTap == null)
|
||||
_registerFirstTap(tracker);
|
||||
else
|
||||
_registerSecondTap(tracker);
|
||||
} else if (event.type == 'pointermove' &&
|
||||
!tracker.isWithinTolerance(event, kTouchSlop)) {
|
||||
_reject(tracker);
|
||||
} else if (event.type == 'pointercancel') {
|
||||
_reject(tracker);
|
||||
}
|
||||
}
|
||||
|
||||
void acceptGesture(int pointer) {}
|
||||
|
||||
void rejectGesture(int pointer) {
|
||||
TapTracker tracker = _trackers[pointer];
|
||||
// If tracker isn't in the list, check if this is the first tap tracker
|
||||
if (tracker == null &&
|
||||
_firstTap != null &&
|
||||
_firstTap.pointer == pointer)
|
||||
tracker = _firstTap;
|
||||
// If tracker is still null, we rejected ourselves already
|
||||
if (tracker != null)
|
||||
_reject(tracker);
|
||||
}
|
||||
|
||||
void _reject(TapTracker tracker) {
|
||||
_trackers.remove(tracker.pointer);
|
||||
tracker.entry.resolve(GestureDisposition.rejected);
|
||||
_freezeTracker(tracker);
|
||||
// If the first tap is in progress, and we've run out of taps to track,
|
||||
// reset won't have any work to do. But if we're in the second tap, we need
|
||||
// to clear intermediate state.
|
||||
if (_firstTap != null &&
|
||||
(_trackers.isEmpty || tracker == _firstTap))
|
||||
_reset();
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
_reset();
|
||||
router = null;
|
||||
}
|
||||
|
||||
void _reset() {
|
||||
_stopDoubleTapTimer();
|
||||
if (_firstTap != null) {
|
||||
// Note, order is important below in order for the resolve -> reject logic
|
||||
// to work properly
|
||||
TapTracker tracker = _firstTap;
|
||||
_firstTap = null;
|
||||
_reject(tracker);
|
||||
GestureArena.instance.release(tracker.pointer);
|
||||
}
|
||||
_clearTrackers();
|
||||
}
|
||||
|
||||
void _registerFirstTap(TapTracker tracker) {
|
||||
_startDoubleTapTimer();
|
||||
GestureArena.instance.hold(tracker.pointer);
|
||||
// Note, order is important below in order for the clear -> reject logic to
|
||||
// work properly.
|
||||
_freezeTracker(tracker);
|
||||
_trackers.remove(tracker.pointer);
|
||||
_clearTrackers();
|
||||
_firstTap = tracker;
|
||||
}
|
||||
|
||||
void _registerSecondTap(TapTracker tracker) {
|
||||
_firstTap.entry.resolve(GestureDisposition.accepted);
|
||||
tracker.entry.resolve(GestureDisposition.accepted);
|
||||
_freezeTracker(tracker);
|
||||
_trackers.remove(tracker.pointer);
|
||||
if (onDoubleTap != null)
|
||||
onDoubleTap();
|
||||
_reset();
|
||||
}
|
||||
|
||||
void _clearTrackers() {
|
||||
List<TapTracker> localTrackers = new List<TapTracker>.from(_trackers.values);
|
||||
for (TapTracker tracker in localTrackers)
|
||||
_reject(tracker);
|
||||
assert(_trackers.isEmpty);
|
||||
}
|
||||
|
||||
void _freezeTracker(TapTracker tracker) {
|
||||
tracker.stopTimer();
|
||||
tracker.stopTrackingPointer(router, handleEvent);
|
||||
}
|
||||
|
||||
void _startDoubleTapTimer() {
|
||||
_doubleTapTimer ??= new Timer(kDoubleTapTimeout, () => _reset());
|
||||
}
|
||||
|
||||
void _stopDoubleTapTimer() {
|
||||
if (_doubleTapTimer != null) {
|
||||
_doubleTapTimer.cancel();
|
||||
_doubleTapTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,6 +2,10 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
export 'dart:ui' show Point;
|
||||
|
||||
/// Base class for input events.
|
||||
class InputEvent {
|
||||
|
||||
@@ -67,4 +71,5 @@ class PointerInputEvent extends InputEvent {
|
||||
final double orientation;
|
||||
final double tilt;
|
||||
|
||||
ui.Point get position => new ui.Point(x, y);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,11 @@ import 'pointer_router.dart';
|
||||
|
||||
export 'pointer_router.dart' show PointerRouter;
|
||||
|
||||
abstract class GestureRecognizer extends GestureArenaMember {
|
||||
abstract class DisposableArenaMember extends GestureArenaMember {
|
||||
void dispose();
|
||||
}
|
||||
|
||||
abstract class GestureRecognizer extends DisposableArenaMember {
|
||||
GestureRecognizer({ PointerRouter router }) : _router = router {
|
||||
assert(_router != null);
|
||||
}
|
||||
@@ -102,10 +106,12 @@ abstract class PrimaryPointerGestureRecognizer extends GestureRecognizer {
|
||||
assert(state != GestureRecognizerState.ready);
|
||||
if (state == GestureRecognizerState.possible && event.pointer == primaryPointer) {
|
||||
// TODO(abarth): Maybe factor the slop handling out into a separate class?
|
||||
if (event.type == 'pointermove' && _getDistance(event) > kTouchSlop)
|
||||
if (event.type == 'pointermove' && _getDistance(event) > kTouchSlop) {
|
||||
resolve(GestureDisposition.rejected);
|
||||
else
|
||||
stopTrackingPointer(event.pointer);
|
||||
} else {
|
||||
handlePrimaryPointer(event);
|
||||
}
|
||||
}
|
||||
stopTrackingIfPointerNoLongerDown(event);
|
||||
}
|
||||
|
||||
@@ -2,37 +2,179 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'arena.dart';
|
||||
import 'constants.dart';
|
||||
import 'events.dart';
|
||||
import 'pointer_router.dart';
|
||||
import 'recognizer.dart';
|
||||
|
||||
typedef void GestureTapCallback();
|
||||
|
||||
class TapGestureRecognizer extends PrimaryPointerGestureRecognizer {
|
||||
TapGestureRecognizer({ PointerRouter router, this.onTap })
|
||||
: super(router: router);
|
||||
enum TapResolution {
|
||||
tap,
|
||||
cancel
|
||||
}
|
||||
|
||||
/// TapTracker helps track individual tap sequences as part of a
|
||||
/// larger gesture.
|
||||
class TapTracker {
|
||||
|
||||
TapTracker({ PointerInputEvent event, this.entry })
|
||||
: pointer = event.pointer,
|
||||
_initialPosition = event.position,
|
||||
_isTrackingPointer = false {
|
||||
assert(event.type == 'pointerdown');
|
||||
}
|
||||
|
||||
int pointer;
|
||||
GestureArenaEntry entry;
|
||||
ui.Point _initialPosition;
|
||||
bool _isTrackingPointer;
|
||||
Timer _timer;
|
||||
|
||||
void startTimer(void callback()) {
|
||||
_timer ??= new Timer(kTapTimeout, callback);
|
||||
}
|
||||
|
||||
void stopTimer() {
|
||||
if (_timer != null) {
|
||||
_timer.cancel();
|
||||
_timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
void startTrackingPointer(PointerRouter router, PointerRoute route) {
|
||||
if (!_isTrackingPointer) {
|
||||
_isTrackingPointer = true;
|
||||
router.addRoute(pointer, route);
|
||||
}
|
||||
}
|
||||
|
||||
void stopTrackingPointer(PointerRouter router, PointerRoute route) {
|
||||
if (_isTrackingPointer) {
|
||||
_isTrackingPointer = false;
|
||||
router.removeRoute(pointer, route);
|
||||
}
|
||||
}
|
||||
|
||||
bool isWithinTolerance(PointerInputEvent event, double tolerance) {
|
||||
ui.Offset offset = event.position - _initialPosition;
|
||||
return offset.distance <= tolerance;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// TapGesture represents a full gesture resulting from a single tap
|
||||
/// sequence. Tap gestures are passive, meaning that they will not
|
||||
/// pre-empt any other arena member in play.
|
||||
class TapGesture extends TapTracker {
|
||||
|
||||
TapGesture({ this.gestureRecognizer, PointerInputEvent event })
|
||||
: super(event: event) {
|
||||
entry = GestureArena.instance.add(event.pointer, gestureRecognizer);
|
||||
_wonArena = false;
|
||||
_didTap = false;
|
||||
startTimer(() => cancel());
|
||||
startTrackingPointer(gestureRecognizer.router, handleEvent);
|
||||
}
|
||||
|
||||
TapGestureRecognizer gestureRecognizer;
|
||||
|
||||
bool _wonArena;
|
||||
bool _didTap;
|
||||
|
||||
void handleEvent(PointerInputEvent event) {
|
||||
assert(event.pointer == pointer);
|
||||
if (event.type == 'pointermove' && !isWithinTolerance(event, kTouchSlop)) {
|
||||
cancel();
|
||||
} else if (event.type == 'pointercancel') {
|
||||
cancel();
|
||||
} else if (event.type == 'pointerup') {
|
||||
stopTimer();
|
||||
stopTrackingPointer(gestureRecognizer.router, handleEvent);
|
||||
_didTap = true;
|
||||
_check();
|
||||
}
|
||||
}
|
||||
|
||||
void accept() {
|
||||
_wonArena = true;
|
||||
_check();
|
||||
}
|
||||
|
||||
void reject() {
|
||||
stopTimer();
|
||||
stopTrackingPointer(gestureRecognizer.router, handleEvent);
|
||||
gestureRecognizer._resolveTap(pointer, TapResolution.cancel);
|
||||
}
|
||||
|
||||
void cancel() {
|
||||
// If we won the arena already, then entry is resolved, so resolving
|
||||
// again is a no-op. But we still need to clean up our own state.
|
||||
if (_wonArena)
|
||||
reject();
|
||||
else
|
||||
entry.resolve(GestureDisposition.rejected);
|
||||
}
|
||||
|
||||
void _check() {
|
||||
if (_wonArena && _didTap)
|
||||
gestureRecognizer._resolveTap(pointer, TapResolution.tap);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TapGestureRecognizer extends DisposableArenaMember {
|
||||
TapGestureRecognizer({ this.router, this.onTap, this.onTapDown, this.onTapCancel });
|
||||
|
||||
PointerRouter router;
|
||||
GestureTapCallback onTap;
|
||||
GestureTapCallback onTapDown;
|
||||
GestureTapCallback onTapCancel;
|
||||
|
||||
void handlePrimaryPointer(PointerInputEvent event) {
|
||||
if (event.type == 'pointerdown') {
|
||||
if (onTapDown != null)
|
||||
onTapDown();
|
||||
} else if (event.type == 'pointerup') {
|
||||
resolve(GestureDisposition.accepted);
|
||||
if (onTap != null)
|
||||
onTap();
|
||||
}
|
||||
Map<int, TapGesture> _gestureMap = new Map<int, TapGesture>();
|
||||
|
||||
void addPointer(PointerInputEvent event) {
|
||||
assert(!_gestureMap.containsKey(event.pointer));
|
||||
_gestureMap[event.pointer] = new TapGesture(
|
||||
gestureRecognizer: this,
|
||||
event: event
|
||||
);
|
||||
if (onTapDown != null)
|
||||
onTapDown();
|
||||
}
|
||||
|
||||
void acceptGesture(int pointer) {
|
||||
assert(_gestureMap.containsKey(pointer));
|
||||
_gestureMap[pointer]?.accept();
|
||||
}
|
||||
|
||||
void rejectGesture(int pointer) {
|
||||
super.rejectGesture(pointer);
|
||||
if (pointer == primaryPointer) {
|
||||
assert(state == GestureRecognizerState.defunct);
|
||||
assert(_gestureMap.containsKey(pointer));
|
||||
_gestureMap[pointer]?.reject();
|
||||
}
|
||||
|
||||
void _resolveTap(int pointer, TapResolution resolution) {
|
||||
_gestureMap.remove(pointer);
|
||||
if (resolution == TapResolution.tap) {
|
||||
if (onTap != null)
|
||||
onTap();
|
||||
} else {
|
||||
if (onTapCancel != null)
|
||||
onTapCancel();
|
||||
}
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
List<TapGesture> localGestures = new List<TapGesture>.from(_gestureMap.values);
|
||||
for (TapGesture gesture in localGestures)
|
||||
gesture.cancel();
|
||||
// Rejection of each gesture should cause it to be removed from our map
|
||||
assert(_gestureMap.isEmpty);
|
||||
router = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ class GestureDetector extends StatefulComponent {
|
||||
Key key,
|
||||
this.child,
|
||||
this.onTap,
|
||||
this.onDoubleTap,
|
||||
this.onTapDown,
|
||||
this.onTapCancel,
|
||||
this.onShowPress,
|
||||
@@ -55,6 +56,7 @@ class GestureDetector extends StatefulComponent {
|
||||
final GestureTapCallback onTap;
|
||||
final GestureTapCallback onTapDown;
|
||||
final GestureTapCallback onTapCancel;
|
||||
final GestureTapCallback onDoubleTap;
|
||||
|
||||
final GestureShowPressCallback onShowPress;
|
||||
final GestureLongPressCallback onLongPress;
|
||||
@@ -82,6 +84,7 @@ class _GestureDetectorState extends State<GestureDetector> {
|
||||
final PointerRouter _router = FlutterBinding.instance.pointerRouter;
|
||||
|
||||
TapGestureRecognizer _tap;
|
||||
DoubleTapGestureRecognizer _doubleTap;
|
||||
ShowPressGestureRecognizer _showPress;
|
||||
LongPressGestureRecognizer _longPress;
|
||||
VerticalDragGestureRecognizer _verticalDrag;
|
||||
@@ -100,6 +103,7 @@ class _GestureDetectorState extends State<GestureDetector> {
|
||||
|
||||
void dispose() {
|
||||
_tap = _ensureDisposed(_tap);
|
||||
_doubleTap = _ensureDisposed(_doubleTap);
|
||||
_showPress = _ensureDisposed(_showPress);
|
||||
_longPress = _ensureDisposed(_longPress);
|
||||
_verticalDrag = _ensureDisposed(_verticalDrag);
|
||||
@@ -111,6 +115,7 @@ class _GestureDetectorState extends State<GestureDetector> {
|
||||
|
||||
void _syncAll() {
|
||||
_syncTap();
|
||||
_syncDoubleTap();
|
||||
_syncShowPress();
|
||||
_syncLongPress();
|
||||
_syncVerticalDrag();
|
||||
@@ -131,6 +136,15 @@ class _GestureDetectorState extends State<GestureDetector> {
|
||||
}
|
||||
}
|
||||
|
||||
void _syncDoubleTap() {
|
||||
if (config.onDoubleTap == null) {
|
||||
_doubleTap = _ensureDisposed(_doubleTap);
|
||||
} else {
|
||||
_doubleTap ??= new DoubleTapGestureRecognizer(router: _router);
|
||||
_doubleTap.onDoubleTap = config.onDoubleTap;
|
||||
}
|
||||
}
|
||||
|
||||
void _syncShowPress() {
|
||||
if (config.onShowPress == null) {
|
||||
_showPress = _ensureDisposed(_showPress);
|
||||
@@ -199,7 +213,7 @@ class _GestureDetectorState extends State<GestureDetector> {
|
||||
}
|
||||
}
|
||||
|
||||
GestureRecognizer _ensureDisposed(GestureRecognizer recognizer) {
|
||||
DisposableArenaMember _ensureDisposed(DisposableArenaMember recognizer) {
|
||||
recognizer?.dispose();
|
||||
return null;
|
||||
}
|
||||
@@ -207,6 +221,8 @@ class _GestureDetectorState extends State<GestureDetector> {
|
||||
void _handlePointerDown(PointerInputEvent event) {
|
||||
if (_tap != null)
|
||||
_tap.addPointer(event);
|
||||
if (_doubleTap != null)
|
||||
_doubleTap.addPointer(event);
|
||||
if (_showPress != null)
|
||||
_showPress.addPointer(event);
|
||||
if (_longPress != null)
|
||||
|
||||
493
packages/unit/test/gestures/double_tap_test.dart
Normal file
493
packages/unit/test/gestures/double_tap_test.dart
Normal file
@@ -0,0 +1,493 @@
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:quiver/testing/async.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
class TestGestureArenaMember extends GestureArenaMember {
|
||||
void acceptGesture(Object key) {
|
||||
accepted = true;
|
||||
}
|
||||
void rejectGesture(Object key) {
|
||||
rejected = true;
|
||||
}
|
||||
bool accepted = false;
|
||||
bool rejected = false;
|
||||
}
|
||||
|
||||
void main() {
|
||||
|
||||
// Down/up pair 1: normal tap sequence
|
||||
final PointerInputEvent down1 = new PointerInputEvent(
|
||||
pointer: 1,
|
||||
type: 'pointerdown',
|
||||
x: 10.0,
|
||||
y: 10.0
|
||||
);
|
||||
|
||||
final PointerInputEvent up1 = new PointerInputEvent(
|
||||
pointer: 1,
|
||||
type: 'pointerup',
|
||||
x: 11.0,
|
||||
y: 9.0
|
||||
);
|
||||
|
||||
// Down/up pair 2: normal tap sequence close to pair 1
|
||||
final PointerInputEvent down2 = new PointerInputEvent(
|
||||
pointer: 2,
|
||||
type: 'pointerdown',
|
||||
x: 12.0,
|
||||
y: 12.0
|
||||
);
|
||||
|
||||
final PointerInputEvent up2 = new PointerInputEvent(
|
||||
pointer: 2,
|
||||
type: 'pointerup',
|
||||
x: 13.0,
|
||||
y: 11.0
|
||||
);
|
||||
|
||||
// Down/up pair 3: normal tap sequence far away from pair 1
|
||||
final PointerInputEvent down3 = new PointerInputEvent(
|
||||
pointer: 3,
|
||||
type: 'pointerdown',
|
||||
x: 30.0,
|
||||
y: 30.0
|
||||
);
|
||||
|
||||
final PointerInputEvent up3 = new PointerInputEvent(
|
||||
pointer: 3,
|
||||
type: 'pointerup',
|
||||
x: 31.0,
|
||||
y: 29.0
|
||||
);
|
||||
|
||||
// Down/move/up sequence 4: intervening motion
|
||||
final PointerInputEvent down4 = new PointerInputEvent(
|
||||
pointer: 4,
|
||||
type: 'pointerdown',
|
||||
x: 10.0,
|
||||
y: 10.0
|
||||
);
|
||||
|
||||
final PointerInputEvent move4 = new PointerInputEvent(
|
||||
pointer: 4,
|
||||
type: 'pointermove',
|
||||
x: 25.0,
|
||||
y: 25.0
|
||||
);
|
||||
|
||||
final PointerInputEvent up4 = new PointerInputEvent(
|
||||
pointer: 4,
|
||||
type: 'pointerup',
|
||||
x: 25.0,
|
||||
y: 25.0
|
||||
);
|
||||
|
||||
test('Should recognize double tap', () {
|
||||
PointerRouter router = new PointerRouter();
|
||||
DoubleTapGestureRecognizer tap = new DoubleTapGestureRecognizer(router: router);
|
||||
|
||||
bool doubleTapRecognized = false;
|
||||
tap.onDoubleTap = () {
|
||||
doubleTapRecognized = true;
|
||||
};
|
||||
|
||||
tap.addPointer(down1);
|
||||
GestureArena.instance.close(1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
router.route(down1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
router.route(up1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
GestureArena.instance.sweep(1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
tap.addPointer(down2);
|
||||
GestureArena.instance.close(2);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
router.route(down2);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
router.route(up2);
|
||||
expect(doubleTapRecognized, isTrue);
|
||||
GestureArena.instance.sweep(2);
|
||||
expect(doubleTapRecognized, isTrue);
|
||||
|
||||
tap.dispose();
|
||||
});
|
||||
|
||||
test('Inter-tap distance cancels double tap', () {
|
||||
PointerRouter router = new PointerRouter();
|
||||
DoubleTapGestureRecognizer tap = new DoubleTapGestureRecognizer(router: router);
|
||||
|
||||
bool doubleTapRecognized = false;
|
||||
tap.onDoubleTap = () {
|
||||
doubleTapRecognized = true;
|
||||
};
|
||||
|
||||
tap.addPointer(down1);
|
||||
GestureArena.instance.close(1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
router.route(down1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
router.route(up1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
GestureArena.instance.sweep(1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
tap.addPointer(down3);
|
||||
GestureArena.instance.close(3);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
router.route(down3);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
router.route(up3);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
GestureArena.instance.sweep(3);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
tap.dispose();
|
||||
});
|
||||
|
||||
test('Intra-tap distance cancels double tap', () {
|
||||
PointerRouter router = new PointerRouter();
|
||||
DoubleTapGestureRecognizer tap = new DoubleTapGestureRecognizer(router: router);
|
||||
|
||||
bool doubleTapRecognized = false;
|
||||
tap.onDoubleTap = () {
|
||||
doubleTapRecognized = true;
|
||||
};
|
||||
|
||||
tap.addPointer(down4);
|
||||
GestureArena.instance.close(4);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
router.route(down4);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
router.route(move4);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
router.route(up4);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
GestureArena.instance.sweep(4);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
tap.addPointer(down1);
|
||||
GestureArena.instance.close(1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
router.route(down2);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
router.route(up1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
GestureArena.instance.sweep(1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
tap.dispose();
|
||||
});
|
||||
|
||||
test('Inter-tap delay cancels double tap', () {
|
||||
PointerRouter router = new PointerRouter();
|
||||
DoubleTapGestureRecognizer tap = new DoubleTapGestureRecognizer(router: router);
|
||||
|
||||
bool doubleTapRecognized = false;
|
||||
tap.onDoubleTap = () {
|
||||
doubleTapRecognized = true;
|
||||
};
|
||||
|
||||
new FakeAsync().run((FakeAsync async) {
|
||||
tap.addPointer(down1);
|
||||
GestureArena.instance.close(1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
router.route(down1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
router.route(up1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
GestureArena.instance.sweep(1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
async.elapse(new Duration(milliseconds: 5000));
|
||||
tap.addPointer(down2);
|
||||
GestureArena.instance.close(2);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
router.route(down2);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
router.route(up2);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
GestureArena.instance.sweep(2);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
});
|
||||
|
||||
tap.dispose();
|
||||
});
|
||||
|
||||
test('Intra-tap delay cancels double tap', () {
|
||||
PointerRouter router = new PointerRouter();
|
||||
DoubleTapGestureRecognizer tap = new DoubleTapGestureRecognizer(router: router);
|
||||
|
||||
bool doubleTapRecognized = false;
|
||||
tap.onDoubleTap = () {
|
||||
doubleTapRecognized = true;
|
||||
};
|
||||
|
||||
new FakeAsync().run((FakeAsync async) {
|
||||
tap.addPointer(down1);
|
||||
GestureArena.instance.close(1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
router.route(down1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
async.elapse(new Duration(milliseconds: 1000));
|
||||
router.route(up1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
GestureArena.instance.sweep(1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
tap.addPointer(down2);
|
||||
GestureArena.instance.close(2);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
router.route(down2);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
router.route(up2);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
GestureArena.instance.sweep(2);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
});
|
||||
|
||||
tap.dispose();
|
||||
});
|
||||
|
||||
test('Should not recognize two overlapping taps', () {
|
||||
PointerRouter router = new PointerRouter();
|
||||
DoubleTapGestureRecognizer tap = new DoubleTapGestureRecognizer(router: router);
|
||||
|
||||
bool doubleTapRecognized = false;
|
||||
tap.onDoubleTap = () {
|
||||
doubleTapRecognized = true;
|
||||
};
|
||||
|
||||
tap.addPointer(down1);
|
||||
GestureArena.instance.close(1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
router.route(down1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
tap.addPointer(down2);
|
||||
GestureArena.instance.close(2);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
router.route(down1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
router.route(up1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
GestureArena.instance.sweep(1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
router.route(up2);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
GestureArena.instance.sweep(2);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
tap.dispose();
|
||||
});
|
||||
|
||||
test('Should recognize one tap of group followed by second tap', () {
|
||||
PointerRouter router = new PointerRouter();
|
||||
DoubleTapGestureRecognizer tap = new DoubleTapGestureRecognizer(router: router);
|
||||
|
||||
bool doubleTapRecognized = false;
|
||||
tap.onDoubleTap = () {
|
||||
doubleTapRecognized = true;
|
||||
};
|
||||
|
||||
tap.addPointer(down1);
|
||||
GestureArena.instance.close(1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
router.route(down1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
tap.addPointer(down2);
|
||||
GestureArena.instance.close(2);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
router.route(down1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
router.route(up1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
GestureArena.instance.sweep(1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
router.route(up2);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
GestureArena.instance.sweep(2);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
tap.addPointer(down1);
|
||||
GestureArena.instance.close(1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
router.route(down1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
router.route(up1);
|
||||
expect(doubleTapRecognized, isTrue);
|
||||
GestureArena.instance.sweep(1);
|
||||
expect(doubleTapRecognized, isTrue);
|
||||
|
||||
tap.dispose();
|
||||
|
||||
});
|
||||
|
||||
test('Should cancel on arena reject during first tap', () {
|
||||
PointerRouter router = new PointerRouter();
|
||||
DoubleTapGestureRecognizer tap = new DoubleTapGestureRecognizer(router: router);
|
||||
|
||||
bool doubleTapRecognized = false;
|
||||
tap.onDoubleTap = () {
|
||||
doubleTapRecognized = true;
|
||||
};
|
||||
|
||||
tap.addPointer(down1);
|
||||
TestGestureArenaMember member = new TestGestureArenaMember();
|
||||
GestureArenaEntry entry = GestureArena.instance.add(1, member);
|
||||
GestureArena.instance.close(1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
router.route(down1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
router.route(up1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
entry.resolve(GestureDisposition.accepted);
|
||||
expect(member.accepted, isTrue);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
GestureArena.instance.sweep(1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
tap.addPointer(down2);
|
||||
GestureArena.instance.close(2);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
router.route(down2);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
router.route(up2);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
GestureArena.instance.sweep(2);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
tap.dispose();
|
||||
});
|
||||
|
||||
test('Should cancel on arena reject between taps', () {
|
||||
PointerRouter router = new PointerRouter();
|
||||
DoubleTapGestureRecognizer tap = new DoubleTapGestureRecognizer(router: router);
|
||||
|
||||
bool doubleTapRecognized = false;
|
||||
tap.onDoubleTap = () {
|
||||
doubleTapRecognized = true;
|
||||
};
|
||||
|
||||
tap.addPointer(down1);
|
||||
TestGestureArenaMember member = new TestGestureArenaMember();
|
||||
GestureArenaEntry entry = GestureArena.instance.add(1, member);
|
||||
GestureArena.instance.close(1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
router.route(down1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
router.route(up1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
GestureArena.instance.sweep(1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
entry.resolve(GestureDisposition.accepted);
|
||||
expect(member.accepted, isTrue);
|
||||
|
||||
tap.addPointer(down2);
|
||||
GestureArena.instance.close(2);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
router.route(down2);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
router.route(up2);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
GestureArena.instance.sweep(2);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
tap.dispose();
|
||||
});
|
||||
|
||||
test('Should cancel on arena reject during last tap', () {
|
||||
PointerRouter router = new PointerRouter();
|
||||
DoubleTapGestureRecognizer tap = new DoubleTapGestureRecognizer(router: router);
|
||||
|
||||
bool doubleTapRecognized = false;
|
||||
tap.onDoubleTap = () {
|
||||
doubleTapRecognized = true;
|
||||
};
|
||||
|
||||
tap.addPointer(down1);
|
||||
TestGestureArenaMember member = new TestGestureArenaMember();
|
||||
GestureArenaEntry entry = GestureArena.instance.add(1, member);
|
||||
GestureArena.instance.close(1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
router.route(down1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
router.route(up1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
GestureArena.instance.sweep(1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
tap.addPointer(down2);
|
||||
GestureArena.instance.close(2);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
router.route(down2);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
entry.resolve(GestureDisposition.accepted);
|
||||
expect(member.accepted, isTrue);
|
||||
|
||||
router.route(up2);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
GestureArena.instance.sweep(2);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
tap.dispose();
|
||||
});
|
||||
|
||||
test('Passive gesture should trigger on double tap cancel', () {
|
||||
PointerRouter router = new PointerRouter();
|
||||
DoubleTapGestureRecognizer tap = new DoubleTapGestureRecognizer(router: router);
|
||||
|
||||
bool doubleTapRecognized = false;
|
||||
tap.onDoubleTap = () {
|
||||
doubleTapRecognized = true;
|
||||
};
|
||||
|
||||
new FakeAsync().run((FakeAsync async) {
|
||||
tap.addPointer(down1);
|
||||
TestGestureArenaMember member = new TestGestureArenaMember();
|
||||
GestureArena.instance.add(1, member);
|
||||
GestureArena.instance.close(1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
router.route(down1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
router.route(up1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
GestureArena.instance.sweep(1);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
expect(member.accepted, isFalse);
|
||||
|
||||
async.elapse(new Duration(milliseconds: 5000));
|
||||
|
||||
expect(member.accepted, isTrue);
|
||||
});
|
||||
|
||||
tap.dispose();
|
||||
});
|
||||
|
||||
}
|
||||
75
packages/unit/test/gestures/lsq_solver_test_disabled.dart
Normal file
75
packages/unit/test/gestures/lsq_solver_test_disabled.dart
Normal file
@@ -0,0 +1,75 @@
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
|
||||
void main() {
|
||||
void printFit(PolynomialFit fit) {
|
||||
print("Confidence: " + fit.confidence.toString());
|
||||
for (int i = 0; i < fit.coefficients.length; i++)
|
||||
print(i.toString() + ": " + fit.coefficients[i].toString());
|
||||
}
|
||||
|
||||
approx(double value, double expectation) {
|
||||
const double eps = 1e-6;
|
||||
return (value - expectation).abs() < eps;
|
||||
}
|
||||
|
||||
test('Least-squares fit: linear polynomial to line', () {
|
||||
List<double> x = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
|
||||
List<double> y = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
|
||||
List<double> w = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
|
||||
|
||||
LeastSquaresSolver solver = new LeastSquaresSolver(x, y, w);
|
||||
PolynomialFit fit = solver.solve(1);
|
||||
|
||||
expect(fit.coefficients.length, 2);
|
||||
expect(approx(fit.coefficients[0], 1.0), isTrue);
|
||||
expect(approx(fit.coefficients[1], 0.0), isTrue);
|
||||
expect(approx(fit.confidence, 1.0), isTrue);
|
||||
});
|
||||
|
||||
test('Least-squares fit: linear polynomial to sloped line', () {
|
||||
List<double> x = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
|
||||
List<double> y = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
|
||||
List<double> w = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
|
||||
|
||||
LeastSquaresSolver solver = new LeastSquaresSolver(x, y, w);
|
||||
PolynomialFit fit = solver.solve(1);
|
||||
|
||||
expect(fit.coefficients.length, 2);
|
||||
expect(approx(fit.coefficients[0], 1.0), isTrue);
|
||||
expect(approx(fit.coefficients[1], 1.0), isTrue);
|
||||
expect(approx(fit.confidence, 1.0), isTrue);
|
||||
});
|
||||
|
||||
test('Least-squares fit: quadratic polynomial to line', () {
|
||||
List<double> x = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
|
||||
List<double> y = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
|
||||
List<double> w = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
|
||||
|
||||
LeastSquaresSolver solver = new LeastSquaresSolver(x, y, w);
|
||||
PolynomialFit fit = solver.solve(2);
|
||||
|
||||
expect(fit.coefficients.length, 3);
|
||||
expect(approx(fit.coefficients[0], 1.0), isTrue);
|
||||
expect(approx(fit.coefficients[1], 0.0), isTrue);
|
||||
expect(approx(fit.coefficients[2], 0.0), isTrue);
|
||||
expect(approx(fit.confidence, 1.0), isTrue);
|
||||
});
|
||||
|
||||
test('Least-squares fit: quadratic polynomial to sloped line', () {
|
||||
List<double> x = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
|
||||
List<double> y = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
|
||||
List<double> w = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
|
||||
|
||||
LeastSquaresSolver solver = new LeastSquaresSolver(x, y, w);
|
||||
PolynomialFit fit = solver.solve(2);
|
||||
|
||||
expect(fit.coefficients.length, 3);
|
||||
expect(approx(fit.coefficients[0], 1.0), isTrue);
|
||||
expect(approx(fit.coefficients[1], 1.0), isTrue);
|
||||
expect(approx(fit.coefficients[2], 0.0), isTrue);
|
||||
expect(approx(fit.confidence, 1.0), isTrue);
|
||||
});
|
||||
|
||||
}
|
||||
@@ -1,7 +1,66 @@
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:quiver/testing/async.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
class TestGestureArenaMember extends GestureArenaMember {
|
||||
void acceptGesture(Object key) {}
|
||||
void rejectGesture(Object key) {}
|
||||
}
|
||||
|
||||
void main() {
|
||||
|
||||
// Down/up pair 1: normal tap sequence
|
||||
final PointerInputEvent down1 = new PointerInputEvent(
|
||||
pointer: 1,
|
||||
type: 'pointerdown',
|
||||
x: 10.0,
|
||||
y: 10.0
|
||||
);
|
||||
|
||||
final PointerInputEvent up1 = new PointerInputEvent(
|
||||
pointer: 1,
|
||||
type: 'pointerup',
|
||||
x: 11.0,
|
||||
y: 9.0
|
||||
);
|
||||
|
||||
// Down/up pair 2: normal tap sequence far away from pair 1
|
||||
final PointerInputEvent down2 = new PointerInputEvent(
|
||||
pointer: 2,
|
||||
type: 'pointerdown',
|
||||
x: 30.0,
|
||||
y: 30.0
|
||||
);
|
||||
|
||||
final PointerInputEvent up2 = new PointerInputEvent(
|
||||
pointer: 2,
|
||||
type: 'pointerup',
|
||||
x: 31.0,
|
||||
y: 29.0
|
||||
);
|
||||
|
||||
// Down/move/up sequence 3: intervening motion
|
||||
final PointerInputEvent down3 = new PointerInputEvent(
|
||||
pointer: 3,
|
||||
type: 'pointerdown',
|
||||
x: 10.0,
|
||||
y: 10.0
|
||||
);
|
||||
|
||||
final PointerInputEvent move3 = new PointerInputEvent(
|
||||
pointer: 3,
|
||||
type: 'pointermove',
|
||||
x: 25.0,
|
||||
y: 25.0
|
||||
);
|
||||
|
||||
final PointerInputEvent up3 = new PointerInputEvent(
|
||||
pointer: 3,
|
||||
type: 'pointerup',
|
||||
x: 25.0,
|
||||
y: 25.0
|
||||
);
|
||||
|
||||
test('Should recognize tap', () {
|
||||
PointerRouter router = new PointerRouter();
|
||||
TapGestureRecognizer tap = new TapGestureRecognizer(router: router);
|
||||
@@ -11,29 +70,163 @@ void main() {
|
||||
tapRecognized = true;
|
||||
};
|
||||
|
||||
PointerInputEvent down = new PointerInputEvent(
|
||||
pointer: 5,
|
||||
type: 'pointerdown',
|
||||
x: 10.0,
|
||||
y: 10.0
|
||||
);
|
||||
|
||||
tap.addPointer(down);
|
||||
GestureArena.instance.close(5);
|
||||
tap.addPointer(down1);
|
||||
GestureArena.instance.close(1);
|
||||
expect(tapRecognized, isFalse);
|
||||
router.route(down);
|
||||
router.route(down1);
|
||||
expect(tapRecognized, isFalse);
|
||||
|
||||
PointerInputEvent up = new PointerInputEvent(
|
||||
pointer: 5,
|
||||
type: 'pointerup',
|
||||
x: 11.0,
|
||||
y: 9.0
|
||||
);
|
||||
|
||||
router.route(up);
|
||||
router.route(up1);
|
||||
expect(tapRecognized, isTrue);
|
||||
GestureArena.instance.sweep(1);
|
||||
expect(tapRecognized, isTrue);
|
||||
|
||||
tap.dispose();
|
||||
});
|
||||
|
||||
test('Should recognize two overlapping taps', () {
|
||||
PointerRouter router = new PointerRouter();
|
||||
TapGestureRecognizer tap = new TapGestureRecognizer(router: router);
|
||||
|
||||
int tapsRecognized = 0;
|
||||
tap.onTap = () {
|
||||
tapsRecognized++;
|
||||
};
|
||||
|
||||
tap.addPointer(down1);
|
||||
GestureArena.instance.close(1);
|
||||
expect(tapsRecognized, 0);
|
||||
router.route(down1);
|
||||
expect(tapsRecognized, 0);
|
||||
|
||||
tap.addPointer(down2);
|
||||
GestureArena.instance.close(2);
|
||||
expect(tapsRecognized, 0);
|
||||
router.route(down1);
|
||||
expect(tapsRecognized, 0);
|
||||
|
||||
|
||||
router.route(up1);
|
||||
expect(tapsRecognized, 1);
|
||||
GestureArena.instance.sweep(1);
|
||||
expect(tapsRecognized, 1);
|
||||
|
||||
router.route(up2);
|
||||
expect(tapsRecognized, 2);
|
||||
GestureArena.instance.sweep(2);
|
||||
expect(tapsRecognized, 2);
|
||||
|
||||
tap.dispose();
|
||||
});
|
||||
|
||||
test('Distance cancels tap', () {
|
||||
PointerRouter router = new PointerRouter();
|
||||
TapGestureRecognizer tap = new TapGestureRecognizer(router: router);
|
||||
|
||||
bool tapRecognized = false;
|
||||
tap.onTap = () {
|
||||
tapRecognized = true;
|
||||
};
|
||||
|
||||
tap.addPointer(down3);
|
||||
GestureArena.instance.close(3);
|
||||
expect(tapRecognized, isFalse);
|
||||
router.route(down3);
|
||||
expect(tapRecognized, isFalse);
|
||||
|
||||
router.route(move3);
|
||||
expect(tapRecognized, isFalse);
|
||||
router.route(up3);
|
||||
expect(tapRecognized, isFalse);
|
||||
GestureArena.instance.sweep(3);
|
||||
expect(tapRecognized, isFalse);
|
||||
|
||||
tap.dispose();
|
||||
});
|
||||
|
||||
test('Timeout cancels tap', () {
|
||||
PointerRouter router = new PointerRouter();
|
||||
TapGestureRecognizer tap = new TapGestureRecognizer(router: router);
|
||||
|
||||
bool tapRecognized = false;
|
||||
tap.onTap = () {
|
||||
tapRecognized = true;
|
||||
};
|
||||
|
||||
new FakeAsync().run((FakeAsync async) {
|
||||
tap.addPointer(down1);
|
||||
GestureArena.instance.close(1);
|
||||
expect(tapRecognized, isFalse);
|
||||
router.route(down1);
|
||||
expect(tapRecognized, isFalse);
|
||||
|
||||
async.elapse(new Duration(milliseconds: 500));
|
||||
expect(tapRecognized, isFalse);
|
||||
router.route(up1);
|
||||
expect(tapRecognized, isFalse);
|
||||
GestureArena.instance.sweep(1);
|
||||
expect(tapRecognized, isFalse);
|
||||
});
|
||||
|
||||
tap.dispose();
|
||||
});
|
||||
|
||||
test('Should yield to other arena members', () {
|
||||
PointerRouter router = new PointerRouter();
|
||||
TapGestureRecognizer tap = new TapGestureRecognizer(router: router);
|
||||
|
||||
bool tapRecognized = false;
|
||||
tap.onTap = () {
|
||||
tapRecognized = true;
|
||||
};
|
||||
|
||||
tap.addPointer(down1);
|
||||
TestGestureArenaMember member = new TestGestureArenaMember();
|
||||
GestureArenaEntry entry = GestureArena.instance.add(1, member);
|
||||
GestureArena.instance.hold(1);
|
||||
GestureArena.instance.close(1);
|
||||
expect(tapRecognized, isFalse);
|
||||
router.route(down1);
|
||||
expect(tapRecognized, isFalse);
|
||||
|
||||
router.route(up1);
|
||||
expect(tapRecognized, isFalse);
|
||||
GestureArena.instance.sweep(1);
|
||||
expect(tapRecognized, isFalse);
|
||||
|
||||
entry.resolve(GestureDisposition.accepted);
|
||||
expect(tapRecognized, isFalse);
|
||||
|
||||
tap.dispose();
|
||||
});
|
||||
|
||||
test('Should trigger on release of held arena', () {
|
||||
PointerRouter router = new PointerRouter();
|
||||
TapGestureRecognizer tap = new TapGestureRecognizer(router: router);
|
||||
|
||||
bool tapRecognized = false;
|
||||
tap.onTap = () {
|
||||
tapRecognized = true;
|
||||
};
|
||||
|
||||
tap.addPointer(down1);
|
||||
TestGestureArenaMember member = new TestGestureArenaMember();
|
||||
GestureArenaEntry entry = GestureArena.instance.add(1, member);
|
||||
GestureArena.instance.hold(1);
|
||||
GestureArena.instance.close(1);
|
||||
expect(tapRecognized, isFalse);
|
||||
router.route(down1);
|
||||
expect(tapRecognized, isFalse);
|
||||
|
||||
router.route(up1);
|
||||
expect(tapRecognized, isFalse);
|
||||
GestureArena.instance.sweep(1);
|
||||
expect(tapRecognized, isFalse);
|
||||
|
||||
entry.resolve(GestureDisposition.rejected);
|
||||
expect(tapRecognized, isTrue);
|
||||
|
||||
tap.dispose();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user