Commit a0c8a4c6 authored by Ian Hickson's avatar Ian Hickson

Merge pull request #1911 from Hixie/so-long-show-press

Replace ShowPress with TapDown
parents 6611bf62 8a4a36d0
...@@ -14,6 +14,5 @@ export 'src/gestures/lsq_solver.dart'; ...@@ -14,6 +14,5 @@ export 'src/gestures/lsq_solver.dart';
export 'src/gestures/pointer_router.dart'; export 'src/gestures/pointer_router.dart';
export 'src/gestures/recognizer.dart'; export 'src/gestures/recognizer.dart';
export 'src/gestures/scale.dart'; export 'src/gestures/scale.dart';
export 'src/gestures/show_press.dart';
export 'src/gestures/tap.dart'; export 'src/gestures/tap.dart';
export 'src/gestures/velocity_tracker.dart'; export 'src/gestures/velocity_tracker.dart';
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
// Modeled after Android's ViewConfiguration: // Modeled after Android's ViewConfiguration:
// https://github.com/android/platform_frameworks_base/blob/master/core/java/android/view/ViewConfiguration.java // https://github.com/android/platform_frameworks_base/blob/master/core/java/android/view/ViewConfiguration.java
const Duration kLongPressTimeout = const Duration(milliseconds: 500);
const Duration kPressTimeout = const Duration(milliseconds: 100); const Duration kPressTimeout = const Duration(milliseconds: 100);
const Duration kLongPressTimeout = const Duration(milliseconds: 500);
const Duration kJumpTapTimeout = const Duration(milliseconds: 500); const Duration kJumpTapTimeout = const Duration(milliseconds: 500);
const Duration kDoubleTapTimeout = const Duration(milliseconds: 300); const Duration kDoubleTapTimeout = const Duration(milliseconds: 300);
const Duration kDoubleTapMinTime = const Duration(milliseconds: 40); const Duration kDoubleTapMinTime = const Duration(milliseconds: 40);
......
// 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 'arena.dart';
import 'constants.dart';
import 'events.dart';
import 'recognizer.dart';
typedef void GestureShowPressCallback();
class ShowPressGestureRecognizer extends PrimaryPointerGestureRecognizer {
ShowPressGestureRecognizer({ PointerRouter router, this.onShowPress })
: super(router: router, deadline: kPressTimeout);
GestureShowPressCallback onShowPress;
void didExceedDeadline() {
// Show press isn't an exclusive gesture. We can recognize a show press
// as well as another gesture, like a long press.
resolve(GestureDisposition.rejected);
onShowPress();
}
void handlePrimaryPointer(PointerInputEvent event) {
if (event.type == 'pointerup')
resolve(GestureDisposition.rejected);
}
}
...@@ -26,31 +26,34 @@ class TapGestureRecognizer extends PrimaryPointerGestureRecognizer { ...@@ -26,31 +26,34 @@ class TapGestureRecognizer extends PrimaryPointerGestureRecognizer {
this.onTapUp, this.onTapUp,
this.onTap, this.onTap,
this.onTapCancel this.onTapCancel
}) : super(router: router); }) : super(router: router, deadline: kPressTimeout);
GestureTapDownCallback onTapDown; GestureTapDownCallback onTapDown;
GestureTapDownCallback onTapUp; GestureTapDownCallback onTapUp;
GestureTapCallback onTap; GestureTapCallback onTap;
GestureTapCancelCallback onTapCancel; GestureTapCancelCallback onTapCancel;
bool _sentTapDown = false;
bool _wonArena = false; bool _wonArena = false;
Point _finalPosition; Point _finalPosition;
void handlePrimaryPointer(PointerInputEvent event) { void handlePrimaryPointer(PointerInputEvent event) {
if (event.type == 'pointerdown') { if (event.type == 'pointerup') {
if (onTapDown != null)
onTapDown(event.position);
} else if (event.type == 'pointerup') {
_finalPosition = event.position; _finalPosition = event.position;
_check(); _checkUp();
} }
} }
void didExceedDeadline() {
_checkDown();
}
void acceptGesture(int pointer) { void acceptGesture(int pointer) {
super.acceptGesture(pointer); super.acceptGesture(pointer);
if (pointer == primaryPointer) { if (pointer == primaryPointer) {
_checkDown();
_wonArena = true; _wonArena = true;
_check(); _checkUp();
} }
} }
...@@ -64,7 +67,15 @@ class TapGestureRecognizer extends PrimaryPointerGestureRecognizer { ...@@ -64,7 +67,15 @@ class TapGestureRecognizer extends PrimaryPointerGestureRecognizer {
} }
} }
void _check() { void _checkDown() {
if (!_sentTapDown) {
if (onTapDown != null)
onTapDown(initialPosition);
_sentTapDown = true;
}
}
void _checkUp() {
if (_wonArena && _finalPosition != null) { if (_wonArena && _finalPosition != null) {
resolve(GestureDisposition.accepted); resolve(GestureDisposition.accepted);
if (onTapUp != null) if (onTapUp != null)
...@@ -76,6 +87,7 @@ class TapGestureRecognizer extends PrimaryPointerGestureRecognizer { ...@@ -76,6 +87,7 @@ class TapGestureRecognizer extends PrimaryPointerGestureRecognizer {
} }
void _reset() { void _reset() {
_sentTapDown = false;
_wonArena = false; _wonArena = false;
_finalPosition = null; _finalPosition = null;
} }
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async';
import 'dart:math' as math; import 'dart:math' as math;
import 'package:flutter/animation.dart'; import 'package:flutter/animation.dart';
...@@ -88,13 +87,8 @@ class _InkSplash { ...@@ -88,13 +87,8 @@ class _InkSplash {
curve: Curves.easeOut curve: Curves.easeOut
), ),
duration: new Duration(milliseconds: (_targetRadius / _kSplashUnconfirmedVelocity).floor()) duration: new Duration(milliseconds: (_targetRadius / _kSplashUnconfirmedVelocity).floor())
)..addListener(_handleRadiusChange); )..addListener(_handleRadiusChange)
..play();
// Wait kPressTimeout to avoid creating tiny splashes during scrolls.
// TODO(ianh): Instead of a timer in _InkSplash, we should start splashes from the gesture recognisers' onTapDown.
// ...and onTapDown should use a timer _or_ fire as soon as the tap is committed.
// When we do this, make sure it works even if we're only listening to onLongPress.
_startTimer = new Timer(kPressTimeout, _play);
} }
final Point position; final Point position;
...@@ -103,45 +97,26 @@ class _InkSplash { ...@@ -103,45 +97,26 @@ class _InkSplash {
double _targetRadius; double _targetRadius;
double _pinnedRadius; double _pinnedRadius;
ValuePerformance<double> _radius; ValuePerformance<double> _radius;
Timer _startTimer;
bool _cancelStartTimer() {
if (_startTimer != null) {
_startTimer.cancel();
_startTimer = null;
return true;
}
return false;
}
void _play() {
_cancelStartTimer();
_radius.play();
}
void _updateVelocity(double velocity) { void _updateVelocity(double velocity) {
int duration = (_targetRadius / velocity).floor(); int duration = (_targetRadius / velocity).floor();
_radius.duration = new Duration(milliseconds: duration); _radius.duration = new Duration(milliseconds: duration);
_play(); _radius.play();
} }
void confirm() { void confirm() {
if (_cancelStartTimer())
return;
_updateVelocity(_kSplashConfirmedVelocity); _updateVelocity(_kSplashConfirmedVelocity);
_pinnedRadius = null; _pinnedRadius = null;
} }
void cancel() { void cancel() {
if (_cancelStartTimer())
return;
_updateVelocity(_kSplashCanceledVelocity); _updateVelocity(_kSplashCanceledVelocity);
_pinnedRadius = _radius.value; _pinnedRadius = _radius.value;
} }
void _handleRadiusChange() { void _handleRadiusChange() {
if (_radius.value == _targetRadius) if (_radius.value == _targetRadius)
renderer._splashes.remove(this); renderer._removeSplash(this);
renderer.markNeedsPaint(); renderer.markNeedsPaint();
} }
...@@ -183,15 +158,21 @@ class _RenderInkSplashes extends RenderProxyBox { ...@@ -183,15 +158,21 @@ class _RenderInkSplashes extends RenderProxyBox {
_HighlightChangedCallback onHighlightChanged; _HighlightChangedCallback onHighlightChanged;
final List<_InkSplash> _splashes = new List<_InkSplash>(); final List<_InkSplash> _splashes = new List<_InkSplash>();
_InkSplash _lastSplash;
TapGestureRecognizer _tap; TapGestureRecognizer _tap;
LongPressGestureRecognizer _longPress; LongPressGestureRecognizer _longPress;
void _removeSplash(_InkSplash splash) {
_splashes.remove(splash);
if (_lastSplash == splash)
_lastSplash = null;
}
void handleEvent(InputEvent event, BoxHitTestEntry entry) { void handleEvent(InputEvent event, BoxHitTestEntry entry) {
if (event.type == 'pointerdown' && (_tap != null || _longPress != null)) { if (event.type == 'pointerdown' && (onTap != null || onLongPress != null)) {
_tap?.addPointer(event); _tap?.addPointer(event);
_longPress?.addPointer(event); _longPress?.addPointer(event);
_splashes.add(new _InkSplash(entry.localPosition, this));
} }
} }
...@@ -208,7 +189,7 @@ class _RenderInkSplashes extends RenderProxyBox { ...@@ -208,7 +189,7 @@ class _RenderInkSplashes extends RenderProxyBox {
} }
void _syncTapRecognizer() { void _syncTapRecognizer() {
if (onTap == null) { if (onTap == null && onLongPress == null) {
_disposeTapRecognizer(); _disposeTapRecognizer();
} else { } else {
_tap ??= new TapGestureRecognizer(router: FlutterBinding.instance.pointerRouter) _tap ??= new TapGestureRecognizer(router: FlutterBinding.instance.pointerRouter)
...@@ -237,31 +218,34 @@ class _RenderInkSplashes extends RenderProxyBox { ...@@ -237,31 +218,34 @@ class _RenderInkSplashes extends RenderProxyBox {
_longPress = null; _longPress = null;
} }
void _handleTapDown(_) { void _handleTapDown(Point position) {
_lastSplash = new _InkSplash(globalToLocal(position), this);
_splashes.add(_lastSplash);
if (onHighlightChanged != null) if (onHighlightChanged != null)
onHighlightChanged(true); onHighlightChanged(true);
} }
void _handleTap() { void _handleTap() {
if (_splashes.isNotEmpty) _lastSplash?.confirm();
_splashes.last.confirm(); _lastSplash = null;
if (onHighlightChanged != null) if (onHighlightChanged != null)
onHighlightChanged(false); onHighlightChanged(false);
if (onTap != null) if (onTap != null)
onTap(); onTap();
} }
void _handleTapCancel() { void _handleTapCancel() {
_splashes.last?.cancel(); _lastSplash?.cancel();
_lastSplash = null;
if (onHighlightChanged != null) if (onHighlightChanged != null)
onHighlightChanged(false); onHighlightChanged(false);
} }
void _handleLongPress() { void _handleLongPress() {
_splashes.last?.confirm(); _lastSplash?.confirm();
onLongPress(); _lastSplash = null;
if (onLongPress != null)
onLongPress();
} }
void paint(PaintingContext context, Offset offset) { void paint(PaintingContext context, Offset offset) {
......
...@@ -13,7 +13,6 @@ export 'package:flutter/gestures.dart' show ...@@ -13,7 +13,6 @@ export 'package:flutter/gestures.dart' show
GestureTapUpCallback, GestureTapUpCallback,
GestureTapCallback, GestureTapCallback,
GestureTapCancelCallback, GestureTapCancelCallback,
GestureShowPressCallback,
GestureLongPressCallback, GestureLongPressCallback,
GestureDragStartCallback, GestureDragStartCallback,
GestureDragUpdateCallback, GestureDragUpdateCallback,
...@@ -37,7 +36,6 @@ class GestureDetector extends StatefulComponent { ...@@ -37,7 +36,6 @@ class GestureDetector extends StatefulComponent {
this.onTap, this.onTap,
this.onTapCancel, this.onTapCancel,
this.onDoubleTap, this.onDoubleTap,
this.onShowPress,
this.onLongPress, this.onLongPress,
this.onVerticalDragStart, this.onVerticalDragStart,
this.onVerticalDragUpdate, this.onVerticalDragUpdate,
...@@ -61,7 +59,6 @@ class GestureDetector extends StatefulComponent { ...@@ -61,7 +59,6 @@ class GestureDetector extends StatefulComponent {
final GestureTapCancelCallback onTapCancel; final GestureTapCancelCallback onTapCancel;
final GestureTapCallback onDoubleTap; final GestureTapCallback onDoubleTap;
final GestureShowPressCallback onShowPress;
final GestureLongPressCallback onLongPress; final GestureLongPressCallback onLongPress;
final GestureDragStartCallback onVerticalDragStart; final GestureDragStartCallback onVerticalDragStart;
...@@ -88,7 +85,6 @@ class _GestureDetectorState extends State<GestureDetector> { ...@@ -88,7 +85,6 @@ class _GestureDetectorState extends State<GestureDetector> {
TapGestureRecognizer _tap; TapGestureRecognizer _tap;
DoubleTapGestureRecognizer _doubleTap; DoubleTapGestureRecognizer _doubleTap;
ShowPressGestureRecognizer _showPress;
LongPressGestureRecognizer _longPress; LongPressGestureRecognizer _longPress;
VerticalDragGestureRecognizer _verticalDrag; VerticalDragGestureRecognizer _verticalDrag;
HorizontalDragGestureRecognizer _horizontalDrag; HorizontalDragGestureRecognizer _horizontalDrag;
...@@ -107,7 +103,6 @@ class _GestureDetectorState extends State<GestureDetector> { ...@@ -107,7 +103,6 @@ class _GestureDetectorState extends State<GestureDetector> {
void dispose() { void dispose() {
_tap = _ensureDisposed(_tap); _tap = _ensureDisposed(_tap);
_doubleTap = _ensureDisposed(_doubleTap); _doubleTap = _ensureDisposed(_doubleTap);
_showPress = _ensureDisposed(_showPress);
_longPress = _ensureDisposed(_longPress); _longPress = _ensureDisposed(_longPress);
_verticalDrag = _ensureDisposed(_verticalDrag); _verticalDrag = _ensureDisposed(_verticalDrag);
_horizontalDrag = _ensureDisposed(_horizontalDrag); _horizontalDrag = _ensureDisposed(_horizontalDrag);
...@@ -119,7 +114,6 @@ class _GestureDetectorState extends State<GestureDetector> { ...@@ -119,7 +114,6 @@ class _GestureDetectorState extends State<GestureDetector> {
void _syncAll() { void _syncAll() {
_syncTap(); _syncTap();
_syncDoubleTap(); _syncDoubleTap();
_syncShowPress();
_syncLongPress(); _syncLongPress();
_syncVerticalDrag(); _syncVerticalDrag();
_syncHorizontalDrag(); _syncHorizontalDrag();
...@@ -149,15 +143,6 @@ class _GestureDetectorState extends State<GestureDetector> { ...@@ -149,15 +143,6 @@ class _GestureDetectorState extends State<GestureDetector> {
} }
} }
void _syncShowPress() {
if (config.onShowPress == null) {
_showPress = _ensureDisposed(_showPress);
} else {
_showPress ??= new ShowPressGestureRecognizer(router: _router);
_showPress.onShowPress = config.onShowPress;
}
}
void _syncLongPress() { void _syncLongPress() {
if (config.onLongPress == null) { if (config.onLongPress == null) {
_longPress = _ensureDisposed(_longPress); _longPress = _ensureDisposed(_longPress);
...@@ -227,8 +212,6 @@ class _GestureDetectorState extends State<GestureDetector> { ...@@ -227,8 +212,6 @@ class _GestureDetectorState extends State<GestureDetector> {
_tap.addPointer(event); _tap.addPointer(event);
if (_doubleTap != null) if (_doubleTap != null)
_doubleTap.addPointer(event); _doubleTap.addPointer(event);
if (_showPress != null)
_showPress.addPointer(event);
if (_longPress != null) if (_longPress != null)
_longPress.addPointer(event); _longPress.addPointer(event);
if (_verticalDrag != null) if (_verticalDrag != null)
...@@ -255,8 +238,6 @@ class _GestureDetectorState extends State<GestureDetector> { ...@@ -255,8 +238,6 @@ class _GestureDetectorState extends State<GestureDetector> {
gestures.add('tap'); gestures.add('tap');
if (_doubleTap != null) if (_doubleTap != null)
gestures.add('double tap'); gestures.add('double tap');
if (_showPress != null)
gestures.add('show press');
if (_longPress != null) if (_longPress != null)
gestures.add('long press'); gestures.add('long press');
if (_verticalDrag != null) if (_verticalDrag != null)
......
...@@ -67,14 +67,14 @@ void main() { ...@@ -67,14 +67,14 @@ void main() {
longPress.dispose(); longPress.dispose();
}); });
test('Should recognize both show press and long press', () { test('Should recognize both tap down and long press', () {
PointerRouter router = new PointerRouter(); PointerRouter router = new PointerRouter();
ShowPressGestureRecognizer showPress = new ShowPressGestureRecognizer(router: router); TapGestureRecognizer tap = new TapGestureRecognizer(router: router);
LongPressGestureRecognizer longPress = new LongPressGestureRecognizer(router: router); LongPressGestureRecognizer longPress = new LongPressGestureRecognizer(router: router);
bool showPressRecognized = false; bool tapDownRecognized = false;
showPress.onShowPress = () { tap.onTapDown = (_) {
showPressRecognized = true; tapDownRecognized = true;
}; };
bool longPressRecognized = false; bool longPressRecognized = false;
...@@ -83,23 +83,23 @@ void main() { ...@@ -83,23 +83,23 @@ void main() {
}; };
new FakeAsync().run((FakeAsync async) { new FakeAsync().run((FakeAsync async) {
showPress.addPointer(down); tap.addPointer(down);
longPress.addPointer(down); longPress.addPointer(down);
GestureArena.instance.close(5); GestureArena.instance.close(5);
expect(showPressRecognized, isFalse); expect(tapDownRecognized, isFalse);
expect(longPressRecognized, isFalse); expect(longPressRecognized, isFalse);
router.route(down); router.route(down);
expect(showPressRecognized, isFalse); expect(tapDownRecognized, isFalse);
expect(longPressRecognized, isFalse); expect(longPressRecognized, isFalse);
async.elapse(new Duration(milliseconds: 300)); async.elapse(new Duration(milliseconds: 300));
expect(showPressRecognized, isTrue); expect(tapDownRecognized, isTrue);
expect(longPressRecognized, isFalse); expect(longPressRecognized, isFalse);
async.elapse(new Duration(milliseconds: 700)); async.elapse(new Duration(milliseconds: 700));
expect(showPressRecognized, isTrue); expect(tapDownRecognized, isTrue);
expect(longPressRecognized, isTrue); expect(longPressRecognized, isTrue);
}); });
showPress.dispose(); tap.dispose();
longPress.dispose(); longPress.dispose();
}); });
} }
import 'package:quiver/testing/async.dart';
import 'package:flutter/gestures.dart';
import 'package:test/test.dart';
final PointerInputEvent down = new PointerInputEvent(
pointer: 5,
type: 'pointerdown',
x: 10.0,
y: 10.0
);
final PointerInputEvent up = new PointerInputEvent(
pointer: 5,
type: 'pointerup',
x: 11.0,
y: 9.0
);
void main() {
test('Should recognize show press', () {
PointerRouter router = new PointerRouter();
ShowPressGestureRecognizer showPress = new ShowPressGestureRecognizer(router: router);
bool showPressRecognized = false;
showPress.onShowPress = () {
showPressRecognized = true;
};
new FakeAsync().run((FakeAsync async) {
showPress.addPointer(down);
GestureArena.instance.close(5);
expect(showPressRecognized, isFalse);
router.route(down);
expect(showPressRecognized, isFalse);
async.elapse(new Duration(milliseconds: 300));
expect(showPressRecognized, isTrue);
});
showPress.dispose();
});
test('Up cancels show press', () {
PointerRouter router = new PointerRouter();
ShowPressGestureRecognizer showPress = new ShowPressGestureRecognizer(router: router);
bool showPressRecognized = false;
showPress.onShowPress = () {
showPressRecognized = true;
};
new FakeAsync().run((FakeAsync async) {
showPress.addPointer(down);
GestureArena.instance.close(5);
expect(showPressRecognized, isFalse);
router.route(down);
expect(showPressRecognized, isFalse);
async.elapse(new Duration(milliseconds: 50));
expect(showPressRecognized, isFalse);
router.route(up);
expect(showPressRecognized, isFalse);
async.elapse(new Duration(seconds: 1));
expect(showPressRecognized, isFalse);
});
showPress.dispose();
});
}
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