From 3fc05b79aa1bbac0b69ac52c2de3ea8d551e5c8f Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Tue, 27 Oct 2015 10:08:48 -0700 Subject: [PATCH] Make TapTracker and TapGesture private classes We don't want to expose these from the gesture package. This patch moves DoubleTapGestureRecognizer into tap.dart so we can make TapTracker and TapGesture private to that file. --- packages/flutter/lib/gestures.dart | 1 - .../flutter/lib/src/gestures/double_tap.dart | 161 ----------------- packages/flutter/lib/src/gestures/tap.dart | 167 +++++++++++++++++- 3 files changed, 159 insertions(+), 170 deletions(-) delete mode 100644 packages/flutter/lib/src/gestures/double_tap.dart diff --git a/packages/flutter/lib/gestures.dart b/packages/flutter/lib/gestures.dart index 8a3f7dfde3..f5432bc455 100644 --- a/packages/flutter/lib/gestures.dart +++ b/packages/flutter/lib/gestures.dart @@ -8,7 +8,6 @@ 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'; diff --git a/packages/flutter/lib/src/gestures/double_tap.dart b/packages/flutter/lib/src/gestures/double_tap.dart deleted file mode 100644 index 1ecaf68068..0000000000 --- a/packages/flutter/lib/src/gestures/double_tap.dart +++ /dev/null @@ -1,161 +0,0 @@ -// 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 _trackers = new Map(); - - 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.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 localTrackers = new List.from(_trackers.values); - for (TapTracker tracker in localTrackers) - _reject(tracker); - assert(_trackers.isEmpty); - } - - void _freezeTracker(TapTracker tracker) { - tracker.stopTrackingPointer(router, handleEvent); - } - - void _startDoubleTapTimer() { - _doubleTapTimer ??= new Timer(kDoubleTapTimeout, () => _reset()); - } - - void _stopDoubleTapTimer() { - if (_doubleTapTimer != null) { - _doubleTapTimer.cancel(); - _doubleTapTimer = null; - } - } - -} diff --git a/packages/flutter/lib/src/gestures/tap.dart b/packages/flutter/lib/src/gestures/tap.dart index 1ca0967761..56a8add85e 100644 --- a/packages/flutter/lib/src/gestures/tap.dart +++ b/packages/flutter/lib/src/gestures/tap.dart @@ -2,6 +2,7 @@ // 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'; @@ -66,9 +67,9 @@ class TapGestureRecognizer extends PrimaryPointerGestureRecognizer { /// TapTracker helps track individual tap sequences as part of a /// larger gesture. -class TapTracker { +class _TapTracker { - TapTracker({ PointerInputEvent event, this.entry }) + _TapTracker({ PointerInputEvent event, this.entry }) : pointer = event.pointer, _initialPosition = event.position, _isTrackingPointer = false { @@ -109,9 +110,9 @@ enum TapResolution { /// 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 { +class _TapGesture extends _TapTracker { - TapGesture({ this.gestureRecognizer, PointerInputEvent event }) + _TapGesture({ this.gestureRecognizer, PointerInputEvent event }) : super(event: event) { entry = GestureArena.instance.add(event.pointer, gestureRecognizer); _wonArena = false; @@ -175,11 +176,11 @@ class MultiTapGestureRecognizer extends DisposableArenaMember { GestureTapCallback onTapDown; GestureTapCallback onTapCancel; - Map _gestureMap = new Map(); + Map _gestureMap = new Map(); void addPointer(PointerInputEvent event) { assert(!_gestureMap.containsKey(event.pointer)); - _gestureMap[event.pointer] = new TapGesture( + _gestureMap[event.pointer] = new _TapGesture( gestureRecognizer: this, event: event ); @@ -209,8 +210,8 @@ class MultiTapGestureRecognizer extends DisposableArenaMember { } void dispose() { - List localGestures = new List.from(_gestureMap.values); - for (TapGesture gesture in localGestures) + 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); @@ -218,3 +219,153 @@ class MultiTapGestureRecognizer extends DisposableArenaMember { } } + +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 _trackers = new Map(); + + 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.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.stopTrackingPointer(router, handleEvent); + } + + void _startDoubleTapTimer() { + _doubleTapTimer ??= new Timer(kDoubleTapTimeout, () => _reset()); + } + + void _stopDoubleTapTimer() { + if (_doubleTapTimer != null) { + _doubleTapTimer.cancel(); + _doubleTapTimer = null; + } + } + +}