Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(573)

Side by Side Diff: client/touch/TouchHandler.dart

Issue 9382027: Move client/{base, observable, layout, touch, util, view} to samples/ui_lib . (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: '' Created 8 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « client/touch/TimeUtil.dart ('k') | client/touch/TouchUtil.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 /**
6 * Touch Handler. Class that handles all touch events and
7 * uses them to interpret higher level gestures and behaviors. TouchEvent is a
8 * built in mobile safari type:
9 * [http://developer.apple.com/safari/library/documentation/UserExperience/Refer ence/TouchEventClassReference/TouchEvent/TouchEvent.html].
10 *
11 * Examples of higher level gestures this class is intended to support
12 * - click, double click, long click
13 * - dragging, swiping, zooming
14 *
15 * Touch Behavior:
16 * Use this class to make your elements 'touchable' (see Touchable.dart).
17 * Intended to work with all webkit browsers.
18 *
19 * Drag Behavior:
20 * Use this class to make your elements 'draggable' (see draggable.js).
21 * This behavior will handle all of the required events and report the
22 * properties of the drag to you while the touch is happening and at the
23 * end of the drag sequence. This behavior will NOT perform the actual
24 * dragging (redrawing the element) for you, this responsibility is left to
25 * the client code. This behavior contains a work around for a mobile
26 * safari bug where the 'touchend' event is not dispatched when the touch
27 * goes past the bottom of the browser window.
28 * This is intended to work well in iframes.
29 * Intended to work with all webkit browsers, tested only on iPhone 3.x so
30 * far.
31 *
32 * Click Behavior:
33 * Not yet implemented.
34 *
35 * Zoom Behavior:
36 * Not yet implemented.
37 *
38 * Swipe Behavior:
39 * Not yet implemented.
40 */
41 class TouchHandler {
42 Touchable _touchable;
43 Element _element;
44
45 /** The absolute sum of all touch y deltas. */
46 int _totalMoveY;
47
48 /** The absolute sum of all touch x deltas. */
49 int _totalMoveX;
50
51 /**
52 * A list of tuples where the first item is the horizontal component of a
53 * recent relevant touch and the second item is the touch's time stamp. Old
54 * touches are removed based on the max tracking time and when direction
55 * changes.
56 */
57 List<int> _recentTouchesX;
58
59 /**
60 * A list of tuples where the first item is the vertical component of a
61 * recent relevant touch and the second item is the touch's time stamp. Old
62 * touches are removed based on the max tracking time and when direction
63 * changes.
64 */
65 List<int> _recentTouchesY;
66
67 // TODO(jacobr): make customizable by passing optional parameters to the
68 // TouchHandler constructor.
69 /**
70 * Minimum movement of touch required to be considered a drag.
71 */
72 static final _MIN_TRACKING_FOR_DRAG = 2;
73
74 /**
75 * The maximum number of ms to track a touch event. After an event is older
76 * than this value, it will be ignored in velocity calculations.
77 */
78 static final _MAX_TRACKING_TIME = 250;
79
80 /** The maximum number of touches to track. */
81 static final _MAX_TRACKING_TOUCHES = 5;
82
83 /**
84 * The maximum velocity to return, in pixels per millisecond, that is used to
85 * guard against errors in calculating end velocity of a drag. This is a very
86 * fast drag velocity.
87 */
88 static final _MAXIMUM_VELOCITY = 5;
89
90 /**
91 * The velocity to return, in pixel per millisecond, when the time stamps on
92 * the events are erroneous. The browser can return bad time stamps if the
93 * thread is blocked for the duration of the drag. This is a low velocity to
94 * prevent the content from moving quickly after a slow drag. It is less
95 * jarring if the content moves slowly after a fast drag.
96 */
97 static final _VELOCITY_FOR_INCORRECT_EVENTS = 1;
98
99 Draggable _draggable;
100 bool _tracking;
101 bool _dragging;
102 bool _touching;
103 int _startTouchX;
104 int _startTouchY;
105 int _startTime;
106 TouchEvent _lastEvent;
107 int _lastTouchX;
108 int _lastTouchY;
109 int _lastMoveX;
110 int _lastMoveY;
111 int _endTime;
112 int _endTouchX;
113 int _endTouchY;
114
115 TouchHandler(Touchable touchable, [Element element = null])
116 : _touchable = touchable,
117 _totalMoveY = 0,
118 _totalMoveX = 0,
119 _recentTouchesX = new List<int>(),
120 _recentTouchesY = new List<int>(),
121 // TODO(jmesserly): I don't like having to initialize all booleans here
122 // See b/5045736
123 _dragging = false,
124 _tracking = false,
125 _touching = false {
126 _element = element != null ? element : touchable.getElement();
127 }
128
129 /**
130 * Begin tracking the touchable element, it is eligible for dragging.
131 */
132 void _beginTracking() {
133 _tracking = true;
134 }
135
136 /**
137 * Stop tracking the touchable element, it is no longer dragging.
138 */
139 void _endTracking() {
140 _tracking = false;
141 _dragging = false;
142 _totalMoveY = 0;
143 _totalMoveX = 0;
144 }
145
146 /**
147 * Correct erroneous velocities by capping the velocity if we think it's too
148 * high, or setting it to a default velocity if know that the event data is
149 * bad. Returns the corrected velocity.
150 */
151 num _correctVelocity(num velocity) {
152 num absVelocity = velocity.abs();
153 if (absVelocity > _MAXIMUM_VELOCITY) {
154 absVelocity = _recentTouchesY.length < 6 ?
155 _VELOCITY_FOR_INCORRECT_EVENTS : _MAXIMUM_VELOCITY;
156 }
157 return absVelocity * (velocity < 0 ? -1 : 1);
158 }
159
160 /**
161 * Start listenting for events.
162 * If [capture] is True the TouchHandler should listen during the capture
163 * phase.
164 */
165 void enable([bool capture = false]) {
166 Function onEnd = (e) { _onEnd(e.timeStamp, e); };
167 _addEventListeners(
168 _element,
169 (e) { _onStart(e); },
170 (e) { _onMove(e); }, onEnd, onEnd, capture);
171 }
172
173 /**
174 * Get the current horizontal drag delta. Drag delta is defined as the deltaX
175 * of the start touch position and the last touch position.
176 */
177 int getDragDeltaX() {
178 return _lastTouchX - _startTouchX;
179 }
180
181 /**
182 * Get the current vertical drag delta. Drag delta is defined as the deltaY of
183 * the start touch position and the last touch position.
184 */
185 int getDragDeltaY() {
186 return _lastTouchY - _startTouchY;
187 }
188
189 /**
190 * Get end velocity of the drag. This method is specific to drag behavior, so
191 * if touch behavior and drag behavior is split then this should go with drag
192 * behavior. End velocity is defined as deltaXY / deltaTime where deltaXY is
193 * the difference between endPosition and the oldest recent position, and
194 * deltaTime is the difference between endTime and the oldest recent time
195 * stamp.
196 */
197 Coordinate getEndVelocity() {
198 num velocityX = 0;
199 num velocityY = 0;
200
201 if (_recentTouchesX.length > 0) {
202 num timeDeltaX = Math.max(1, _endTime - _recentTouchesX[1]);
203 velocityX = (_endTouchX - _recentTouchesX[0]) / timeDeltaX;
204 }
205
206 if (_recentTouchesY.length > 0) {
207 num timeDeltaY = Math.max(1, _endTime - _recentTouchesY[1]);
208 velocityY = (_endTouchY - _recentTouchesY[0]) / timeDeltaY;
209 }
210 velocityX = _correctVelocity(velocityX);
211 velocityY = _correctVelocity(velocityY);
212 return new Coordinate(velocityX, velocityY);
213 }
214
215 /**
216 * Return the touch of the last event.
217 */
218 Touch _getLastTouch() {
219 assert (_lastEvent != null); // Last event not set
220 return _lastEvent.touches[0];
221 }
222
223 /**
224 * Is the touch manager currently tracking touch moves to detect a drag?
225 */
226 bool isTracking() {
227 return _tracking;
228 }
229
230 /**
231 * Touch end handler.
232 */
233 void _onEnd(int timeStamp, [TouchEvent e = null]) {
234 _touching = false;
235 _touchable.onTouchEnd();
236 if (!_tracking || _draggable === null) {
237 return;
238 }
239 Touch touch = _getLastTouch();
240 int clientX = touch.clientX;
241 int clientY = touch.clientY;
242 if (_dragging) {
243 _endTime = timeStamp;
244 _endTouchX = clientX;
245 _endTouchY = clientY;
246 _recentTouchesX = _removeOldTouches(_recentTouchesX, timeStamp);
247 _recentTouchesY = _removeOldTouches(_recentTouchesY, timeStamp);
248 _draggable.onDragEnd();
249 if (e !== null) {
250 e.preventDefault();
251 }
252 ClickBuster.preventGhostClick(_startTouchX, _startTouchY);
253 }
254 _endTracking();
255 }
256
257 /**
258 * Touch move handler.
259 */
260 void _onMove(TouchEvent e) {
261 if (!_tracking || _draggable === null) {
262 return;
263 }
264 final touch = e.touches[0];
265 int clientX = touch.clientX;
266 int clientY = touch.clientY;
267 int moveX = _lastTouchX - clientX;
268 int moveY = _lastTouchY - clientY;
269 _totalMoveX += moveX.abs();
270 _totalMoveY += moveY.abs();
271 _lastTouchX = clientX;
272 _lastTouchY = clientY;
273 if (!_dragging &&
274 ((_totalMoveY > _MIN_TRACKING_FOR_DRAG && _draggable.verticalEnabled) ||
275 (_totalMoveX > _MIN_TRACKING_FOR_DRAG &&
276 _draggable.horizontalEnabled))) {
277 _dragging = _draggable.onDragStart(e);
278 if (!_dragging) {
279 _endTracking();
280 } else {
281 _startTouchX = clientX;
282 _startTouchY = clientY;
283 _startTime = e.timeStamp;
284 }
285 }
286 if (_dragging) {
287 _draggable.onDragMove();
288 _lastEvent = e;
289 e.preventDefault();
290 _recentTouchesX =
291 _removeTouchesInWrongDirection(_recentTouchesX, _lastMoveX, moveX);
292 _recentTouchesY =
293 _removeTouchesInWrongDirection(_recentTouchesY, _lastMoveY, moveY);
294 _recentTouchesX = _removeOldTouches(_recentTouchesX, e.timeStamp);
295 _recentTouchesY = _removeOldTouches(_recentTouchesY, e.timeStamp);
296 _recentTouchesX.add(clientX);
297 _recentTouchesX.add(e.timeStamp);
298 _recentTouchesY.add(clientY);
299 _recentTouchesY.add(e.timeStamp);
300 }
301 _lastMoveX = moveX;
302 _lastMoveY = moveY;
303 }
304
305 /**
306 * Touch start handler.
307 */
308 void _onStart(TouchEvent e) {
309 if (_touching) {
310 return;
311 }
312 _touching = true;
313 if (!_touchable.onTouchStart(e) || _draggable === null) {
314 return;
315 }
316 final touch = e.touches[0];
317 _startTouchX = _lastTouchX = touch.clientX;
318 _startTouchY = _lastTouchY = touch.clientY;
319 _startTime = e.timeStamp;
320 // TODO(jacobr): why don't we just clear the lists?
321 _recentTouchesX = new List<int>();
322 _recentTouchesY = new List<int>();
323 _recentTouchesX.add(touch.clientX);
324 _recentTouchesX.add(e.timeStamp);
325 _recentTouchesY.add(touch.clientY);
326 _recentTouchesY.add(e.timeStamp);
327 _lastEvent = e;
328 _beginTracking();
329 }
330
331 /**
332 * Filters the provided recent touches list to remove all touches older than
333 * the max tracking time or the 5th most recent touch.
334 * [recentTouches] specifies a list of tuples where the first item is the x
335 * or y component of the recent touch and the second item is the touch time
336 * stamp. The time of the most recent event is specified by [recentTime].
337 */
338 List<int> _removeOldTouches(List<int> recentTouches,
339 int recentTime) {
340 int count = 0;
341 final len = recentTouches.length;
342 assert (len % 2 == 0);
343 while (count < len &&
344 recentTime - recentTouches[count + 1] > _MAX_TRACKING_TIME ||
345 (len - count) > _MAX_TRACKING_TOUCHES * 2) {
346 count += 2;
347 }
348 return count == 0 ? recentTouches : _removeFirstN(recentTouches, count);
349 }
350
351 static List<int> _removeFirstN(List<int> list, int n) {
352 return list.getRange(n, list.length - n);
353 }
354
355 /**
356 * Filters the provided recent touches list to remove all touches except the
357 * last if the move direction has changed.
358 * [recentTouches] specifies a list of tuples where the first item is the x
359 * or y component of the recent touch and the second item is the touch time
360 * stamp. The x or y component of the most recent move is specified by
361 * [recentMove].
362 */
363 List<int> _removeTouchesInWrongDirection(List<int> recentTouches,
364 int lastMove, int recentMove) {
365 if (lastMove !=0 && recentMove != 0 && recentTouches.length > 2 &&
366 _xor(lastMove > 0, recentMove > 0)) {
367 return _removeFirstN(recentTouches, recentTouches.length - 2);
368 }
369 return recentTouches;
370 }
371
372 // TODO(jacobr): why doesn't bool implement the xor operator directly?
373 static bool _xor(bool a, bool b) {
374 return (a === true || b === true) && !(a === true && b === true);
375 }
376
377 /**
378 * Reset the touchable element.
379 */
380 void reset() {
381 _endTracking();
382 _touching = false;
383 }
384
385 /**
386 * Call this method to enable drag behavior on a draggable delegate.
387 * The [draggable] object can be the same as the [_touchable] object, they are
388 * assigned to different members to allow for strong typing with interfaces.
389 */
390 void setDraggable(Draggable draggable) {
391 _draggable = draggable;
392 }
393 }
OLDNEW
« no previous file with comments | « client/touch/TimeUtil.dart ('k') | client/touch/TouchUtil.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698