mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Restore previous tap behaviors: no timeout, one pointer
Don't use a timeout to cancel tap tracking. Track only one primary pointer and ignore non-primary pointers. Update tests to reflect desired behaviors. Fixes #1779, #1780, #1781.
This commit is contained in:
parent
310ce31891
commit
f2fd5e32a5
@ -6,7 +6,7 @@
|
||||
// https://github.com/android/platform_frameworks_base/blob/master/core/java/android/view/ViewConfiguration.java
|
||||
|
||||
const Duration kLongPressTimeout = const Duration(milliseconds: 500);
|
||||
const Duration kTapTimeout = const Duration(milliseconds: 100);
|
||||
const Duration kPressTimeout = const Duration(milliseconds: 100);
|
||||
const Duration kJumpTapTimeout = const Duration(milliseconds: 500);
|
||||
const Duration kDoubleTapTimeout = const Duration(milliseconds: 300);
|
||||
const Duration kDoubleTapMinTime = const Duration(milliseconds: 40);
|
||||
|
||||
@ -52,7 +52,6 @@ class DoubleTapGestureRecognizer extends DisposableArenaMember {
|
||||
entry: GestureArena.instance.add(event.pointer, this)
|
||||
);
|
||||
_trackers[event.pointer] = tracker;
|
||||
tracker.startTimer(() => _reject(tracker));
|
||||
tracker.startTrackingPointer(router, handleEvent);
|
||||
}
|
||||
|
||||
@ -145,7 +144,6 @@ class DoubleTapGestureRecognizer extends DisposableArenaMember {
|
||||
}
|
||||
|
||||
void _freezeTracker(TapTracker tracker) {
|
||||
tracker.stopTimer();
|
||||
tracker.stopTrackingPointer(router, handleEvent);
|
||||
}
|
||||
|
||||
|
||||
@ -12,7 +12,7 @@ typedef void GestureLongPressCallback();
|
||||
|
||||
class LongPressGestureRecognizer extends PrimaryPointerGestureRecognizer {
|
||||
LongPressGestureRecognizer({ PointerRouter router, this.onLongPress })
|
||||
: super(router: router, deadline: kTapTimeout + kLongPressTimeout);
|
||||
: super(router: router, deadline: kLongPressTimeout);
|
||||
|
||||
GestureLongPressCallback onLongPress;
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ typedef void GestureShowPressCallback();
|
||||
|
||||
class ShowPressGestureRecognizer extends PrimaryPointerGestureRecognizer {
|
||||
ShowPressGestureRecognizer({ PointerRouter router, this.onShowPress })
|
||||
: super(router: router, deadline: kTapTimeout);
|
||||
: super(router: router, deadline: kPressTimeout);
|
||||
|
||||
GestureShowPressCallback onShowPress;
|
||||
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
// 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';
|
||||
@ -13,9 +12,56 @@ import 'recognizer.dart';
|
||||
|
||||
typedef void GestureTapCallback();
|
||||
|
||||
enum TapResolution {
|
||||
tap,
|
||||
cancel
|
||||
/// TapGestureRecognizer is a tap recognizer that tracks only one primary
|
||||
/// pointer per gesture. That is, during tap recognition, extra pointer events
|
||||
/// are ignored: down-1, down-2, up-1, up-2 produces only one tap on up-1.
|
||||
class TapGestureRecognizer extends PrimaryPointerGestureRecognizer {
|
||||
TapGestureRecognizer({ PointerRouter router, this.onTap })
|
||||
: super(router: router);
|
||||
|
||||
GestureTapCallback onTap;
|
||||
GestureTapCallback onTapDown;
|
||||
GestureTapCallback onTapCancel;
|
||||
|
||||
bool _wonArena = false;
|
||||
bool _didTap = false;
|
||||
|
||||
void handlePrimaryPointer(PointerInputEvent event) {
|
||||
if (event.type == 'pointerdown') {
|
||||
if (onTapDown != null)
|
||||
onTapDown();
|
||||
} else if (event.type == 'pointerup') {
|
||||
_didTap = true;
|
||||
_check();
|
||||
}
|
||||
}
|
||||
|
||||
void acceptGesture(int pointer) {
|
||||
super.acceptGesture(pointer);
|
||||
if (pointer == primaryPointer) {
|
||||
_wonArena = true;
|
||||
_check();
|
||||
}
|
||||
}
|
||||
|
||||
void rejectGesture(int pointer) {
|
||||
super.rejectGesture(pointer);
|
||||
if (pointer == primaryPointer) {
|
||||
assert(state == GestureRecognizerState.defunct);
|
||||
_wonArena = false;
|
||||
_didTap = false;
|
||||
if (onTapCancel != null)
|
||||
onTapCancel();
|
||||
}
|
||||
}
|
||||
|
||||
void _check() {
|
||||
if (_wonArena && _didTap) {
|
||||
resolve(GestureDisposition.accepted);
|
||||
if (onTap != null)
|
||||
onTap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// TapTracker helps track individual tap sequences as part of a
|
||||
@ -33,18 +79,6 @@ class TapTracker {
|
||||
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) {
|
||||
@ -67,6 +101,11 @@ class TapTracker {
|
||||
|
||||
}
|
||||
|
||||
enum TapResolution {
|
||||
tap,
|
||||
cancel
|
||||
}
|
||||
|
||||
/// 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.
|
||||
@ -77,11 +116,10 @@ class TapGesture extends TapTracker {
|
||||
entry = GestureArena.instance.add(event.pointer, gestureRecognizer);
|
||||
_wonArena = false;
|
||||
_didTap = false;
|
||||
startTimer(cancel);
|
||||
startTrackingPointer(gestureRecognizer.router, handleEvent);
|
||||
}
|
||||
|
||||
TapGestureRecognizer gestureRecognizer;
|
||||
MultiTapGestureRecognizer gestureRecognizer;
|
||||
|
||||
bool _wonArena;
|
||||
bool _didTap;
|
||||
@ -93,7 +131,6 @@ class TapGesture extends TapTracker {
|
||||
} else if (event.type == 'pointercancel') {
|
||||
cancel();
|
||||
} else if (event.type == 'pointerup') {
|
||||
stopTimer();
|
||||
stopTrackingPointer(gestureRecognizer.router, handleEvent);
|
||||
_didTap = true;
|
||||
_check();
|
||||
@ -106,7 +143,6 @@ class TapGesture extends TapTracker {
|
||||
}
|
||||
|
||||
void reject() {
|
||||
stopTimer();
|
||||
stopTrackingPointer(gestureRecognizer.router, handleEvent);
|
||||
gestureRecognizer._resolveTap(pointer, TapResolution.cancel);
|
||||
}
|
||||
@ -127,8 +163,12 @@ class TapGesture extends TapTracker {
|
||||
|
||||
}
|
||||
|
||||
class TapGestureRecognizer extends DisposableArenaMember {
|
||||
TapGestureRecognizer({ this.router, this.onTap, this.onTapDown, this.onTapCancel });
|
||||
/// MultiTapGestureRecognizer is a tap recognizer that treats taps
|
||||
/// independently. That is, each pointer sequence that could resolve to a tap
|
||||
/// does so independently of others: down-1, down-2, up-1, up-2 produces two
|
||||
/// taps, on up-1 and up-2.
|
||||
class MultiTapGestureRecognizer extends DisposableArenaMember {
|
||||
MultiTapGestureRecognizer({ this.router, this.onTap, this.onTapDown, this.onTapCancel });
|
||||
|
||||
PointerRouter router;
|
||||
GestureTapCallback onTap;
|
||||
|
||||
@ -35,8 +35,8 @@ class _InkSplash {
|
||||
duration: new Duration(milliseconds: (_targetRadius / _kSplashUnconfirmedVelocity).floor())
|
||||
)..addListener(_handleRadiusChange);
|
||||
|
||||
// Wait kTapTimeout to avoid creating tiny splashes during scrolls.
|
||||
_startTimer = new Timer(kTapTimeout, _play);
|
||||
// Wait kPressTimeout to avoid creating tiny splashes during scrolls.
|
||||
_startTimer = new Timer(kPressTimeout, _play);
|
||||
}
|
||||
|
||||
final Point position;
|
||||
|
||||
@ -223,7 +223,7 @@ void main() {
|
||||
tap.dispose();
|
||||
});
|
||||
|
||||
test('Intra-tap delay cancels double tap', () {
|
||||
test('Intra-tap delay does not cancel double tap', () {
|
||||
PointerRouter router = new PointerRouter();
|
||||
DoubleTapGestureRecognizer tap = new DoubleTapGestureRecognizer(router: router);
|
||||
|
||||
@ -252,9 +252,9 @@ void main() {
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
|
||||
router.route(up2);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
expect(doubleTapRecognized, isTrue);
|
||||
GestureArena.instance.sweep(2);
|
||||
expect(doubleTapRecognized, isFalse);
|
||||
expect(doubleTapRecognized, isTrue);
|
||||
});
|
||||
|
||||
tap.dispose();
|
||||
|
||||
@ -84,7 +84,7 @@ void main() {
|
||||
tap.dispose();
|
||||
});
|
||||
|
||||
test('Should recognize two overlapping taps', () {
|
||||
test('Should not recognize two overlapping taps', () {
|
||||
PointerRouter router = new PointerRouter();
|
||||
TapGestureRecognizer tap = new TapGestureRecognizer(router: router);
|
||||
|
||||
@ -112,9 +112,9 @@ void main() {
|
||||
expect(tapsRecognized, 1);
|
||||
|
||||
router.route(up2);
|
||||
expect(tapsRecognized, 2);
|
||||
expect(tapsRecognized, 1);
|
||||
GestureArena.instance.sweep(2);
|
||||
expect(tapsRecognized, 2);
|
||||
expect(tapsRecognized, 1);
|
||||
|
||||
tap.dispose();
|
||||
});
|
||||
@ -144,7 +144,7 @@ void main() {
|
||||
tap.dispose();
|
||||
});
|
||||
|
||||
test('Timeout cancels tap', () {
|
||||
test('Timeout does not cancel tap', () {
|
||||
PointerRouter router = new PointerRouter();
|
||||
TapGestureRecognizer tap = new TapGestureRecognizer(router: router);
|
||||
|
||||
@ -163,9 +163,9 @@ void main() {
|
||||
async.elapse(new Duration(milliseconds: 500));
|
||||
expect(tapRecognized, isFalse);
|
||||
router.route(up1);
|
||||
expect(tapRecognized, isFalse);
|
||||
expect(tapRecognized, isTrue);
|
||||
GestureArena.instance.sweep(1);
|
||||
expect(tapRecognized, isFalse);
|
||||
expect(tapRecognized, isTrue);
|
||||
});
|
||||
|
||||
tap.dispose();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user