Commit 3fc05b79 authored by Adam Barth's avatar Adam Barth

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.
parent 93622ebd
......@@ -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';
......
// 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.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;
}
}
}
......@@ -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<int, TapGesture> _gestureMap = new Map<int, TapGesture>();
Map<int, _TapGesture> _gestureMap = new Map<int, _TapGesture>();
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<TapGesture> localGestures = new List<TapGesture>.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<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.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;
}
}
}
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