Index: client/touch/TouchHandler.dart |
=================================================================== |
--- client/touch/TouchHandler.dart (revision 4144) |
+++ client/touch/TouchHandler.dart (working copy) |
@@ -1,393 +0,0 @@ |
-// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE file. |
- |
-/** |
- * Touch Handler. Class that handles all touch events and |
- * uses them to interpret higher level gestures and behaviors. TouchEvent is a |
- * built in mobile safari type: |
- * [http://developer.apple.com/safari/library/documentation/UserExperience/Reference/TouchEventClassReference/TouchEvent/TouchEvent.html]. |
- * |
- * Examples of higher level gestures this class is intended to support |
- * - click, double click, long click |
- * - dragging, swiping, zooming |
- * |
- * Touch Behavior: |
- * Use this class to make your elements 'touchable' (see Touchable.dart). |
- * Intended to work with all webkit browsers. |
- * |
- * Drag Behavior: |
- * Use this class to make your elements 'draggable' (see draggable.js). |
- * This behavior will handle all of the required events and report the |
- * properties of the drag to you while the touch is happening and at the |
- * end of the drag sequence. This behavior will NOT perform the actual |
- * dragging (redrawing the element) for you, this responsibility is left to |
- * the client code. This behavior contains a work around for a mobile |
- * safari bug where the 'touchend' event is not dispatched when the touch |
- * goes past the bottom of the browser window. |
- * This is intended to work well in iframes. |
- * Intended to work with all webkit browsers, tested only on iPhone 3.x so |
- * far. |
- * |
- * Click Behavior: |
- * Not yet implemented. |
- * |
- * Zoom Behavior: |
- * Not yet implemented. |
- * |
- * Swipe Behavior: |
- * Not yet implemented. |
- */ |
-class TouchHandler { |
- Touchable _touchable; |
- Element _element; |
- |
- /** The absolute sum of all touch y deltas. */ |
- int _totalMoveY; |
- |
- /** The absolute sum of all touch x deltas. */ |
- int _totalMoveX; |
- |
- /** |
- * A list of tuples where the first item is the horizontal component of a |
- * recent relevant touch and the second item is the touch's time stamp. Old |
- * touches are removed based on the max tracking time and when direction |
- * changes. |
- */ |
- List<int> _recentTouchesX; |
- |
- /** |
- * A list of tuples where the first item is the vertical component of a |
- * recent relevant touch and the second item is the touch's time stamp. Old |
- * touches are removed based on the max tracking time and when direction |
- * changes. |
- */ |
- List<int> _recentTouchesY; |
- |
- // TODO(jacobr): make customizable by passing optional parameters to the |
- // TouchHandler constructor. |
- /** |
- * Minimum movement of touch required to be considered a drag. |
- */ |
- static final _MIN_TRACKING_FOR_DRAG = 2; |
- |
- /** |
- * The maximum number of ms to track a touch event. After an event is older |
- * than this value, it will be ignored in velocity calculations. |
- */ |
- static final _MAX_TRACKING_TIME = 250; |
- |
- /** The maximum number of touches to track. */ |
- static final _MAX_TRACKING_TOUCHES = 5; |
- |
- /** |
- * The maximum velocity to return, in pixels per millisecond, that is used to |
- * guard against errors in calculating end velocity of a drag. This is a very |
- * fast drag velocity. |
- */ |
- static final _MAXIMUM_VELOCITY = 5; |
- |
- /** |
- * The velocity to return, in pixel per millisecond, when the time stamps on |
- * the events are erroneous. The browser can return bad time stamps if the |
- * thread is blocked for the duration of the drag. This is a low velocity to |
- * prevent the content from moving quickly after a slow drag. It is less |
- * jarring if the content moves slowly after a fast drag. |
- */ |
- static final _VELOCITY_FOR_INCORRECT_EVENTS = 1; |
- |
- Draggable _draggable; |
- bool _tracking; |
- bool _dragging; |
- bool _touching; |
- int _startTouchX; |
- int _startTouchY; |
- int _startTime; |
- TouchEvent _lastEvent; |
- int _lastTouchX; |
- int _lastTouchY; |
- int _lastMoveX; |
- int _lastMoveY; |
- int _endTime; |
- int _endTouchX; |
- int _endTouchY; |
- |
- TouchHandler(Touchable touchable, [Element element = null]) |
- : _touchable = touchable, |
- _totalMoveY = 0, |
- _totalMoveX = 0, |
- _recentTouchesX = new List<int>(), |
- _recentTouchesY = new List<int>(), |
- // TODO(jmesserly): I don't like having to initialize all booleans here |
- // See b/5045736 |
- _dragging = false, |
- _tracking = false, |
- _touching = false { |
- _element = element != null ? element : touchable.getElement(); |
- } |
- |
- /** |
- * Begin tracking the touchable element, it is eligible for dragging. |
- */ |
- void _beginTracking() { |
- _tracking = true; |
- } |
- |
- /** |
- * Stop tracking the touchable element, it is no longer dragging. |
- */ |
- void _endTracking() { |
- _tracking = false; |
- _dragging = false; |
- _totalMoveY = 0; |
- _totalMoveX = 0; |
- } |
- |
- /** |
- * Correct erroneous velocities by capping the velocity if we think it's too |
- * high, or setting it to a default velocity if know that the event data is |
- * bad. Returns the corrected velocity. |
- */ |
- num _correctVelocity(num velocity) { |
- num absVelocity = velocity.abs(); |
- if (absVelocity > _MAXIMUM_VELOCITY) { |
- absVelocity = _recentTouchesY.length < 6 ? |
- _VELOCITY_FOR_INCORRECT_EVENTS : _MAXIMUM_VELOCITY; |
- } |
- return absVelocity * (velocity < 0 ? -1 : 1); |
- } |
- |
- /** |
- * Start listenting for events. |
- * If [capture] is True the TouchHandler should listen during the capture |
- * phase. |
- */ |
- void enable([bool capture = false]) { |
- Function onEnd = (e) { _onEnd(e.timeStamp, e); }; |
- _addEventListeners( |
- _element, |
- (e) { _onStart(e); }, |
- (e) { _onMove(e); }, onEnd, onEnd, capture); |
- } |
- |
- /** |
- * Get the current horizontal drag delta. Drag delta is defined as the deltaX |
- * of the start touch position and the last touch position. |
- */ |
- int getDragDeltaX() { |
- return _lastTouchX - _startTouchX; |
- } |
- |
- /** |
- * Get the current vertical drag delta. Drag delta is defined as the deltaY of |
- * the start touch position and the last touch position. |
- */ |
- int getDragDeltaY() { |
- return _lastTouchY - _startTouchY; |
- } |
- |
- /** |
- * Get end velocity of the drag. This method is specific to drag behavior, so |
- * if touch behavior and drag behavior is split then this should go with drag |
- * behavior. End velocity is defined as deltaXY / deltaTime where deltaXY is |
- * the difference between endPosition and the oldest recent position, and |
- * deltaTime is the difference between endTime and the oldest recent time |
- * stamp. |
- */ |
- Coordinate getEndVelocity() { |
- num velocityX = 0; |
- num velocityY = 0; |
- |
- if (_recentTouchesX.length > 0) { |
- num timeDeltaX = Math.max(1, _endTime - _recentTouchesX[1]); |
- velocityX = (_endTouchX - _recentTouchesX[0]) / timeDeltaX; |
- } |
- |
- if (_recentTouchesY.length > 0) { |
- num timeDeltaY = Math.max(1, _endTime - _recentTouchesY[1]); |
- velocityY = (_endTouchY - _recentTouchesY[0]) / timeDeltaY; |
- } |
- velocityX = _correctVelocity(velocityX); |
- velocityY = _correctVelocity(velocityY); |
- return new Coordinate(velocityX, velocityY); |
- } |
- |
- /** |
- * Return the touch of the last event. |
- */ |
- Touch _getLastTouch() { |
- assert (_lastEvent != null); // Last event not set |
- return _lastEvent.touches[0]; |
- } |
- |
- /** |
- * Is the touch manager currently tracking touch moves to detect a drag? |
- */ |
- bool isTracking() { |
- return _tracking; |
- } |
- |
- /** |
- * Touch end handler. |
- */ |
- void _onEnd(int timeStamp, [TouchEvent e = null]) { |
- _touching = false; |
- _touchable.onTouchEnd(); |
- if (!_tracking || _draggable === null) { |
- return; |
- } |
- Touch touch = _getLastTouch(); |
- int clientX = touch.clientX; |
- int clientY = touch.clientY; |
- if (_dragging) { |
- _endTime = timeStamp; |
- _endTouchX = clientX; |
- _endTouchY = clientY; |
- _recentTouchesX = _removeOldTouches(_recentTouchesX, timeStamp); |
- _recentTouchesY = _removeOldTouches(_recentTouchesY, timeStamp); |
- _draggable.onDragEnd(); |
- if (e !== null) { |
- e.preventDefault(); |
- } |
- ClickBuster.preventGhostClick(_startTouchX, _startTouchY); |
- } |
- _endTracking(); |
- } |
- |
- /** |
- * Touch move handler. |
- */ |
- void _onMove(TouchEvent e) { |
- if (!_tracking || _draggable === null) { |
- return; |
- } |
- final touch = e.touches[0]; |
- int clientX = touch.clientX; |
- int clientY = touch.clientY; |
- int moveX = _lastTouchX - clientX; |
- int moveY = _lastTouchY - clientY; |
- _totalMoveX += moveX.abs(); |
- _totalMoveY += moveY.abs(); |
- _lastTouchX = clientX; |
- _lastTouchY = clientY; |
- if (!_dragging && |
- ((_totalMoveY > _MIN_TRACKING_FOR_DRAG && _draggable.verticalEnabled) || |
- (_totalMoveX > _MIN_TRACKING_FOR_DRAG && |
- _draggable.horizontalEnabled))) { |
- _dragging = _draggable.onDragStart(e); |
- if (!_dragging) { |
- _endTracking(); |
- } else { |
- _startTouchX = clientX; |
- _startTouchY = clientY; |
- _startTime = e.timeStamp; |
- } |
- } |
- if (_dragging) { |
- _draggable.onDragMove(); |
- _lastEvent = e; |
- e.preventDefault(); |
- _recentTouchesX = |
- _removeTouchesInWrongDirection(_recentTouchesX, _lastMoveX, moveX); |
- _recentTouchesY = |
- _removeTouchesInWrongDirection(_recentTouchesY, _lastMoveY, moveY); |
- _recentTouchesX = _removeOldTouches(_recentTouchesX, e.timeStamp); |
- _recentTouchesY = _removeOldTouches(_recentTouchesY, e.timeStamp); |
- _recentTouchesX.add(clientX); |
- _recentTouchesX.add(e.timeStamp); |
- _recentTouchesY.add(clientY); |
- _recentTouchesY.add(e.timeStamp); |
- } |
- _lastMoveX = moveX; |
- _lastMoveY = moveY; |
- } |
- |
- /** |
- * Touch start handler. |
- */ |
- void _onStart(TouchEvent e) { |
- if (_touching) { |
- return; |
- } |
- _touching = true; |
- if (!_touchable.onTouchStart(e) || _draggable === null) { |
- return; |
- } |
- final touch = e.touches[0]; |
- _startTouchX = _lastTouchX = touch.clientX; |
- _startTouchY = _lastTouchY = touch.clientY; |
- _startTime = e.timeStamp; |
- // TODO(jacobr): why don't we just clear the lists? |
- _recentTouchesX = new List<int>(); |
- _recentTouchesY = new List<int>(); |
- _recentTouchesX.add(touch.clientX); |
- _recentTouchesX.add(e.timeStamp); |
- _recentTouchesY.add(touch.clientY); |
- _recentTouchesY.add(e.timeStamp); |
- _lastEvent = e; |
- _beginTracking(); |
- } |
- |
- /** |
- * Filters the provided recent touches list to remove all touches older than |
- * the max tracking time or the 5th most recent touch. |
- * [recentTouches] specifies a list of tuples where the first item is the x |
- * or y component of the recent touch and the second item is the touch time |
- * stamp. The time of the most recent event is specified by [recentTime]. |
- */ |
- List<int> _removeOldTouches(List<int> recentTouches, |
- int recentTime) { |
- int count = 0; |
- final len = recentTouches.length; |
- assert (len % 2 == 0); |
- while (count < len && |
- recentTime - recentTouches[count + 1] > _MAX_TRACKING_TIME || |
- (len - count) > _MAX_TRACKING_TOUCHES * 2) { |
- count += 2; |
- } |
- return count == 0 ? recentTouches : _removeFirstN(recentTouches, count); |
- } |
- |
- static List<int> _removeFirstN(List<int> list, int n) { |
- return list.getRange(n, list.length - n); |
- } |
- |
- /** |
- * Filters the provided recent touches list to remove all touches except the |
- * last if the move direction has changed. |
- * [recentTouches] specifies a list of tuples where the first item is the x |
- * or y component of the recent touch and the second item is the touch time |
- * stamp. The x or y component of the most recent move is specified by |
- * [recentMove]. |
- */ |
- List<int> _removeTouchesInWrongDirection(List<int> recentTouches, |
- int lastMove, int recentMove) { |
- if (lastMove !=0 && recentMove != 0 && recentTouches.length > 2 && |
- _xor(lastMove > 0, recentMove > 0)) { |
- return _removeFirstN(recentTouches, recentTouches.length - 2); |
- } |
- return recentTouches; |
- } |
- |
- // TODO(jacobr): why doesn't bool implement the xor operator directly? |
- static bool _xor(bool a, bool b) { |
- return (a === true || b === true) && !(a === true && b === true); |
- } |
- |
- /** |
- * Reset the touchable element. |
- */ |
- void reset() { |
- _endTracking(); |
- _touching = false; |
- } |
- |
- /** |
- * Call this method to enable drag behavior on a draggable delegate. |
- * The [draggable] object can be the same as the [_touchable] object, they are |
- * assigned to different members to allow for strong typing with interfaces. |
- */ |
- void setDraggable(Draggable draggable) { |
- _draggable = draggable; |
- } |
-} |