OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 // MSVC++ requires this to be set before any other includes to get M_PI. | |
6 #define _USE_MATH_DEFINES | |
7 | |
8 #include "ui/events/gestures/gesture_sequence.h" | |
9 | |
10 #include <stdlib.h> | |
11 #include <cmath> | |
12 #include <limits> | |
13 | |
14 #include "base/command_line.h" | |
15 #include "base/logging.h" | |
16 #include "base/memory/scoped_ptr.h" | |
17 #include "base/strings/string_number_conversions.h" | |
18 #include "base/time/time.h" | |
19 #include "ui/events/event.h" | |
20 #include "ui/events/event_constants.h" | |
21 #include "ui/events/event_switches.h" | |
22 #include "ui/events/gestures/gesture_configuration.h" | |
23 #include "ui/gfx/rect.h" | |
24 | |
25 namespace ui { | |
26 | |
27 namespace { | |
28 | |
29 // ui::EventType is mapped to TouchState so it can fit into 3 bits of | |
30 // Signature. | |
31 enum TouchState { | |
32 TS_RELEASED, | |
33 TS_PRESSED, | |
34 TS_MOVED, | |
35 TS_CANCELLED, | |
36 TS_UNKNOWN, | |
37 }; | |
38 | |
39 // ui::EventResult is mapped to TouchStatusInternal to simply indicate whether a | |
40 // processed touch-event should affect gesture-recognition or not. | |
41 enum TouchStatusInternal { | |
42 TSI_NOT_PROCESSED, // The touch-event should take-part into | |
43 // gesture-recognition only if the touch-event has not | |
44 // been processed. | |
45 | |
46 TSI_PROCESSED, // The touch-event should affect gesture-recognition only | |
47 // if the touch-event has been processed. For example,, | |
48 // this means that a JavaScript touch handler called | |
49 // |preventDefault| on the associated touch event | |
50 // or was processed by an aura-window or views-view. | |
51 | |
52 TSI_ALWAYS // The touch-event should always affect gesture | |
53 // recognition. | |
54 }; | |
55 | |
56 // Get equivalent TouchState from EventType |type|. | |
57 TouchState TouchEventTypeToTouchState(ui::EventType type) { | |
58 switch (type) { | |
59 case ui::ET_TOUCH_RELEASED: | |
60 return TS_RELEASED; | |
61 case ui::ET_TOUCH_PRESSED: | |
62 return TS_PRESSED; | |
63 case ui::ET_TOUCH_MOVED: | |
64 return TS_MOVED; | |
65 case ui::ET_TOUCH_CANCELLED: | |
66 return TS_CANCELLED; | |
67 default: | |
68 DVLOG(1) << "Unknown Touch Event type"; | |
69 } | |
70 return TS_UNKNOWN; | |
71 } | |
72 | |
73 // Gesture signature types for different values of combination (GestureState, | |
74 // touch_id, ui::EventType, touch_handled), see Signature for more info. | |
75 // | |
76 // Note: New addition of types should be placed as per their Signature value. | |
77 #define G(gesture_state, id, touch_state, handled) 1 + ( \ | |
78 (((touch_state) & 0x7) << 1) | \ | |
79 ((handled & 0x3) << 4) | \ | |
80 (((id) & 0xfff) << 6) | \ | |
81 ((gesture_state) << 18)) | |
82 | |
83 enum EdgeStateSignatureType { | |
84 GST_INVALID = -1, | |
85 | |
86 GST_NO_GESTURE_FIRST_PRESSED = | |
87 G(GS_NO_GESTURE, 0, TS_PRESSED, TSI_NOT_PROCESSED), | |
88 | |
89 GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED = | |
90 G(GS_PENDING_SYNTHETIC_CLICK, 0, TS_RELEASED, TSI_NOT_PROCESSED), | |
91 | |
92 GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED_HANDLED = | |
93 G(GS_PENDING_SYNTHETIC_CLICK, 0, TS_RELEASED, TSI_PROCESSED), | |
94 | |
95 // Ignore processed touch-move events until gesture-scroll starts. | |
96 GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED = | |
97 G(GS_PENDING_SYNTHETIC_CLICK, 0, TS_MOVED, TSI_NOT_PROCESSED), | |
98 | |
99 GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED_PROCESSED = | |
100 G(GS_PENDING_SYNTHETIC_CLICK, 0, TS_MOVED, TSI_PROCESSED), | |
101 | |
102 GST_PENDING_SYNTHETIC_CLICK_FIRST_CANCELLED = | |
103 G(GS_PENDING_SYNTHETIC_CLICK, 0, TS_CANCELLED, TSI_ALWAYS), | |
104 | |
105 GST_PENDING_SYNTHETIC_CLICK_SECOND_PRESSED = | |
106 G(GS_PENDING_SYNTHETIC_CLICK, 1, TS_PRESSED, TSI_NOT_PROCESSED), | |
107 | |
108 GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_RELEASED = | |
109 G(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL, | |
110 0, | |
111 TS_RELEASED, | |
112 TSI_NOT_PROCESSED), | |
113 | |
114 GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_RELEASED_HANDLED = | |
115 G(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL, 0, TS_RELEASED, TSI_PROCESSED), | |
116 | |
117 GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_MOVED = | |
118 G(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL, 0, TS_MOVED, TSI_ALWAYS), | |
119 | |
120 GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_CANCELLED = | |
121 G(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL, 0, TS_CANCELLED, TSI_ALWAYS), | |
122 | |
123 GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_SECOND_PRESSED = | |
124 G(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL, 1, TS_PRESSED, TSI_NOT_PROCESSED), | |
125 | |
126 GST_SYNTHETIC_CLICK_ABORTED_FIRST_RELEASED = | |
127 G(GS_SYNTHETIC_CLICK_ABORTED, 0, TS_RELEASED, TSI_ALWAYS), | |
128 | |
129 GST_SYNTHETIC_CLICK_ABORTED_SECOND_PRESSED = | |
130 G(GS_SYNTHETIC_CLICK_ABORTED, 1, TS_PRESSED, TSI_NOT_PROCESSED), | |
131 | |
132 GST_SCROLL_FIRST_RELEASED = | |
133 G(GS_SCROLL, 0, TS_RELEASED, TSI_ALWAYS), | |
134 | |
135 GST_SCROLL_FIRST_MOVED = | |
136 G(GS_SCROLL, 0, TS_MOVED, TSI_NOT_PROCESSED), | |
137 | |
138 GST_SCROLL_FIRST_MOVED_HANDLED = | |
139 G(GS_SCROLL, 0, TS_MOVED, TSI_PROCESSED), | |
140 | |
141 GST_SCROLL_FIRST_CANCELLED = | |
142 G(GS_SCROLL, 0, TS_CANCELLED, TSI_ALWAYS), | |
143 | |
144 GST_SCROLL_SECOND_PRESSED = | |
145 G(GS_SCROLL, 1, TS_PRESSED, TSI_NOT_PROCESSED), | |
146 | |
147 GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED = | |
148 G(GS_PENDING_TWO_FINGER_TAP, 0, TS_RELEASED, TSI_NOT_PROCESSED), | |
149 | |
150 GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED_HANDLED = | |
151 G(GS_PENDING_TWO_FINGER_TAP, 0, TS_RELEASED, TSI_PROCESSED), | |
152 | |
153 GST_PENDING_TWO_FINGER_TAP_SECOND_RELEASED = | |
154 G(GS_PENDING_TWO_FINGER_TAP, 1, TS_RELEASED, TSI_NOT_PROCESSED), | |
155 | |
156 GST_PENDING_TWO_FINGER_TAP_SECOND_RELEASED_HANDLED = | |
157 G(GS_PENDING_TWO_FINGER_TAP, 1, TS_RELEASED, TSI_PROCESSED), | |
158 | |
159 GST_PENDING_TWO_FINGER_TAP_FIRST_MOVED = | |
160 G(GS_PENDING_TWO_FINGER_TAP, 0, TS_MOVED, TSI_NOT_PROCESSED), | |
161 | |
162 GST_PENDING_TWO_FINGER_TAP_SECOND_MOVED = | |
163 G(GS_PENDING_TWO_FINGER_TAP, 1, TS_MOVED, TSI_NOT_PROCESSED), | |
164 | |
165 GST_PENDING_TWO_FINGER_TAP_FIRST_MOVED_HANDLED = | |
166 G(GS_PENDING_TWO_FINGER_TAP, 0, TS_MOVED, TSI_PROCESSED), | |
167 | |
168 GST_PENDING_TWO_FINGER_TAP_SECOND_MOVED_HANDLED = | |
169 G(GS_PENDING_TWO_FINGER_TAP, 1, TS_MOVED, TSI_PROCESSED), | |
170 | |
171 GST_PENDING_TWO_FINGER_TAP_FIRST_CANCELLED = | |
172 G(GS_PENDING_TWO_FINGER_TAP, 0, TS_CANCELLED, TSI_ALWAYS), | |
173 | |
174 GST_PENDING_TWO_FINGER_TAP_SECOND_CANCELLED = | |
175 G(GS_PENDING_TWO_FINGER_TAP, 1, TS_CANCELLED, TSI_ALWAYS), | |
176 | |
177 GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_RELEASED = | |
178 G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 0, TS_RELEASED, TSI_NOT_PROCESSED), | |
179 | |
180 GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_RELEASED_HANDLED = | |
181 G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 0, TS_RELEASED, TSI_PROCESSED), | |
182 | |
183 GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_RELEASED = | |
184 G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 1, TS_RELEASED, TSI_NOT_PROCESSED), | |
185 | |
186 GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_RELEASED_HANDLED = | |
187 G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 1, TS_RELEASED, TSI_PROCESSED), | |
188 | |
189 GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_MOVED = | |
190 G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 0, TS_MOVED, TSI_ALWAYS), | |
191 | |
192 GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_MOVED = | |
193 G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 1, TS_MOVED, TSI_ALWAYS), | |
194 | |
195 GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_CANCELLED = | |
196 G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 0, TS_CANCELLED, TSI_ALWAYS), | |
197 | |
198 GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_CANCELLED = | |
199 G(GS_PENDING_TWO_FINGER_TAP_NO_PINCH, 1, TS_CANCELLED, TSI_ALWAYS), | |
200 | |
201 GST_PENDING_TWO_FINGER_TAP_THIRD_PRESSED = | |
202 G(GS_PENDING_TWO_FINGER_TAP, 2, TS_PRESSED, TSI_NOT_PROCESSED), | |
203 | |
204 GST_PENDING_PINCH_FIRST_MOVED = | |
205 G(GS_PENDING_PINCH, 0, TS_MOVED, TSI_NOT_PROCESSED), | |
206 | |
207 GST_PENDING_PINCH_SECOND_MOVED = | |
208 G(GS_PENDING_PINCH, 1, TS_MOVED, TSI_NOT_PROCESSED), | |
209 | |
210 GST_PENDING_PINCH_FIRST_MOVED_HANDLED = | |
211 G(GS_PENDING_PINCH, 0, TS_MOVED, TSI_PROCESSED), | |
212 | |
213 GST_PENDING_PINCH_SECOND_MOVED_HANDLED = | |
214 G(GS_PENDING_PINCH, 1, TS_MOVED, TSI_PROCESSED), | |
215 | |
216 GST_PENDING_PINCH_FIRST_CANCELLED = | |
217 G(GS_PENDING_PINCH, 0, TS_CANCELLED, TSI_ALWAYS), | |
218 | |
219 GST_PENDING_PINCH_SECOND_CANCELLED = | |
220 G(GS_PENDING_PINCH, 1, TS_CANCELLED, TSI_ALWAYS), | |
221 | |
222 GST_PENDING_PINCH_FIRST_RELEASED = | |
223 G(GS_PENDING_PINCH, 0, TS_RELEASED, TSI_ALWAYS), | |
224 | |
225 GST_PENDING_PINCH_SECOND_RELEASED = | |
226 G(GS_PENDING_PINCH, 1, TS_RELEASED, TSI_ALWAYS), | |
227 | |
228 GST_PENDING_PINCH_NO_PINCH_FIRST_MOVED = | |
229 G(GS_PENDING_PINCH_NO_PINCH, 0, TS_MOVED, TSI_ALWAYS), | |
230 | |
231 GST_PENDING_PINCH_NO_PINCH_SECOND_MOVED = | |
232 G(GS_PENDING_PINCH_NO_PINCH, 1, TS_MOVED, TSI_ALWAYS), | |
233 | |
234 GST_PENDING_PINCH_NO_PINCH_FIRST_CANCELLED = | |
235 G(GS_PENDING_PINCH_NO_PINCH, 0, TS_CANCELLED, TSI_ALWAYS), | |
236 | |
237 GST_PENDING_PINCH_NO_PINCH_SECOND_CANCELLED = | |
238 G(GS_PENDING_PINCH_NO_PINCH, 1, TS_CANCELLED, TSI_ALWAYS), | |
239 | |
240 GST_PENDING_PINCH_NO_PINCH_FIRST_RELEASED = | |
241 G(GS_PENDING_PINCH_NO_PINCH, 0, TS_RELEASED, TSI_ALWAYS), | |
242 | |
243 GST_PENDING_PINCH_NO_PINCH_SECOND_RELEASED = | |
244 G(GS_PENDING_PINCH_NO_PINCH, 1, TS_RELEASED, TSI_ALWAYS), | |
245 | |
246 GST_PINCH_FIRST_MOVED = | |
247 G(GS_PINCH, 0, TS_MOVED, TSI_NOT_PROCESSED), | |
248 | |
249 GST_PINCH_FIRST_MOVED_HANDLED = | |
250 G(GS_PINCH, 0, TS_MOVED, TSI_PROCESSED), | |
251 | |
252 GST_PINCH_SECOND_MOVED = | |
253 G(GS_PINCH, 1, TS_MOVED, TSI_NOT_PROCESSED), | |
254 | |
255 GST_PINCH_SECOND_MOVED_HANDLED = | |
256 G(GS_PINCH, 1, TS_MOVED, TSI_PROCESSED), | |
257 | |
258 GST_PINCH_FIRST_RELEASED = | |
259 G(GS_PINCH, 0, TS_RELEASED, TSI_ALWAYS), | |
260 | |
261 GST_PINCH_SECOND_RELEASED = | |
262 G(GS_PINCH, 1, TS_RELEASED, TSI_ALWAYS), | |
263 | |
264 GST_PINCH_FIRST_CANCELLED = | |
265 G(GS_PINCH, 0, TS_CANCELLED, TSI_ALWAYS), | |
266 | |
267 GST_PINCH_SECOND_CANCELLED = | |
268 G(GS_PINCH, 1, TS_CANCELLED, TSI_ALWAYS), | |
269 | |
270 GST_PINCH_THIRD_PRESSED = | |
271 G(GS_PINCH, 2, TS_PRESSED, TSI_NOT_PROCESSED), | |
272 | |
273 GST_PINCH_THIRD_MOVED = | |
274 G(GS_PINCH, 2, TS_MOVED, TSI_NOT_PROCESSED), | |
275 | |
276 GST_PINCH_THIRD_MOVED_HANDLED = | |
277 G(GS_PINCH, 2, TS_MOVED, TSI_PROCESSED), | |
278 | |
279 GST_PINCH_THIRD_RELEASED = | |
280 G(GS_PINCH, 2, TS_RELEASED, TSI_ALWAYS), | |
281 | |
282 GST_PINCH_THIRD_CANCELLED = | |
283 G(GS_PINCH, 2, TS_CANCELLED, TSI_ALWAYS), | |
284 | |
285 GST_PINCH_FOURTH_PRESSED = | |
286 G(GS_PINCH, 3, TS_PRESSED, TSI_NOT_PROCESSED), | |
287 | |
288 GST_PINCH_FOURTH_MOVED = | |
289 G(GS_PINCH, 3, TS_MOVED, TSI_NOT_PROCESSED), | |
290 | |
291 GST_PINCH_FOURTH_MOVED_HANDLED = | |
292 G(GS_PINCH, 3, TS_MOVED, TSI_PROCESSED), | |
293 | |
294 GST_PINCH_FOURTH_RELEASED = | |
295 G(GS_PINCH, 3, TS_RELEASED, TSI_ALWAYS), | |
296 | |
297 GST_PINCH_FOURTH_CANCELLED = | |
298 G(GS_PINCH, 3, TS_CANCELLED, TSI_ALWAYS), | |
299 | |
300 GST_PINCH_FIFTH_PRESSED = | |
301 G(GS_PINCH, 4, TS_PRESSED, TSI_NOT_PROCESSED), | |
302 | |
303 GST_PINCH_FIFTH_MOVED = | |
304 G(GS_PINCH, 4, TS_MOVED, TSI_NOT_PROCESSED), | |
305 | |
306 GST_PINCH_FIFTH_MOVED_HANDLED = | |
307 G(GS_PINCH, 4, TS_MOVED, TSI_PROCESSED), | |
308 | |
309 GST_PINCH_FIFTH_RELEASED = | |
310 G(GS_PINCH, 4, TS_RELEASED, TSI_ALWAYS), | |
311 | |
312 GST_PINCH_FIFTH_CANCELLED = | |
313 G(GS_PINCH, 4, TS_CANCELLED, TSI_ALWAYS), | |
314 }; | |
315 | |
316 // Builds a signature. Signatures are assembled by joining together | |
317 // multiple bits. | |
318 // 1 LSB bit so that the computed signature is always greater than 0 | |
319 // 3 bits for the |type|. | |
320 // 2 bit for |touch_status| | |
321 // 12 bits for |touch_id| | |
322 // 14 bits for the |gesture_state|. | |
323 EdgeStateSignatureType Signature(GestureState gesture_state, | |
324 unsigned int touch_id, | |
325 ui::EventType type, | |
326 TouchStatusInternal touch_status) { | |
327 CHECK((touch_id & 0xfff) == touch_id); | |
328 TouchState touch_state = TouchEventTypeToTouchState(type); | |
329 EdgeStateSignatureType signature = static_cast<EdgeStateSignatureType> | |
330 (G(gesture_state, touch_id, touch_state, touch_status)); | |
331 | |
332 switch (signature) { | |
333 case GST_NO_GESTURE_FIRST_PRESSED: | |
334 case GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED: | |
335 case GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED_HANDLED: | |
336 case GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED: | |
337 case GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED_PROCESSED: | |
338 case GST_PENDING_SYNTHETIC_CLICK_FIRST_CANCELLED: | |
339 case GST_PENDING_SYNTHETIC_CLICK_SECOND_PRESSED: | |
340 case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_RELEASED: | |
341 case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_RELEASED_HANDLED: | |
342 case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_MOVED: | |
343 case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_CANCELLED: | |
344 case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_SECOND_PRESSED: | |
345 case GST_SYNTHETIC_CLICK_ABORTED_FIRST_RELEASED: | |
346 case GST_SYNTHETIC_CLICK_ABORTED_SECOND_PRESSED: | |
347 case GST_SCROLL_FIRST_RELEASED: | |
348 case GST_SCROLL_FIRST_MOVED: | |
349 case GST_SCROLL_FIRST_MOVED_HANDLED: | |
350 case GST_SCROLL_FIRST_CANCELLED: | |
351 case GST_SCROLL_SECOND_PRESSED: | |
352 case GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED: | |
353 case GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED_HANDLED: | |
354 case GST_PENDING_TWO_FINGER_TAP_SECOND_RELEASED: | |
355 case GST_PENDING_TWO_FINGER_TAP_SECOND_RELEASED_HANDLED: | |
356 case GST_PENDING_TWO_FINGER_TAP_FIRST_MOVED: | |
357 case GST_PENDING_TWO_FINGER_TAP_SECOND_MOVED: | |
358 case GST_PENDING_TWO_FINGER_TAP_FIRST_MOVED_HANDLED: | |
359 case GST_PENDING_TWO_FINGER_TAP_SECOND_MOVED_HANDLED: | |
360 case GST_PENDING_TWO_FINGER_TAP_FIRST_CANCELLED: | |
361 case GST_PENDING_TWO_FINGER_TAP_SECOND_CANCELLED: | |
362 case GST_PENDING_TWO_FINGER_TAP_THIRD_PRESSED: | |
363 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_MOVED: | |
364 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_MOVED: | |
365 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_RELEASED: | |
366 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_RELEASED: | |
367 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_RELEASED_HANDLED: | |
368 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_RELEASED_HANDLED: | |
369 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_CANCELLED: | |
370 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_CANCELLED: | |
371 case GST_PENDING_PINCH_FIRST_MOVED: | |
372 case GST_PENDING_PINCH_SECOND_MOVED: | |
373 case GST_PENDING_PINCH_FIRST_MOVED_HANDLED: | |
374 case GST_PENDING_PINCH_SECOND_MOVED_HANDLED: | |
375 case GST_PENDING_PINCH_FIRST_RELEASED: | |
376 case GST_PENDING_PINCH_SECOND_RELEASED: | |
377 case GST_PENDING_PINCH_FIRST_CANCELLED: | |
378 case GST_PENDING_PINCH_SECOND_CANCELLED: | |
379 case GST_PENDING_PINCH_NO_PINCH_FIRST_MOVED: | |
380 case GST_PENDING_PINCH_NO_PINCH_SECOND_MOVED: | |
381 case GST_PENDING_PINCH_NO_PINCH_FIRST_RELEASED: | |
382 case GST_PENDING_PINCH_NO_PINCH_SECOND_RELEASED: | |
383 case GST_PENDING_PINCH_NO_PINCH_FIRST_CANCELLED: | |
384 case GST_PENDING_PINCH_NO_PINCH_SECOND_CANCELLED: | |
385 case GST_PINCH_FIRST_MOVED: | |
386 case GST_PINCH_FIRST_MOVED_HANDLED: | |
387 case GST_PINCH_SECOND_MOVED: | |
388 case GST_PINCH_SECOND_MOVED_HANDLED: | |
389 case GST_PINCH_FIRST_RELEASED: | |
390 case GST_PINCH_SECOND_RELEASED: | |
391 case GST_PINCH_FIRST_CANCELLED: | |
392 case GST_PINCH_SECOND_CANCELLED: | |
393 case GST_PINCH_THIRD_PRESSED: | |
394 case GST_PINCH_THIRD_MOVED: | |
395 case GST_PINCH_THIRD_MOVED_HANDLED: | |
396 case GST_PINCH_THIRD_RELEASED: | |
397 case GST_PINCH_THIRD_CANCELLED: | |
398 case GST_PINCH_FOURTH_PRESSED: | |
399 case GST_PINCH_FOURTH_MOVED: | |
400 case GST_PINCH_FOURTH_MOVED_HANDLED: | |
401 case GST_PINCH_FOURTH_RELEASED: | |
402 case GST_PINCH_FOURTH_CANCELLED: | |
403 case GST_PINCH_FIFTH_PRESSED: | |
404 case GST_PINCH_FIFTH_MOVED: | |
405 case GST_PINCH_FIFTH_MOVED_HANDLED: | |
406 case GST_PINCH_FIFTH_RELEASED: | |
407 case GST_PINCH_FIFTH_CANCELLED: | |
408 break; | |
409 default: | |
410 signature = GST_INVALID; | |
411 break; | |
412 } | |
413 | |
414 return signature; | |
415 } | |
416 #undef G | |
417 | |
418 float BoundingBoxDiagonal(const gfx::RectF& rect) { | |
419 float width = rect.width() * rect.width(); | |
420 float height = rect.height() * rect.height(); | |
421 return sqrt(width + height); | |
422 } | |
423 | |
424 const float kFlingCurveNormalization = 1.0f / 1875.f; | |
425 | |
426 float CalibrateFlingVelocity(float velocity) { | |
427 const unsigned last_coefficient = | |
428 GestureConfiguration::NumAccelParams - 1; | |
429 float normalized_velocity = fabs(velocity * kFlingCurveNormalization); | |
430 float nu = 0.0f, x = 1.f; | |
431 | |
432 for (int i = last_coefficient ; i >= 0; i--) { | |
433 float a = GestureConfiguration::fling_acceleration_curve_coefficients(i); | |
434 nu += x * a; | |
435 x *= normalized_velocity; | |
436 } | |
437 if (velocity < 0.f) | |
438 return std::max(nu * velocity, -GestureConfiguration::fling_velocity_cap()); | |
439 else | |
440 return std::min(nu * velocity, GestureConfiguration::fling_velocity_cap()); | |
441 } | |
442 | |
443 | |
444 void UpdateGestureEventLatencyInfo(const TouchEvent& event, | |
445 GestureSequence::Gestures* gestures) { | |
446 // Copy some of the touch event's LatencyInfo into the generated gesture's | |
447 // LatencyInfo so we can compute touch to scroll latency from gesture | |
448 // event's LatencyInfo. | |
449 GestureSequence::Gestures::iterator it = gestures->begin(); | |
450 for (; it != gestures->end(); it++) { | |
451 ui::LatencyInfo* gesture_latency = (*it)->latency(); | |
452 gesture_latency->CopyLatencyFrom( | |
453 *event.latency(), ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT); | |
454 gesture_latency->CopyLatencyFrom( | |
455 *event.latency(), ui::INPUT_EVENT_LATENCY_UI_COMPONENT); | |
456 gesture_latency->CopyLatencyFrom( | |
457 *event.latency(), ui::INPUT_EVENT_LATENCY_ACKED_TOUCH_COMPONENT); | |
458 } | |
459 } | |
460 | |
461 bool GestureStateSupportsActiveTimer(GestureState state) { | |
462 switch(state) { | |
463 case GS_PENDING_SYNTHETIC_CLICK: | |
464 case GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL: | |
465 return true; | |
466 default: | |
467 return false; | |
468 } | |
469 } | |
470 | |
471 } // namespace | |
472 | |
473 //////////////////////////////////////////////////////////////////////////////// | |
474 // GestureSequence Public: | |
475 | |
476 GestureSequence::GestureSequence(GestureSequenceDelegate* delegate) | |
477 : state_(GS_NO_GESTURE), | |
478 flags_(0), | |
479 pinch_distance_start_(0.f), | |
480 pinch_distance_current_(0.f), | |
481 scroll_type_(ST_FREE), | |
482 point_count_(0), | |
483 delegate_(delegate) { | |
484 CHECK(delegate_); | |
485 } | |
486 | |
487 GestureSequence::~GestureSequence() { | |
488 } | |
489 | |
490 GestureSequence::Gestures* GestureSequence::ProcessTouchEventForGesture( | |
491 const TouchEvent& event, | |
492 EventResult result) { | |
493 StopTimersIfRequired(event); | |
494 last_touch_location_ = event.location(); | |
495 if (result & ER_CONSUMED) | |
496 return NULL; | |
497 | |
498 // Set a limit on the number of simultaneous touches in a gesture. | |
499 if (event.touch_id() >= kMaxGesturePoints) | |
500 return NULL; | |
501 | |
502 if (event.type() == ui::ET_TOUCH_PRESSED) { | |
503 if (point_count_ == kMaxGesturePoints) | |
504 return NULL; | |
505 GesturePoint* new_point = &points_[event.touch_id()]; | |
506 // We shouldn't be able to get two PRESSED events from the same | |
507 // finger without either a RELEASE or CANCEL in between. But let's not crash | |
508 // in a release build. | |
509 if (new_point->in_use()) { | |
510 LOG(ERROR) << "Received a second press for a point: " << event.touch_id(); | |
511 new_point->ResetVelocity(); | |
512 new_point->UpdateValues(event); | |
513 return NULL; | |
514 } | |
515 new_point->set_point_id(point_count_++); | |
516 new_point->set_touch_id(event.touch_id()); | |
517 new_point->set_source_device_id(event.source_device_id()); | |
518 } | |
519 | |
520 GestureState last_state = state_; | |
521 | |
522 // NOTE: when modifying these state transitions, also update gestures.dot | |
523 scoped_ptr<Gestures> gestures(new Gestures()); | |
524 GesturePoint& point = GesturePointForEvent(event); | |
525 point.UpdateValues(event); | |
526 RecreateBoundingBox(); | |
527 flags_ = event.flags(); | |
528 const int point_id = point.point_id(); | |
529 if (point_id < 0) | |
530 return NULL; | |
531 | |
532 // Send GESTURE_BEGIN for any touch pressed. | |
533 if (event.type() == ui::ET_TOUCH_PRESSED) | |
534 AppendBeginGestureEvent(point, gestures.get()); | |
535 | |
536 TouchStatusInternal status_internal = (result == ER_UNHANDLED) ? | |
537 TSI_NOT_PROCESSED : TSI_PROCESSED; | |
538 | |
539 EdgeStateSignatureType signature = Signature(state_, point_id, | |
540 event.type(), status_internal); | |
541 | |
542 if (signature == GST_INVALID) | |
543 signature = Signature(state_, point_id, event.type(), TSI_ALWAYS); | |
544 | |
545 switch (signature) { | |
546 case GST_INVALID: | |
547 break; | |
548 | |
549 case GST_NO_GESTURE_FIRST_PRESSED: | |
550 TouchDown(event, point, gestures.get()); | |
551 set_state(GS_PENDING_SYNTHETIC_CLICK); | |
552 break; | |
553 case GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED: | |
554 case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_RELEASED: | |
555 if (Click(event, point, gestures.get())) | |
556 point.UpdateForTap(); | |
557 else | |
558 PrependTapCancelGestureEvent(point, gestures.get()); | |
559 set_state(GS_NO_GESTURE); | |
560 break; | |
561 case GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED: | |
562 if (ScrollStart(event, point, gestures.get())) { | |
563 PrependTapCancelGestureEvent(point, gestures.get()); | |
564 set_state(GS_SCROLL); | |
565 if (ScrollUpdate(event, point, gestures.get(), FS_FIRST_SCROLL)) | |
566 point.UpdateForScroll(); | |
567 } | |
568 break; | |
569 case GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED_PROCESSED: | |
570 case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_MOVED: | |
571 if (point.IsInScrollWindow(event)) { | |
572 PrependTapCancelGestureEvent(point, gestures.get()); | |
573 set_state(GS_SYNTHETIC_CLICK_ABORTED); | |
574 } else { | |
575 set_state(GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL); | |
576 } | |
577 break; | |
578 case GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED_HANDLED: | |
579 case GST_PENDING_SYNTHETIC_CLICK_FIRST_CANCELLED: | |
580 case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_RELEASED_HANDLED: | |
581 case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_FIRST_CANCELLED: | |
582 PrependTapCancelGestureEvent(point, gestures.get()); | |
583 set_state(GS_NO_GESTURE); | |
584 break; | |
585 case GST_SYNTHETIC_CLICK_ABORTED_FIRST_RELEASED: | |
586 set_state(GS_NO_GESTURE); | |
587 break; | |
588 case GST_SCROLL_FIRST_MOVED: | |
589 if (scroll_type_ == ST_VERTICAL || | |
590 scroll_type_ == ST_HORIZONTAL) | |
591 BreakRailScroll(event, point, gestures.get()); | |
592 if (ScrollUpdate(event, point, gestures.get(), FS_NOT_FIRST_SCROLL)) | |
593 point.UpdateForScroll(); | |
594 break; | |
595 case GST_SCROLL_FIRST_MOVED_HANDLED: | |
596 if (point.DidScroll(event, 0)) | |
597 point.UpdateForScroll(); | |
598 break; | |
599 case GST_SCROLL_FIRST_RELEASED: | |
600 case GST_SCROLL_FIRST_CANCELLED: | |
601 ScrollEnd(event, point, gestures.get()); | |
602 set_state(GS_NO_GESTURE); | |
603 break; | |
604 case GST_PENDING_SYNTHETIC_CLICK_SECOND_PRESSED: | |
605 case GST_PENDING_SYNTHETIC_CLICK_NO_SCROLL_SECOND_PRESSED: | |
606 PrependTapCancelGestureEvent(point, gestures.get()); | |
607 TwoFingerTapOrPinch(event, point, gestures.get()); | |
608 break; | |
609 case GST_SYNTHETIC_CLICK_ABORTED_SECOND_PRESSED: | |
610 TwoFingerTapOrPinch(event, point, gestures.get()); | |
611 break; | |
612 case GST_SCROLL_SECOND_PRESSED: | |
613 PinchStart(event, point, gestures.get()); | |
614 set_state(GS_PINCH); | |
615 break; | |
616 case GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED: | |
617 case GST_PENDING_TWO_FINGER_TAP_SECOND_RELEASED: | |
618 TwoFingerTouchReleased(event, point, gestures.get()); | |
619 StartRailFreeScroll(point, gestures.get()); | |
620 break; | |
621 case GST_PENDING_TWO_FINGER_TAP_FIRST_MOVED: | |
622 case GST_PENDING_TWO_FINGER_TAP_SECOND_MOVED: | |
623 if (TwoFingerTouchMove(event, point, gestures.get())) | |
624 set_state(GS_PINCH); | |
625 break; | |
626 case GST_PENDING_TWO_FINGER_TAP_FIRST_MOVED_HANDLED: | |
627 case GST_PENDING_TWO_FINGER_TAP_SECOND_MOVED_HANDLED: | |
628 set_state(GS_PENDING_TWO_FINGER_TAP_NO_PINCH); | |
629 break; | |
630 case GST_PENDING_TWO_FINGER_TAP_FIRST_RELEASED_HANDLED: | |
631 case GST_PENDING_TWO_FINGER_TAP_SECOND_RELEASED_HANDLED: | |
632 case GST_PENDING_TWO_FINGER_TAP_FIRST_CANCELLED: | |
633 case GST_PENDING_TWO_FINGER_TAP_SECOND_CANCELLED: | |
634 StartRailFreeScroll(point, gestures.get()); | |
635 break; | |
636 case GST_PENDING_TWO_FINGER_TAP_THIRD_PRESSED: | |
637 set_state(GS_PENDING_PINCH); | |
638 break; | |
639 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_MOVED: | |
640 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_MOVED: | |
641 // No pinch allowed, so nothing happens. | |
642 break; | |
643 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_RELEASED: | |
644 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_RELEASED: | |
645 TwoFingerTouchReleased(event, point, gestures.get()); | |
646 // We transition into GS_SCROLL even though the touch move can be consumed | |
647 // and no scroll should happen. crbug.com/240399. | |
648 StartRailFreeScroll(point, gestures.get()); | |
649 break; | |
650 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_RELEASED_HANDLED: | |
651 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_RELEASED_HANDLED: | |
652 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_FIRST_CANCELLED: | |
653 case GST_PENDING_TWO_FINGER_TAP_NO_PINCH_SECOND_CANCELLED: | |
654 // We transition into GS_SCROLL even though the touch move can be consumed | |
655 // and no scroll should happen. crbug.com/240399. | |
656 StartRailFreeScroll(point, gestures.get()); | |
657 break; | |
658 case GST_PENDING_PINCH_FIRST_MOVED: | |
659 case GST_PENDING_PINCH_SECOND_MOVED: | |
660 if (TwoFingerTouchMove(event, point, gestures.get())) | |
661 set_state(GS_PINCH); | |
662 break; | |
663 case GST_PENDING_PINCH_FIRST_MOVED_HANDLED: | |
664 case GST_PENDING_PINCH_SECOND_MOVED_HANDLED: | |
665 set_state(GS_PENDING_PINCH_NO_PINCH); | |
666 break; | |
667 case GST_PENDING_PINCH_FIRST_RELEASED: | |
668 case GST_PENDING_PINCH_SECOND_RELEASED: | |
669 case GST_PENDING_PINCH_FIRST_CANCELLED: | |
670 case GST_PENDING_PINCH_SECOND_CANCELLED: | |
671 // We transition into GS_SCROLL even though the touch move can be consumed | |
672 // and no scroll should happen. crbug.com/240399. | |
673 StartRailFreeScroll(point, gestures.get()); | |
674 break; | |
675 case GST_PENDING_PINCH_NO_PINCH_FIRST_MOVED: | |
676 case GST_PENDING_PINCH_NO_PINCH_SECOND_MOVED: | |
677 // No pinch allowed, so nothing happens. | |
678 break; | |
679 case GST_PENDING_PINCH_NO_PINCH_FIRST_RELEASED: | |
680 case GST_PENDING_PINCH_NO_PINCH_SECOND_RELEASED: | |
681 case GST_PENDING_PINCH_NO_PINCH_FIRST_CANCELLED: | |
682 case GST_PENDING_PINCH_NO_PINCH_SECOND_CANCELLED: | |
683 // We transition into GS_SCROLL even though the touch move can be consumed | |
684 // and no scroll should happen. crbug.com/240399. | |
685 StartRailFreeScroll(point, gestures.get()); | |
686 break; | |
687 case GST_PINCH_FIRST_MOVED_HANDLED: | |
688 case GST_PINCH_SECOND_MOVED_HANDLED: | |
689 case GST_PINCH_THIRD_MOVED_HANDLED: | |
690 case GST_PINCH_FOURTH_MOVED_HANDLED: | |
691 case GST_PINCH_FIFTH_MOVED_HANDLED: | |
692 // If touches are consumed for a while, and then left unconsumed, we don't | |
693 // want a PinchUpdate or ScrollUpdate with a massive delta. | |
694 latest_multi_scroll_update_location_ = bounding_box_.CenterPoint(); | |
695 pinch_distance_current_ = BoundingBoxDiagonal(bounding_box_); | |
696 break; | |
697 case GST_PINCH_FIRST_MOVED: | |
698 case GST_PINCH_SECOND_MOVED: | |
699 case GST_PINCH_THIRD_MOVED: | |
700 case GST_PINCH_FOURTH_MOVED: | |
701 case GST_PINCH_FIFTH_MOVED: | |
702 if (PinchUpdate(event, point, gestures.get())) { | |
703 for (int i = 0; i < point_count_; ++i) | |
704 GetPointByPointId(i)->UpdateForScroll(); | |
705 } | |
706 break; | |
707 case GST_PINCH_FIRST_RELEASED: | |
708 case GST_PINCH_SECOND_RELEASED: | |
709 case GST_PINCH_THIRD_RELEASED: | |
710 case GST_PINCH_FOURTH_RELEASED: | |
711 case GST_PINCH_FIFTH_RELEASED: | |
712 case GST_PINCH_FIRST_CANCELLED: | |
713 case GST_PINCH_SECOND_CANCELLED: | |
714 case GST_PINCH_THIRD_CANCELLED: | |
715 case GST_PINCH_FOURTH_CANCELLED: | |
716 case GST_PINCH_FIFTH_CANCELLED: | |
717 // Was it a swipe? i.e. were all the fingers moving in the same | |
718 // direction? | |
719 MaybeSwipe(event, point, gestures.get()); | |
720 | |
721 if (point_count_ == 2) { | |
722 PinchEnd(event, point, gestures.get()); | |
723 | |
724 // Once pinch ends, it should still be possible to scroll with the | |
725 // remaining finger on the screen. | |
726 set_state(GS_SCROLL); | |
727 } else { | |
728 // Nothing else to do if we have more than 2 fingers active, since after | |
729 // the release/cancel, there are still enough fingers to do pinch. | |
730 // pinch_distance_current_ and pinch_distance_start_ will be updated | |
731 // when the bounding-box is updated. | |
732 } | |
733 ResetVelocities(); | |
734 break; | |
735 case GST_PINCH_THIRD_PRESSED: | |
736 case GST_PINCH_FOURTH_PRESSED: | |
737 case GST_PINCH_FIFTH_PRESSED: | |
738 pinch_distance_current_ = BoundingBoxDiagonal(bounding_box_); | |
739 pinch_distance_start_ = pinch_distance_current_; | |
740 break; | |
741 } | |
742 | |
743 if (event.type() == ui::ET_TOUCH_RELEASED || | |
744 event.type() == ui::ET_TOUCH_CANCELLED) | |
745 AppendEndGestureEvent(point, gestures.get()); | |
746 | |
747 if (state_ != last_state) | |
748 DVLOG(4) << "Gesture Sequence" | |
749 << " State: " << state_ | |
750 << " touch id: " << event.touch_id(); | |
751 | |
752 // If the state has changed from one in which a long/show press is possible to | |
753 // one in which they are not possible, cancel the timers. | |
754 if (GestureStateSupportsActiveTimer(last_state) && | |
755 !GestureStateSupportsActiveTimer(state_)) { | |
756 GetLongPressTimer()->Stop(); | |
757 GetShowPressTimer()->Stop(); | |
758 } | |
759 | |
760 // The set of point_ids must be contiguous and include 0. | |
761 // When a touch point is released, all points with ids greater than the | |
762 // released point must have their ids decremented, or the set of point_ids | |
763 // could end up with gaps. | |
764 if (event.type() == ui::ET_TOUCH_RELEASED || | |
765 event.type() == ui::ET_TOUCH_CANCELLED) { | |
766 for (int i = 0; i < kMaxGesturePoints; ++i) { | |
767 GesturePoint& iter_point = points_[i]; | |
768 if (iter_point.point_id() > point.point_id()) | |
769 iter_point.set_point_id(iter_point.point_id() - 1); | |
770 } | |
771 | |
772 point.Reset(); | |
773 --point_count_; | |
774 CHECK_GE(point_count_, 0); | |
775 RecreateBoundingBox(); | |
776 if (state_ == GS_PINCH) { | |
777 pinch_distance_current_ = BoundingBoxDiagonal(bounding_box_); | |
778 pinch_distance_start_ = pinch_distance_current_; | |
779 } | |
780 } | |
781 | |
782 UpdateGestureEventLatencyInfo(event, gestures.get()); | |
783 return gestures.release(); | |
784 } | |
785 | |
786 void GestureSequence::RecreateBoundingBox() { | |
787 // TODO(sad): Recreating the bounding box at every touch-event is not very | |
788 // efficient. This should be made better. | |
789 if (point_count_ == 0) { | |
790 bounding_box_.SetRect(0, 0, 0, 0); | |
791 } else if (point_count_ == 1) { | |
792 bounding_box_ = GetPointByPointId(0)->enclosing_rectangle(); | |
793 } else { | |
794 float left = std::numeric_limits<float>::max(); | |
795 float top = std::numeric_limits<float>::max(); | |
796 float right = -std::numeric_limits<float>::max(); | |
797 float bottom = -std::numeric_limits<float>::max(); | |
798 for (int i = 0; i < kMaxGesturePoints; ++i) { | |
799 if (!points_[i].in_use()) | |
800 continue; | |
801 // Using the |enclosing_rectangle()| for the touch-points would be ideal. | |
802 // However, this becomes brittle especially when a finger is in motion | |
803 // because the change in radius can overshadow the actual change in | |
804 // position. So the actual position of the point is used instead. | |
805 const gfx::PointF& point = points_[i].last_touch_position(); | |
806 left = std::min(left, point.x()); | |
807 right = std::max(right, point.x()); | |
808 top = std::min(top, point.y()); | |
809 bottom = std::max(bottom, point.y()); | |
810 } | |
811 bounding_box_.SetRect(left, top, right - left, bottom - top); | |
812 } | |
813 } | |
814 | |
815 void GestureSequence::ResetVelocities() { | |
816 for (int i = 0; i < kMaxGesturePoints; ++i) { | |
817 if (points_[i].in_use()) | |
818 points_[i].ResetVelocity(); | |
819 } | |
820 } | |
821 | |
822 //////////////////////////////////////////////////////////////////////////////// | |
823 // GestureSequence Protected: | |
824 | |
825 base::OneShotTimer<GestureSequence>* GestureSequence::CreateTimer() { | |
826 return new base::OneShotTimer<GestureSequence>(); | |
827 } | |
828 | |
829 base::OneShotTimer<GestureSequence>* GestureSequence::GetLongPressTimer() { | |
830 if (!long_press_timer_.get()) | |
831 long_press_timer_.reset(CreateTimer()); | |
832 return long_press_timer_.get(); | |
833 } | |
834 | |
835 base::OneShotTimer<GestureSequence>* GestureSequence::GetShowPressTimer() { | |
836 if (!show_press_timer_.get()) | |
837 show_press_timer_.reset(CreateTimer()); | |
838 return show_press_timer_.get(); | |
839 } | |
840 | |
841 //////////////////////////////////////////////////////////////////////////////// | |
842 // GestureSequence Private: | |
843 | |
844 GesturePoint& GestureSequence::GesturePointForEvent( | |
845 const TouchEvent& event) { | |
846 return points_[event.touch_id()]; | |
847 } | |
848 | |
849 GesturePoint* GestureSequence::GetPointByPointId(int point_id) { | |
850 DCHECK(0 <= point_id && point_id < kMaxGesturePoints); | |
851 for (int i = 0; i < kMaxGesturePoints; ++i) { | |
852 GesturePoint& point = points_[i]; | |
853 if (point.in_use() && point.point_id() == point_id) | |
854 return &point; | |
855 } | |
856 NOTREACHED(); | |
857 return NULL; | |
858 } | |
859 | |
860 bool GestureSequence::IsSecondTouchDownCloseEnoughForTwoFingerTap() { | |
861 gfx::PointF p1 = GetPointByPointId(0)->last_touch_position(); | |
862 gfx::PointF p2 = GetPointByPointId(1)->last_touch_position(); | |
863 double max_distance = | |
864 GestureConfiguration::max_distance_for_two_finger_tap_in_pixels(); | |
865 double distance = (p1.x() - p2.x()) * (p1.x() - p2.x()) + | |
866 (p1.y() - p2.y()) * (p1.y() - p2.y()); | |
867 if (distance < max_distance * max_distance) | |
868 return true; | |
869 return false; | |
870 } | |
871 | |
872 GestureEvent* GestureSequence::CreateGestureEvent( | |
873 const GestureEventDetails& details, | |
874 const gfx::PointF& location, | |
875 int flags, | |
876 base::Time timestamp, | |
877 int oldest_touch_id) { | |
878 GestureEventDetails gesture_details(details); | |
879 gesture_details.set_touch_points(point_count_); | |
880 gesture_details.set_bounding_box(bounding_box_); | |
881 gesture_details.set_oldest_touch_id(oldest_touch_id); | |
882 base::TimeDelta time_stamp = | |
883 base::TimeDelta::FromMicroseconds(timestamp.ToDoubleT() * 1000000); | |
884 return new GestureEvent(location.x(), location.y(), | |
885 flags, time_stamp, gesture_details); | |
886 } | |
887 | |
888 void GestureSequence::AppendTapDownGestureEvent(const GesturePoint& point, | |
889 Gestures* gestures) { | |
890 gestures->push_back(CreateGestureEvent( | |
891 GestureEventDetails(ui::ET_GESTURE_TAP_DOWN, 0, 0), | |
892 point.first_touch_position(), | |
893 flags_, | |
894 base::Time::FromDoubleT(point.last_touch_time()), | |
895 point.touch_id())); | |
896 } | |
897 | |
898 void GestureSequence::PrependTapCancelGestureEvent(const GesturePoint& point, | |
899 Gestures* gestures) { | |
900 gestures->insert(gestures->begin(), CreateGestureEvent( | |
901 GestureEventDetails(ui::ET_GESTURE_TAP_CANCEL, 0, 0), | |
902 point.first_touch_position(), | |
903 flags_, | |
904 base::Time::FromDoubleT(point.last_touch_time()), | |
905 point.touch_id())); | |
906 } | |
907 | |
908 void GestureSequence::AppendBeginGestureEvent(const GesturePoint& point, | |
909 Gestures* gestures) { | |
910 gestures->push_back(CreateGestureEvent( | |
911 GestureEventDetails(ui::ET_GESTURE_BEGIN, 0, 0), | |
912 point.first_touch_position(), | |
913 flags_, | |
914 base::Time::FromDoubleT(point.last_touch_time()), | |
915 point.touch_id())); | |
916 } | |
917 | |
918 void GestureSequence::AppendEndGestureEvent(const GesturePoint& point, | |
919 Gestures* gestures) { | |
920 gestures->push_back(CreateGestureEvent( | |
921 GestureEventDetails(ui::ET_GESTURE_END, 0, 0), | |
922 point.last_touch_position(), | |
923 flags_, | |
924 base::Time::FromDoubleT(point.last_touch_time()), | |
925 point.touch_id())); | |
926 } | |
927 | |
928 void GestureSequence::AppendClickGestureEvent(const GesturePoint& point, | |
929 int tap_count, | |
930 Gestures* gestures) { | |
931 gfx::RectF er = point.enclosing_rectangle(); | |
932 gfx::PointF center = er.CenterPoint(); | |
933 gestures->push_back(CreateGestureEvent( | |
934 GestureEventDetails(ui::ET_GESTURE_TAP, tap_count, 0), | |
935 center, | |
936 flags_, | |
937 base::Time::FromDoubleT(point.last_touch_time()), | |
938 point.touch_id())); | |
939 } | |
940 | |
941 void GestureSequence::AppendScrollGestureBegin(const GesturePoint& point, | |
942 const gfx::PointF& location, | |
943 Gestures* gestures) { | |
944 gfx::Vector2dF d = point.ScrollDelta(); | |
945 gestures->push_back(CreateGestureEvent( | |
946 GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN, d.x(), d.y()), | |
947 location, | |
948 flags_, | |
949 base::Time::FromDoubleT(point.last_touch_time()), | |
950 point.touch_id())); | |
951 } | |
952 | |
953 void GestureSequence::AppendScrollGestureEnd(const GesturePoint& point, | |
954 const gfx::PointF& location, | |
955 Gestures* gestures, | |
956 float x_velocity, | |
957 float y_velocity) { | |
958 float railed_x_velocity = x_velocity; | |
959 float railed_y_velocity = y_velocity; | |
960 last_scroll_prediction_offset_.set_x(0); | |
961 last_scroll_prediction_offset_.set_y(0); | |
962 | |
963 if (scroll_type_ == ST_HORIZONTAL) | |
964 railed_y_velocity = 0; | |
965 else if (scroll_type_ == ST_VERTICAL) | |
966 railed_x_velocity = 0; | |
967 | |
968 if (railed_x_velocity != 0 || railed_y_velocity != 0) { | |
969 | |
970 gestures->push_back(CreateGestureEvent( | |
971 GestureEventDetails(ui::ET_SCROLL_FLING_START, | |
972 CalibrateFlingVelocity(railed_x_velocity), | |
973 CalibrateFlingVelocity(railed_y_velocity)), | |
974 location, | |
975 flags_, | |
976 base::Time::FromDoubleT(point.last_touch_time()), | |
977 point.touch_id())); | |
978 } else { | |
979 gestures->push_back(CreateGestureEvent( | |
980 GestureEventDetails(ui::ET_GESTURE_SCROLL_END, 0, 0), | |
981 location, | |
982 flags_, | |
983 base::Time::FromDoubleT(point.last_touch_time()), | |
984 point.touch_id())); | |
985 } | |
986 } | |
987 | |
988 void GestureSequence::AppendScrollGestureUpdate(GesturePoint& point, | |
989 Gestures* gestures, | |
990 IsFirstScroll is_first_scroll) { | |
991 static bool use_scroll_prediction = CommandLine::ForCurrentProcess()-> | |
992 HasSwitch(switches::kEnableScrollPrediction); | |
993 gfx::Vector2dF d; | |
994 gfx::PointF location; | |
995 if (point_count_ == 1) { | |
996 d = point.ScrollDelta(); | |
997 location = point.last_touch_position(); | |
998 } else { | |
999 location = bounding_box_.CenterPoint(); | |
1000 d = location - latest_multi_scroll_update_location_; | |
1001 latest_multi_scroll_update_location_ = location; | |
1002 } | |
1003 | |
1004 if (use_scroll_prediction) { | |
1005 // Remove the extra distance added by the last scroll prediction and add | |
1006 // the new prediction offset. | |
1007 d -= last_scroll_prediction_offset_; | |
1008 last_scroll_prediction_offset_.set_x( | |
1009 GestureConfiguration::scroll_prediction_seconds() * point.XVelocity()); | |
1010 last_scroll_prediction_offset_.set_y( | |
1011 GestureConfiguration::scroll_prediction_seconds() * point.YVelocity()); | |
1012 d += last_scroll_prediction_offset_; | |
1013 location += gfx::Vector2dF(last_scroll_prediction_offset_.x(), | |
1014 last_scroll_prediction_offset_.y()); | |
1015 } | |
1016 | |
1017 if (is_first_scroll == FS_FIRST_SCROLL) { | |
1018 float slop = GestureConfiguration::max_touch_move_in_pixels_for_click(); | |
1019 float length = d.Length(); | |
1020 float ratio = std::max((length - slop) / length, 0.0f); | |
1021 | |
1022 d.set_x(d.x() * ratio); | |
1023 d.set_y(d.y() * ratio); | |
1024 } | |
1025 | |
1026 if (scroll_type_ == ST_HORIZONTAL) | |
1027 d.set_y(0); | |
1028 else if (scroll_type_ == ST_VERTICAL) | |
1029 d.set_x(0); | |
1030 if (d.IsZero()) | |
1031 return; | |
1032 | |
1033 GestureEventDetails details(ui::ET_GESTURE_SCROLL_UPDATE, d.x(), d.y()); | |
1034 gestures->push_back(CreateGestureEvent( | |
1035 details, | |
1036 location, | |
1037 flags_, | |
1038 base::Time::FromDoubleT(point.last_touch_time()), | |
1039 point.touch_id())); | |
1040 } | |
1041 | |
1042 void GestureSequence::AppendPinchGestureBegin(const GesturePoint& p1, | |
1043 const GesturePoint& p2, | |
1044 Gestures* gestures) { | |
1045 gfx::PointF center = bounding_box_.CenterPoint(); | |
1046 gestures->push_back(CreateGestureEvent( | |
1047 GestureEventDetails(ui::ET_GESTURE_PINCH_BEGIN, 0, 0), | |
1048 center, | |
1049 flags_, | |
1050 base::Time::FromDoubleT(p1.last_touch_time()), | |
1051 p1.touch_id())); | |
1052 } | |
1053 | |
1054 void GestureSequence::AppendPinchGestureEnd(const GesturePoint& p1, | |
1055 const GesturePoint& p2, | |
1056 float scale, | |
1057 Gestures* gestures) { | |
1058 gfx::PointF center = bounding_box_.CenterPoint(); | |
1059 gestures->push_back(CreateGestureEvent( | |
1060 GestureEventDetails(ui::ET_GESTURE_PINCH_END, 0, 0), | |
1061 center, | |
1062 flags_, | |
1063 base::Time::FromDoubleT(p1.last_touch_time()), | |
1064 p1.touch_id())); | |
1065 } | |
1066 | |
1067 void GestureSequence::AppendPinchGestureUpdate(const GesturePoint& point, | |
1068 float scale, | |
1069 Gestures* gestures) { | |
1070 // TODO(sad): Compute rotation and include it in delta_y. | |
1071 // http://crbug.com/113145 | |
1072 gestures->push_back(CreateGestureEvent( | |
1073 GestureEventDetails(ui::ET_GESTURE_PINCH_UPDATE, scale, 0), | |
1074 bounding_box_.CenterPoint(), | |
1075 flags_, | |
1076 base::Time::FromDoubleT(point.last_touch_time()), | |
1077 point.touch_id())); | |
1078 } | |
1079 | |
1080 void GestureSequence::AppendSwipeGesture(const GesturePoint& point, | |
1081 int swipe_x, | |
1082 int swipe_y, | |
1083 Gestures* gestures) { | |
1084 gestures->push_back(CreateGestureEvent( | |
1085 GestureEventDetails(ui::ET_GESTURE_SWIPE, swipe_x, swipe_y), | |
1086 bounding_box_.CenterPoint(), | |
1087 flags_, | |
1088 base::Time::FromDoubleT(point.last_touch_time()), | |
1089 point.touch_id())); | |
1090 } | |
1091 | |
1092 void GestureSequence::AppendTwoFingerTapGestureEvent(Gestures* gestures) { | |
1093 const GesturePoint* point = GetPointByPointId(0); | |
1094 const gfx::RectF& rect = point->enclosing_rectangle(); | |
1095 gestures->push_back(CreateGestureEvent( | |
1096 GestureEventDetails(ui::ET_GESTURE_TWO_FINGER_TAP, | |
1097 rect.width(), | |
1098 rect.height()), | |
1099 point->enclosing_rectangle().CenterPoint(), | |
1100 flags_, | |
1101 base::Time::FromDoubleT(point->last_touch_time()), | |
1102 point->touch_id())); | |
1103 } | |
1104 | |
1105 bool GestureSequence::Click(const TouchEvent& event, | |
1106 const GesturePoint& point, | |
1107 Gestures* gestures) { | |
1108 DCHECK(state_ == GS_PENDING_SYNTHETIC_CLICK || | |
1109 state_ == GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL); | |
1110 if (point.IsInClickWindow(event)) { | |
1111 int tap_count = 1; | |
1112 if (point.IsInTripleClickWindow(event)) | |
1113 tap_count = 3; | |
1114 else if (point.IsInDoubleClickWindow(event)) | |
1115 tap_count = 2; | |
1116 if (tap_count == 1 && GetShowPressTimer()->IsRunning()) { | |
1117 GetShowPressTimer()->Stop(); | |
1118 AppendShowPressGestureEvent(); | |
1119 } | |
1120 AppendClickGestureEvent(point, tap_count, gestures); | |
1121 return true; | |
1122 } else if (point.IsInsideTouchSlopRegion(event) && | |
1123 !GetLongPressTimer()->IsRunning()) { | |
1124 AppendLongTapGestureEvent(point, gestures); | |
1125 } | |
1126 return false; | |
1127 } | |
1128 | |
1129 bool GestureSequence::ScrollStart(const TouchEvent& event, | |
1130 GesturePoint& point, | |
1131 Gestures* gestures) { | |
1132 DCHECK(state_ == GS_PENDING_SYNTHETIC_CLICK); | |
1133 if (!point.IsInScrollWindow(event)) | |
1134 return false; | |
1135 AppendScrollGestureBegin(point, point.first_touch_position(), gestures); | |
1136 if (point.IsInHorizontalRailWindow()) | |
1137 scroll_type_ = ST_HORIZONTAL; | |
1138 else if (point.IsInVerticalRailWindow()) | |
1139 scroll_type_ = ST_VERTICAL; | |
1140 else | |
1141 scroll_type_ = ST_FREE; | |
1142 return true; | |
1143 } | |
1144 | |
1145 void GestureSequence::BreakRailScroll(const TouchEvent& event, | |
1146 GesturePoint& point, | |
1147 Gestures* gestures) { | |
1148 DCHECK(state_ == GS_SCROLL); | |
1149 if (scroll_type_ == ST_HORIZONTAL && | |
1150 point.BreaksHorizontalRail()) | |
1151 scroll_type_ = ST_FREE; | |
1152 else if (scroll_type_ == ST_VERTICAL && | |
1153 point.BreaksVerticalRail()) | |
1154 scroll_type_ = ST_FREE; | |
1155 } | |
1156 | |
1157 bool GestureSequence::ScrollUpdate(const TouchEvent& event, | |
1158 GesturePoint& point, | |
1159 Gestures* gestures, | |
1160 IsFirstScroll is_first_scroll) { | |
1161 DCHECK(state_ == GS_SCROLL); | |
1162 if (!point.DidScroll(event, 0)) | |
1163 return false; | |
1164 AppendScrollGestureUpdate(point, gestures, is_first_scroll); | |
1165 return true; | |
1166 } | |
1167 | |
1168 bool GestureSequence::TouchDown(const TouchEvent& event, | |
1169 const GesturePoint& point, | |
1170 Gestures* gestures) { | |
1171 DCHECK(state_ == GS_NO_GESTURE); | |
1172 AppendTapDownGestureEvent(point, gestures); | |
1173 GetLongPressTimer()->Start( | |
1174 FROM_HERE, | |
1175 base::TimeDelta::FromMilliseconds( | |
1176 GestureConfiguration::long_press_time_in_seconds() * 1000), | |
1177 this, | |
1178 &GestureSequence::AppendLongPressGestureEvent); | |
1179 | |
1180 GetShowPressTimer()->Start( | |
1181 FROM_HERE, | |
1182 base::TimeDelta::FromMilliseconds( | |
1183 GestureConfiguration::show_press_delay_in_ms()), | |
1184 this, | |
1185 &GestureSequence::AppendShowPressGestureEvent); | |
1186 | |
1187 return true; | |
1188 } | |
1189 | |
1190 bool GestureSequence::TwoFingerTouchDown(const TouchEvent& event, | |
1191 const GesturePoint& point, | |
1192 Gestures* gestures) { | |
1193 DCHECK(state_ == GS_PENDING_SYNTHETIC_CLICK || | |
1194 state_ == GS_PENDING_SYNTHETIC_CLICK_NO_SCROLL || | |
1195 state_ == GS_SYNTHETIC_CLICK_ABORTED || | |
1196 state_ == GS_SCROLL); | |
1197 | |
1198 if (state_ == GS_SCROLL) { | |
1199 AppendScrollGestureEnd(point, | |
1200 point.last_touch_position(), | |
1201 gestures, 0.f, 0.f); | |
1202 } | |
1203 second_touch_time_ = event.time_stamp(); | |
1204 return true; | |
1205 } | |
1206 | |
1207 bool GestureSequence::TwoFingerTouchMove(const TouchEvent& event, | |
1208 const GesturePoint& point, | |
1209 Gestures* gestures) { | |
1210 DCHECK(state_ == GS_PENDING_TWO_FINGER_TAP || | |
1211 state_ == GS_PENDING_PINCH); | |
1212 | |
1213 base::TimeDelta time_delta = event.time_stamp() - second_touch_time_; | |
1214 base::TimeDelta max_delta = base::TimeDelta::FromMilliseconds(1000 * | |
1215 ui::GestureConfiguration::max_touch_down_duration_in_seconds_for_click()); | |
1216 if (time_delta > max_delta || !point.IsInsideTouchSlopRegion(event)) { | |
1217 PinchStart(event, point, gestures); | |
1218 return true; | |
1219 } | |
1220 return false; | |
1221 } | |
1222 | |
1223 bool GestureSequence::TwoFingerTouchReleased(const TouchEvent& event, | |
1224 const GesturePoint& point, | |
1225 Gestures* gestures) { | |
1226 DCHECK(state_ == GS_PENDING_TWO_FINGER_TAP || | |
1227 state_ == GS_PENDING_TWO_FINGER_TAP_NO_PINCH); | |
1228 base::TimeDelta time_delta = event.time_stamp() - second_touch_time_; | |
1229 base::TimeDelta max_delta = base::TimeDelta::FromMilliseconds(1000 * | |
1230 ui::GestureConfiguration::max_touch_down_duration_in_seconds_for_click()); | |
1231 if (time_delta < max_delta && point.IsInsideTouchSlopRegion(event)) | |
1232 AppendTwoFingerTapGestureEvent(gestures); | |
1233 return true; | |
1234 } | |
1235 | |
1236 void GestureSequence::AppendLongPressGestureEvent() { | |
1237 const GesturePoint* point = GetPointByPointId(0); | |
1238 scoped_ptr<GestureEvent> gesture(CreateGestureEvent( | |
1239 GestureEventDetails(ui::ET_GESTURE_LONG_PRESS, 0, 0), | |
1240 point->first_touch_position(), | |
1241 flags_, | |
1242 base::Time::FromDoubleT(point->last_touch_time()), | |
1243 point->touch_id())); | |
1244 delegate_->DispatchPostponedGestureEvent(gesture.get()); | |
1245 } | |
1246 | |
1247 void GestureSequence::AppendShowPressGestureEvent() { | |
1248 const GesturePoint* point = GetPointByPointId(0); | |
1249 scoped_ptr<GestureEvent> gesture(CreateGestureEvent( | |
1250 GestureEventDetails(ui::ET_GESTURE_SHOW_PRESS, 0, 0), | |
1251 point->first_touch_position(), | |
1252 flags_, | |
1253 base::Time::FromDoubleT(point->last_touch_time()), | |
1254 point->touch_id())); | |
1255 delegate_->DispatchPostponedGestureEvent(gesture.get()); | |
1256 } | |
1257 | |
1258 void GestureSequence::AppendLongTapGestureEvent(const GesturePoint& point, | |
1259 Gestures* gestures) { | |
1260 gestures->push_back(CreateGestureEvent( | |
1261 GestureEventDetails(ui::ET_GESTURE_LONG_TAP, 0, 0), | |
1262 point.enclosing_rectangle().CenterPoint(), | |
1263 flags_, | |
1264 base::Time::FromDoubleT(point.last_touch_time()), | |
1265 point.touch_id())); | |
1266 } | |
1267 | |
1268 bool GestureSequence::ScrollEnd(const TouchEvent& event, | |
1269 GesturePoint& point, | |
1270 Gestures* gestures) { | |
1271 DCHECK(state_ == GS_SCROLL); | |
1272 if (point.IsInFlickWindow(event)) { | |
1273 AppendScrollGestureEnd(point, | |
1274 point.last_touch_position(), | |
1275 gestures, | |
1276 point.XVelocity(), point.YVelocity()); | |
1277 } else { | |
1278 AppendScrollGestureEnd(point, | |
1279 point.last_touch_position(), | |
1280 gestures, 0.f, 0.f); | |
1281 } | |
1282 return true; | |
1283 } | |
1284 | |
1285 bool GestureSequence::PinchStart(const TouchEvent& event, | |
1286 const GesturePoint& point, | |
1287 Gestures* gestures) { | |
1288 DCHECK(state_ == GS_SCROLL || | |
1289 state_ == GS_PENDING_TWO_FINGER_TAP || | |
1290 state_ == GS_PENDING_PINCH); | |
1291 | |
1292 // Once pinch starts, we immediately break rail scroll. | |
1293 scroll_type_ = ST_FREE; | |
1294 | |
1295 const GesturePoint* point1 = GetPointByPointId(0); | |
1296 const GesturePoint* point2 = GetPointByPointId(1); | |
1297 | |
1298 if (state_ == GS_PENDING_TWO_FINGER_TAP || | |
1299 state_ == GS_PENDING_PINCH) { | |
1300 AppendScrollGestureBegin(point, bounding_box_.CenterPoint(), gestures); | |
1301 } | |
1302 | |
1303 pinch_distance_current_ = BoundingBoxDiagonal(bounding_box_); | |
1304 pinch_distance_start_ = pinch_distance_current_; | |
1305 latest_multi_scroll_update_location_ = bounding_box_.CenterPoint(); | |
1306 AppendPinchGestureBegin(*point1, *point2, gestures); | |
1307 | |
1308 return true; | |
1309 } | |
1310 | |
1311 bool GestureSequence::PinchUpdate(const TouchEvent& event, | |
1312 GesturePoint& point, | |
1313 Gestures* gestures) { | |
1314 static double min_pinch_update_distance = | |
1315 CommandLine::ForCurrentProcess()->HasSwitch( | |
1316 switches::kCompensateForUnstablePinchZoom) | |
1317 ? GestureConfiguration::min_pinch_update_distance_in_pixels() | |
1318 : 0; | |
1319 DCHECK(state_ == GS_PINCH); | |
1320 | |
1321 // It is possible that the none of the touch-points changed their position, | |
1322 // but their radii changed, and that caused the bounding box to also change. | |
1323 // But in such cases, we do not want to either pinch or scroll. | |
1324 // To avoid small jiggles, it is also necessary to make sure that at least one | |
1325 // of the fingers moved enough before a pinch or scroll update is created. | |
1326 bool did_scroll = false; | |
1327 for (int i = 0; i < kMaxGesturePoints; ++i) { | |
1328 if (!points_[i].in_use() || !points_[i].DidScroll(event, 0)) | |
1329 continue; | |
1330 did_scroll = true; | |
1331 break; | |
1332 } | |
1333 | |
1334 if (!did_scroll) | |
1335 return false; | |
1336 | |
1337 float distance = BoundingBoxDiagonal(bounding_box_); | |
1338 | |
1339 if (std::abs(distance - pinch_distance_current_) >= | |
1340 min_pinch_update_distance) { | |
1341 AppendPinchGestureUpdate(point, | |
1342 distance / pinch_distance_current_, gestures); | |
1343 pinch_distance_current_ = distance; | |
1344 } | |
1345 AppendScrollGestureUpdate(point, gestures, FS_NOT_FIRST_SCROLL); | |
1346 | |
1347 return true; | |
1348 } | |
1349 | |
1350 bool GestureSequence::PinchEnd(const TouchEvent& event, | |
1351 const GesturePoint& point, | |
1352 Gestures* gestures) { | |
1353 DCHECK(state_ == GS_PINCH); | |
1354 | |
1355 GesturePoint* point1 = GetPointByPointId(0); | |
1356 GesturePoint* point2 = GetPointByPointId(1); | |
1357 | |
1358 float distance = BoundingBoxDiagonal(bounding_box_); | |
1359 AppendPinchGestureEnd(*point1, *point2, | |
1360 distance / pinch_distance_start_, gestures); | |
1361 | |
1362 pinch_distance_start_ = 0; | |
1363 pinch_distance_current_ = 0; | |
1364 return true; | |
1365 } | |
1366 | |
1367 bool GestureSequence::MaybeSwipe(const TouchEvent& event, | |
1368 const GesturePoint& point, | |
1369 Gestures* gestures) { | |
1370 DCHECK(state_ == GS_PINCH); | |
1371 float velocity_x = 0.f, velocity_y = 0.f; | |
1372 bool swipe_x = true, swipe_y = true; | |
1373 int sign_x = 0, sign_y = 0; | |
1374 int i = 0; | |
1375 | |
1376 for (i = 0; i < kMaxGesturePoints; ++i) { | |
1377 if (points_[i].in_use()) | |
1378 break; | |
1379 } | |
1380 DCHECK(i < kMaxGesturePoints); | |
1381 | |
1382 velocity_x = points_[i].XVelocity(); | |
1383 velocity_y = points_[i].YVelocity(); | |
1384 sign_x = velocity_x < 0.f ? -1 : 1; | |
1385 sign_y = velocity_y < 0.f ? -1 : 1; | |
1386 | |
1387 for (++i; i < kMaxGesturePoints; ++i) { | |
1388 if (!points_[i].in_use()) | |
1389 continue; | |
1390 | |
1391 if (sign_x * points_[i].XVelocity() < 0) | |
1392 swipe_x = false; | |
1393 | |
1394 if (sign_y * points_[i].YVelocity() < 0) | |
1395 swipe_y = false; | |
1396 | |
1397 velocity_x += points_[i].XVelocity(); | |
1398 velocity_y += points_[i].YVelocity(); | |
1399 } | |
1400 | |
1401 float min_velocity = GestureConfiguration::min_swipe_speed(); | |
1402 | |
1403 velocity_x = fabs(velocity_x / point_count_); | |
1404 velocity_y = fabs(velocity_y / point_count_); | |
1405 if (velocity_x < min_velocity) | |
1406 swipe_x = false; | |
1407 if (velocity_y < min_velocity) | |
1408 swipe_y = false; | |
1409 | |
1410 if (!swipe_x && !swipe_y) | |
1411 return false; | |
1412 | |
1413 if (!swipe_x) | |
1414 velocity_x = 0.001f; | |
1415 if (!swipe_y) | |
1416 velocity_y = 0.001f; | |
1417 | |
1418 float ratio = velocity_x < velocity_y ? velocity_x / velocity_y : | |
1419 velocity_y / velocity_x; | |
1420 float angle = atan(ratio) * 180.0f / static_cast<float>(M_PI); | |
1421 | |
1422 if (angle > GestureConfiguration::max_swipe_deviation_angle()) | |
1423 return false; | |
1424 | |
1425 if (velocity_x > velocity_y) | |
1426 sign_y = 0; | |
1427 else | |
1428 sign_x = 0; | |
1429 | |
1430 AppendSwipeGesture(point, sign_x, sign_y, gestures); | |
1431 | |
1432 return true; | |
1433 } | |
1434 | |
1435 void GestureSequence::TwoFingerTapOrPinch(const TouchEvent& event, | |
1436 const GesturePoint& point, | |
1437 Gestures* gestures) { | |
1438 if (IsSecondTouchDownCloseEnoughForTwoFingerTap()) { | |
1439 TwoFingerTouchDown(event, point, gestures); | |
1440 set_state(GS_PENDING_TWO_FINGER_TAP); | |
1441 } else { | |
1442 set_state(GS_PENDING_PINCH); | |
1443 } | |
1444 } | |
1445 | |
1446 | |
1447 void GestureSequence::StopTimersIfRequired(const TouchEvent& event) { | |
1448 if ((!GetLongPressTimer()->IsRunning() && | |
1449 !GetShowPressTimer()->IsRunning()) || | |
1450 event.type() != ui::ET_TOUCH_MOVED) | |
1451 return; | |
1452 | |
1453 // Since a timer is running, there should be a non-NULL point. | |
1454 const GesturePoint* point = GetPointByPointId(0); | |
1455 if (!point->IsInsideTouchSlopRegion(event)) { | |
1456 GetLongPressTimer()->Stop(); | |
1457 GetShowPressTimer()->Stop(); | |
1458 } | |
1459 } | |
1460 | |
1461 void GestureSequence::StartRailFreeScroll(const GesturePoint& point, | |
1462 Gestures* gestures) { | |
1463 AppendScrollGestureBegin(point, point.first_touch_position(), gestures); | |
1464 scroll_type_ = ST_FREE; | |
1465 set_state(GS_SCROLL); | |
1466 } | |
1467 | |
1468 } // namespace ui | |
OLD | NEW |