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

Side by Side Diff: ui/events/gesture_detection/gesture_detector.cc

Issue 128613003: [Tracking Patch] Unified gesture detection (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Cleanup Created 6 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
OLDNEW
(Empty)
1 // Copyright 2013 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 #include "ui/events/gesture_detection/gesture_detector.h"
6
7 #include "base/timer/timer.h"
8 #include "ui/events/gesture_detection/motion_event.h"
9
10 namespace ui {
11 namespace {
12
13 // Constants used by TimeoutGestureHandler.
14 enum TimeoutEvent {
15 SHOW_PRESS = 0,
16 LONG_PRESS,
17 TAP,
18 TIMEOUT_EVENT_COUNT
19 };
20
21 } // namespace
22
23 GestureDetector::Config::Config()
24 : longpress_timeout(base::TimeDelta::FromMilliseconds(500)),
25 tap_timeout(base::TimeDelta::FromMilliseconds(180)),
26 double_tap_timeout(base::TimeDelta::FromMilliseconds(300)),
27 scaled_touch_slop(8),
28 scaled_double_tap_slop(100),
29 scaled_minimum_fling_velocity(50),
30 scaled_maximum_fling_velocity(8000) {}
31
32 GestureDetector::Config::~Config() {}
33
34 bool GestureDetector::SimpleOnGestureListener::OnDown(const MotionEvent& e) {
35 return false;
36 }
37
38 void GestureDetector::SimpleOnGestureListener::OnShowPress(
39 const MotionEvent& e) {}
40
41 bool GestureDetector::SimpleOnGestureListener::OnSingleTapUp(
42 const MotionEvent& e) {
43 return false;
44 }
45
46 bool GestureDetector::SimpleOnGestureListener::OnLongPress(
47 const MotionEvent& e) {
48 return false;
49 }
50
51 bool GestureDetector::SimpleOnGestureListener::OnScroll(const MotionEvent& e1,
52 const MotionEvent& e2,
53 float distance_x,
54 float distance_y) {
55 return false;
56 }
57
58 bool GestureDetector::SimpleOnGestureListener::OnFling(const MotionEvent& e1,
59 const MotionEvent& e2,
60 float velocity_x,
61 float velocity_y) {
62 return false;
63 }
64
65 bool GestureDetector::SimpleOnGestureListener::OnSingleTapConfirmed(
66 const MotionEvent& e) {
67 return false;
68 }
69
70 bool GestureDetector::SimpleOnGestureListener::OnDoubleTap(
71 const MotionEvent& e) {
72 return false;
73 }
74
75 bool GestureDetector::SimpleOnGestureListener::OnDoubleTapEvent(
76 const MotionEvent& e) {
77 return false;
78 }
79
80 class GestureDetector::TimeoutGestureHandler {
81 public:
82 typedef void (GestureDetector::*ReceiverMethod)();
83
84 TimeoutGestureHandler(const Config& config, GestureDetector* gesture_detector)
85 : gesture_detector_(gesture_detector) {
86 timeout_callbacks_[SHOW_PRESS] = &GestureDetector::OnShowPressTimeout;
87 timeout_delays_[SHOW_PRESS] = config.tap_timeout;
88
89 timeout_callbacks_[LONG_PRESS] = &GestureDetector::OnLongPressTimeout;
90 timeout_delays_[LONG_PRESS] = config.longpress_timeout + config.tap_timeout;
91
92 timeout_callbacks_[TAP] = &GestureDetector::OnTapTimeout;
93 timeout_delays_[TAP] = config.double_tap_timeout;
94 }
95
96 void StartTimeout(TimeoutEvent event) {
97 timeout_timers_[event].Start(FROM_HERE, timeout_delays_[event],
98 gesture_detector_, timeout_callbacks_[event]);
99 }
100
101 void StopTimeout(TimeoutEvent event) {
102 timeout_timers_[event].Stop();
103 }
104
105 void Stop() {
106 for (size_t i = SHOW_PRESS; i < TIMEOUT_EVENT_COUNT; ++i)
107 timeout_timers_[i].Stop();
108 }
109
110 bool HasTimeout(TimeoutEvent event) const {
111 return timeout_timers_[event].IsRunning();
112 }
113
114 private:
115 GestureDetector* const gesture_detector_;
116 base::OneShotTimer<GestureDetector> timeout_timers_[TIMEOUT_EVENT_COUNT];
117 ReceiverMethod timeout_callbacks_[TIMEOUT_EVENT_COUNT];
118 base::TimeDelta timeout_delays_[TIMEOUT_EVENT_COUNT];
119 };
120
121
122 GestureDetector::GestureDetector(
123 const Config& config,
124 OnGestureListener* listener,
125 OnDoubleTapListener* optional_double_tap_listener)
126 : timeout_handler_(new TimeoutGestureHandler(config, this)),
127 listener_(listener),
128 double_tap_listener_(optional_double_tap_listener),
129 touch_slop_square_(0),
130 double_tap_touch_slop_square_(0),
131 double_tap_slop_square_(0),
132 min_fling_velocity_(1),
133 max_fling_velocity_(1),
134 still_down_(false),
135 defer_confirm_single_tap_(false),
136 in_long_press_(false),
137 always_in_tap_region_(false),
138 always_in_bigger_tap_region_(false),
139 is_double_tapping_(false),
140 last_focus_x_(0),
141 last_focus_y_(0),
142 down_focus_x_(0),
143 down_focus_y_(0),
144 is_longpress_enabled_(true) {
145 DCHECK(listener_);
146 Init(config);
147 }
148
149 GestureDetector::~GestureDetector() {}
150
151 bool GestureDetector::OnTouchEvent(const MotionEvent& ev) {
152 const MotionEvent::Action action = ev.GetAction();
153
154 velocity_tracker_.AddMovement(ev);
155
156 const bool pointer_up = action == MotionEvent::ACTION_POINTER_UP;
157 const int skip_index = pointer_up ? ev.GetActionIndex() : -1;
158
159 // Determine focal point
160 float sum_x = 0, sum_y = 0;
161 const int count = ev.GetPointerCount();
162 for (int i = 0; i < count; i++) {
163 if (skip_index == i)
164 continue;
165 sum_x += ev.GetX(i);
166 sum_y += ev.GetY(i);
167 }
168 const int div = pointer_up ? count - 1 : count;
169 const float focus_x = sum_x / div;
170 const float focus_y = sum_y / div;
171
172 bool handled = false;
173
174 switch (action) {
175 case MotionEvent::ACTION_POINTER_DOWN:
176 down_focus_x_ = last_focus_x_ = focus_x;
177 down_focus_y_ = last_focus_y_ = focus_y;
178 // Cancel long press and taps
179 CancelTaps();
180 break;
181
182 case MotionEvent::ACTION_POINTER_UP:
183 down_focus_x_ = last_focus_x_ = focus_x;
184 down_focus_y_ = last_focus_y_ = focus_y;
185
186 // Check the dot product of current velocities.
187 // If the pointer that left was opposing another velocity vector, clear.
188 velocity_tracker_.ComputeCurrentVelocity(1000, max_fling_velocity_);
189 {
190 const int upIndex = ev.GetActionIndex();
191 const int id1 = ev.GetPointerId(upIndex);
192 const float x1 = velocity_tracker_.GetXVelocity(id1);
193 const float y1 = velocity_tracker_.GetYVelocity(id1);
194 for (int i = 0; i < count; i++) {
195 if (i == upIndex)
196 continue;
197
198 const int id2 = ev.GetPointerId(i);
199 const float x = x1 * velocity_tracker_.GetXVelocity(id2);
200 const float y = y1 * velocity_tracker_.GetYVelocity(id2);
201
202 const float dot = x + y;
203 if (dot < 0) {
204 velocity_tracker_.Clear();
205 break;
206 }
207 }
208 }
209 break;
210
211 case MotionEvent::ACTION_DOWN:
212 if (double_tap_listener_) {
213 bool hadTapMessage = timeout_handler_->HasTimeout(TAP);
214 if (hadTapMessage) timeout_handler_->StopTimeout(TAP);
215 if (current_down_event_ && previous_up_event_ && hadTapMessage &&
216 IsConsideredDoubleTap(
217 *current_down_event_, *previous_up_event_, ev)) {
218 // This is a second tap
219 is_double_tapping_ = true;
220 // Give a callback with the first tap of the double-tap
221 handled |= double_tap_listener_->OnDoubleTap(*current_down_event_);
222 // Give a callback with down event of the double-tap
223 handled |= double_tap_listener_->OnDoubleTapEvent(ev);
224 } else {
225 // This is a first tap
226 timeout_handler_->StartTimeout(TAP);
227 }
228 }
229
230 down_focus_x_ = last_focus_x_ = focus_x;
231 down_focus_y_ = last_focus_y_ = focus_y;
232 current_down_event_ = ev.Clone();
233
234 always_in_tap_region_ = true;
235 always_in_bigger_tap_region_ = true;
236 still_down_ = true;
237 in_long_press_ = false;
238 defer_confirm_single_tap_ = false;
239
240 if (is_longpress_enabled_)
241 timeout_handler_->StartTimeout(LONG_PRESS);
242 timeout_handler_->StartTimeout(SHOW_PRESS);
243 handled |= listener_->OnDown(ev);
244 break;
245
246 case MotionEvent::ACTION_MOVE:
247 if (in_long_press_)
248 break;
249
250 {
251 const float scroll_x = last_focus_x_ - focus_x;
252 const float scroll_y = last_focus_y_ - focus_y;
253 if (is_double_tapping_) {
254 // Give the move events of the double-tap
255 handled |= double_tap_listener_->OnDoubleTapEvent(ev);
256 } else if (always_in_tap_region_) {
257 const int delta_x = (int)(focus_x - down_focus_x_);
258 const int delta_y = (int)(focus_y - down_focus_y_);
259 int distance = (delta_x * delta_x) + (delta_y * delta_y);
260 if (distance > touch_slop_square_) {
261 handled = listener_->OnScroll(*current_down_event_,
262 ev,
263 scroll_x,
264 scroll_y);
265 last_focus_x_ = focus_x;
266 last_focus_y_ = focus_y;
267 always_in_tap_region_ = false;
268 timeout_handler_->Stop();
269 }
270 if (distance > double_tap_touch_slop_square_)
271 always_in_bigger_tap_region_ = false;
272 } else if ((std::abs(scroll_x) >= 1) || (std::abs(scroll_y) >= 1)) {
273 handled = listener_->OnScroll(*current_down_event_,
274 ev,
275 scroll_x,
276 scroll_y);
277 last_focus_x_ = focus_x;
278 last_focus_y_ = focus_y;
279 }
280
281 }
282 break;
283
284 case MotionEvent::ACTION_UP:
285 still_down_ = false;
286 {
287 if (is_double_tapping_) {
288 // Finally, give the up event of the double-tap
289 handled |= double_tap_listener_->OnDoubleTapEvent(ev);
290 } else if (in_long_press_) {
291 timeout_handler_->StopTimeout(TAP);
292 in_long_press_ = false;
293 } else if (always_in_tap_region_) {
294 handled = listener_->OnSingleTapUp(ev);
295 if (defer_confirm_single_tap_ && double_tap_listener_ != NULL) {
296 double_tap_listener_->OnSingleTapConfirmed(ev);
297 }
298 } else {
299
300 // A fling must travel the minimum tap distance
301 const int pointer_id = ev.GetPointerId(0);
302 velocity_tracker_.ComputeCurrentVelocity(1000, max_fling_velocity_);
303 const float velocity_y = velocity_tracker_.GetYVelocity(pointer_id);
304 const float velocity_x = velocity_tracker_.GetXVelocity(pointer_id);
305
306 if ((std::abs(velocity_y) > min_fling_velocity_) ||
307 (std::abs(velocity_x) > min_fling_velocity_)) {
308 handled = listener_->OnFling(
309 *current_down_event_, ev, velocity_x, velocity_y);
310 }
311 }
312
313 previous_up_event_ = ev.Clone();
314
315 velocity_tracker_.Clear();
316 is_double_tapping_ = false;
317 defer_confirm_single_tap_ = false;
318 timeout_handler_->StopTimeout(SHOW_PRESS);
319 timeout_handler_->StopTimeout(LONG_PRESS);
320 }
321 break;
322
323 case MotionEvent::ACTION_CANCEL:
324 Cancel();
325 break;
326 }
327
328 return handled;
329 }
330
331 void GestureDetector::Init(Config config) {
332 DCHECK(listener_);
333
334 const int touch_slop = config.scaled_touch_slop;
335 const int double_tap_touch_slop = touch_slop;
336 const int double_tap_slop = config.scaled_double_tap_slop;
337 min_fling_velocity_ = config.scaled_minimum_fling_velocity;
338 max_fling_velocity_ = config.scaled_maximum_fling_velocity;
339 touch_slop_square_ = touch_slop * touch_slop;
340 double_tap_touch_slop_square_ = double_tap_touch_slop * double_tap_touch_slop;
341 double_tap_slop_square_ = double_tap_slop * double_tap_slop;
342 double_tap_timeout_ = config.double_tap_timeout;
343 }
344
345 void GestureDetector::OnShowPressTimeout() {
346 listener_->OnShowPress(*current_down_event_);
347 }
348
349 void GestureDetector::OnLongPressTimeout() {
350 timeout_handler_->StopTimeout(TAP);
351 defer_confirm_single_tap_ = false;
352 in_long_press_ = listener_->OnLongPress(*current_down_event_);
353 }
354
355 void GestureDetector::OnTapTimeout() {
356 if (!double_tap_listener_)
357 return;
358 if (!still_down_)
359 double_tap_listener_->OnSingleTapConfirmed(*current_down_event_);
360 else
361 defer_confirm_single_tap_ = true;
362 }
363
364 void GestureDetector::Cancel() {
365 timeout_handler_->Stop();
366 velocity_tracker_.Clear();
367 is_double_tapping_ = false;
368 still_down_ = false;
369 always_in_tap_region_ = false;
370 always_in_bigger_tap_region_ = false;
371 defer_confirm_single_tap_ = false;
372 in_long_press_ = false;
373 }
374
375 void GestureDetector::CancelTaps() {
376 timeout_handler_->Stop();
377 is_double_tapping_ = false;
378 always_in_tap_region_ = false;
379 always_in_bigger_tap_region_ = false;
380 defer_confirm_single_tap_ = false;
381 in_long_press_ = false;
382 }
383
384 bool GestureDetector::IsConsideredDoubleTap(const MotionEvent& first_down,
385 const MotionEvent& first_up,
386 const MotionEvent& second_down) {
387 if (!always_in_bigger_tap_region_)
388 return false;
389
390 if (second_down.GetEventTime() - first_up.GetEventTime() >
391 double_tap_timeout_)
392 return false;
393
394 int delta_x = (int)first_down.GetX() - (int)second_down.GetX();
395 int delta_y = (int)first_down.GetY() - (int)second_down.GetY();
396 return (delta_x * delta_x + delta_y * delta_y < double_tap_slop_square_);
397 }
398
399 } // namespace ui
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698