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

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

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