diff --git a/packages/flutter/lib/gestures.dart b/packages/flutter/lib/gestures.dart index 138cf554ca..22d387ab51 100644 --- a/packages/flutter/lib/gestures.dart +++ b/packages/flutter/lib/gestures.dart @@ -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/long_press.dart'; export 'src/gestures/pointer_router.dart'; export 'src/gestures/recognizer.dart'; diff --git a/packages/flutter/lib/src/gestures/double_tap.dart b/packages/flutter/lib/src/gestures/double_tap.dart new file mode 100644 index 0000000000..f441239184 --- /dev/null +++ b/packages/flutter/lib/src/gestures/double_tap.dart @@ -0,0 +1,51 @@ +// 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 'dart:sky' as sky; + +import 'package:sky/src/gestures/arena.dart'; +import 'package:sky/src/gestures/constants.dart'; +import 'package:sky/src/gestures/recognizer.dart'; +import 'package:sky/src/gestures/tap.dart'; + +class DoubleTapGestureRecognizer extends PrimaryPointerGestureRecognizer { + DoubleTapGestureRecognizer({ PointerRouter router, this.onDoubleTap }) + : super(router: router, deadline: kTapTimeout); + + GestureTapListener onDoubleTap; + int _numTaps = 0; + Timer _longTimer; + + void resolve(GestureDisposition disposition) { + super.resolve(disposition); + if (disposition == GestureDisposition.rejected) { + _numTaps = 0; + } + } + + void didExceedDeadline() { + stopTrackingPointer(primaryPointer); + resolve(GestureDisposition.rejected); + } + + void didExceedLongDeadline() { + _numTaps = 0; + _longTimer = null; + } + + void handlePrimaryPointer(sky.PointerEvent event) { + if (event.type == 'pointerup') { + _numTaps++; + if (_numTaps == 1) { + _longTimer = new Timer(kDoubleTapTimeout, didExceedLongDeadline); + } else if (_numTaps == 2) { + resolve(GestureDisposition.accepted); + onDoubleTap(); + } + } + } + + +} diff --git a/packages/flutter/lib/src/gestures/recognizer.dart b/packages/flutter/lib/src/gestures/recognizer.dart index 4705e4c611..488b8e9c1a 100644 --- a/packages/flutter/lib/src/gestures/recognizer.dart +++ b/packages/flutter/lib/src/gestures/recognizer.dart @@ -99,10 +99,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); } diff --git a/packages/flutter/lib/src/gestures/tap.dart b/packages/flutter/lib/src/gestures/tap.dart index fe107b149d..69cb7bbd4a 100644 --- a/packages/flutter/lib/src/gestures/tap.dart +++ b/packages/flutter/lib/src/gestures/tap.dart @@ -5,16 +5,22 @@ import 'dart:sky' as sky; import 'package:sky/src/gestures/arena.dart'; +import 'package:sky/src/gestures/constants.dart'; import 'package:sky/src/gestures/recognizer.dart'; typedef void GestureTapListener(); class TapGestureRecognizer extends PrimaryPointerGestureRecognizer { TapGestureRecognizer({ PointerRouter router, this.onTap }) - : super(router: router); + : super(router: router, deadline: kTapTimeout); GestureTapListener onTap; + void didExceedDeadline() { + stopTrackingPointer(primaryPointer); + resolve(GestureDisposition.rejected); + } + void handlePrimaryPointer(sky.PointerEvent event) { if (event.type == 'pointerup') { resolve(GestureDisposition.accepted); diff --git a/packages/flutter/lib/src/widgets/gesture_detector.dart b/packages/flutter/lib/src/widgets/gesture_detector.dart index 88cacddbbf..991d612fb0 100644 --- a/packages/flutter/lib/src/widgets/gesture_detector.dart +++ b/packages/flutter/lib/src/widgets/gesture_detector.dart @@ -14,6 +14,7 @@ class GestureDetector extends StatefulComponent { Key key, this.child, this.onTap, + this.onDoubleTap, this.onShowPress, this.onLongPress, this.onVerticalDragStart, @@ -32,6 +33,7 @@ class GestureDetector extends StatefulComponent { final Widget child; final GestureTapListener onTap; + final GestureTapListener onDoubleTap; final GestureShowPressListener onShowPress; final GestureLongPressListener onLongPress; @@ -69,6 +71,13 @@ class GestureDetectorState extends State { return _tap; } + DoubleTapGestureRecognizer _doubleTap; + DoubleTapGestureRecognizer _ensureDoubleTap() { + if (_doubleTap == null) + _doubleTap = new DoubleTapGestureRecognizer(router: _router); + return _doubleTap; + } + ShowPressGestureRecognizer _showPress; ShowPressGestureRecognizer _ensureShowPress() { if (_showPress == null) @@ -115,6 +124,7 @@ class GestureDetectorState extends State { void dispose() { _tap = _ensureDisposed(_tap); + _doubleTap = _ensureDisposed(_doubleTap); _showPress = _ensureDisposed(_showPress); _longPress = _ensureDisposed(_longPress); _verticalDrag = _ensureDisposed(_verticalDrag); @@ -126,6 +136,7 @@ class GestureDetectorState extends State { void didUpdateConfig(GestureDetector oldConfig) { _syncTap(); + _syncDoubleTap(); _syncShowPress(); _syncLongPress(); _syncVerticalDrag(); @@ -141,6 +152,13 @@ class GestureDetectorState extends State { _ensureTap().onTap = config.onTap; } + void _syncDoubleTap() { + if (config.onDoubleTap == null) + _doubleTap = _ensureDisposed(_doubleTap); + else + _ensureDoubleTap().onDoubleTap = config.onDoubleTap; + } + void _syncShowPress() { if (config.onShowPress == null) _showPress = _ensureDisposed(_showPress); @@ -207,6 +225,8 @@ class GestureDetectorState extends State { void _handlePointerDown(sky.PointerEvent event) { if (_tap != null) _tap.addPointer(event); + if (_doubleTap != null) + _doubleTap.addPointer(event); if (_showPress != null) _showPress.addPointer(event); if (_longPress != null)