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

Side by Side Diff: content/browser/renderer_host/input/gestures/scale_gesture_detector.cc

Issue 128613003: [Tracking Patch] Unified gesture detection (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 11 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 "content/browser/renderer_host/input/gestures/scale_gesture_detector.h"
6
7 #include <limits.h>
8 #include <math.h>
9
10 #include "base/logging.h"
11 #include "content/browser/renderer_host/input/gestures/motion_event.h"
12
13 using base::TimeDelta;
14 using base::TimeTicks;
15
16 namespace content {
17 namespace {
18 const TimeDelta TOUCH_STABILIZE_TIME = TimeDelta::FromMilliseconds(128);
19 const float SCALE_FACTOR = .5f;
20 }
21
22 ScaleGestureDetector::Config::Config()
23 : quick_scale_enabled(false),
24 min_scaling_touch_major(1),
25 min_scaling_span(1) {}
26
27 ScaleGestureDetector::Config::~Config() {}
28
29 bool ScaleGestureDetector::SimpleOnScaleGestureListener::OnScale(
30 const ScaleGestureDetector&) {
31 return false;
32 }
33
34 bool ScaleGestureDetector::SimpleOnScaleGestureListener::OnScaleBegin(
35 const ScaleGestureDetector&) {
36 return true;
37 }
38
39 void ScaleGestureDetector::SimpleOnScaleGestureListener::OnScaleEnd(
40 const ScaleGestureDetector&) {}
41
42
43 ScaleGestureDetector::ScaleGestureDetector(Config config,
44 OnScaleGestureListener* listener)
45 : listener_(listener),
46 config_(config),
47 focus_x_(0),
48 focus_y_(0),
49 quick_scale_enabled_(false),
50 curr_span_(0),
51 prev_span_(0),
52 initial_span_(0),
53 curr_span_x_(0),
54 curr_span_y_(0),
55 prev_span_x_(0),
56 prev_span_y_(0),
57 in_progress_(0),
58 span_slop_(0),
59 min_span_(0),
60 touch_upper_(0),
61 touch_lower_(0),
62 touch_history_last_accepted_(0),
63 touch_history_direction_(0),
64 touch_min_major_(0),
65 double_tap_focus_x_(0),
66 double_tap_focus_y_(0),
67 double_tap_mode_(DOUBLE_TAP_MODE_NONE),
68 event_before_or_above_starting_gesture_event_(false) {
69 DCHECK(listener_);
70 span_slop_ = config.gesture_detector_config.scaled_touch_slop * 2;
71 touch_min_major_ = config.min_scaling_touch_major;
72 min_span_ = config.min_scaling_span;
73 SetQuickScaleEnabled(config.quick_scale_enabled);
74 }
75
76 ScaleGestureDetector::~ScaleGestureDetector() {}
77
78 bool ScaleGestureDetector::OnTouchEvent(const MotionEvent& event) {
79 curr_time_ = event.GetEventTime();
80
81 const int action = event.GetActionMasked();
82
83 // Forward the event to check for double tap gesture
84 if (quick_scale_enabled_) {
85 DCHECK(gesture_detector_);
86 gesture_detector_->OnTouchEvent(event);
87 }
88
89 const bool stream_complete = action == MotionEvent::ACTION_UP ||
90 action == MotionEvent::ACTION_CANCEL;
91
92 if (action == MotionEvent::ACTION_DOWN || stream_complete) {
93 // Reset any scale in progress with the listener.
94 // If it's an ACTION_DOWN we're beginning a new event stream.
95 // This means the app probably didn't give us all the events. Shame on it.
96 if (in_progress_) {
97 listener_->OnScaleEnd(*this);
98 in_progress_ = false;
99 initial_span_ = 0;
100 double_tap_mode_ = DOUBLE_TAP_MODE_NONE;
101 } else if (double_tap_mode_ == DOUBLE_TAP_MODE_IN_PROGRESS &&
102 stream_complete) {
103 in_progress_ = false;
104 initial_span_ = 0;
105 double_tap_mode_ = DOUBLE_TAP_MODE_NONE;
106 }
107
108 if (stream_complete) {
109 ClearTouchHistory();
110 return true;
111 }
112 }
113
114 const bool config_changed = action == MotionEvent::ACTION_DOWN ||
115 action == MotionEvent::ACTION_POINTER_UP ||
116 action == MotionEvent::ACTION_POINTER_DOWN;
117
118 const bool pionter_up = action == MotionEvent::ACTION_POINTER_UP;
119 const int skip_index = pionter_up ? event.GetActionIndex() : -1;
120
121 // Determine focal point
122 float sum_x = 0, sum_y = 0;
123 const int count = event.GetPointerCount();
124 const int div = pionter_up ? count - 1 : count;
125 float focus_x;
126 float focus_y;
127 if (double_tap_mode_ == DOUBLE_TAP_MODE_IN_PROGRESS) {
128 // In double tap mode, the focal pt is always where the double tap
129 // gesture started
130 focus_x = double_tap_focus_x_;
131 focus_y = double_tap_focus_y_;
132 if (event.GetY() < focus_y) {
133 event_before_or_above_starting_gesture_event_ = true;
134 } else {
135 event_before_or_above_starting_gesture_event_ = false;
136 }
137 } else {
138 for (int i = 0; i < count; i++) {
139 if (skip_index == i)
140 continue;
141 sum_x += event.GetX(i);
142 sum_y += event.GetY(i);
143 }
144
145 focus_x = sum_x / div;
146 focus_y = sum_y / div;
147 }
148
149 AddTouchHistory(event);
150
151 // Determine average deviation from focal point
152 float dev_sum_x = 0, dev_sum_y = 0;
153 for (int i = 0; i < count; i++) {
154 if (skip_index == i)
155 continue;
156
157 // Convert the resulting diameter into a radius.
158 const float touch_size = touch_history_last_accepted_ / 2;
159 dev_sum_x += std::abs(event.GetX(i) - focus_x) + touch_size;
160 dev_sum_y += std::abs(event.GetY(i) - focus_y) + touch_size;
161 }
162 const float dev_x = dev_sum_x / div;
163 const float dev_y = dev_sum_y / div;
164
165 // Span is the average distance between touch points through the focal point;
166 // i.e. the diameter of the circle with a radius of the average deviation from
167 // the focal point.
168 const float span_x = dev_x * 2;
169 const float span_y = dev_y * 2;
170 float span;
171 if (InDoubleTapMode()) {
172 span = span_y;
173 } else {
174 span = std::sqrt(span_x * span_x + span_y * span_y);
175 }
176
177 // Dispatch begin/end events as needed.
178 // If the configuration changes, notify the app to reset its current state by
179 // beginning a fresh scale event stream.
180 const bool was_in_progress = in_progress_;
181 focus_x_ = focus_x;
182 focus_y_ = focus_y;
183 if (!InDoubleTapMode() &&
184 in_progress_ &&
185 (span < min_span_ || config_changed)) {
186 listener_->OnScaleEnd(*this);
187 in_progress_ = false;
188 initial_span_ = span;
189 double_tap_mode_ = DOUBLE_TAP_MODE_NONE;
190 }
191 if (config_changed) {
192 prev_span_x_ = curr_span_x_ = span_x;
193 prev_span_y_ = curr_span_y_ = span_y;
194 initial_span_ = prev_span_ = curr_span_ = span;
195 }
196
197 const int min_span = InDoubleTapMode() ? span_slop_ : min_span_;
198 if (!in_progress_ && span >= min_span &&
199 (was_in_progress || std::abs(span - initial_span_) > span_slop_)) {
200 prev_span_x_ = curr_span_x_ = span_x;
201 prev_span_y_ = curr_span_y_ = span_y;
202 prev_span_ = curr_span_ = span;
203 prev_time_ = curr_time_;
204 in_progress_ = listener_->OnScaleBegin(*this);
205 }
206
207 // Handle motion; focal point and span/scale factor are changing.
208 if (action == MotionEvent::ACTION_MOVE) {
209 curr_span_x_ = span_x;
210 curr_span_y_ = span_y;
211 curr_span_ = span;
212
213 bool update_prev = true;
214
215 if (in_progress_) {
216 update_prev = listener_->OnScale(*this);
217 }
218
219 if (update_prev) {
220 prev_span_x_ = curr_span_x_;
221 prev_span_y_ = curr_span_y_;
222 prev_span_ = curr_span_;
223 prev_time_ = curr_time_;
224 }
225 }
226
227 return true;
228 }
229
230 void ScaleGestureDetector::SetQuickScaleEnabled(bool scales) {
231 quick_scale_enabled_ = scales;
232 if (quick_scale_enabled_ && !gesture_detector_) {
233 gesture_detector_.reset(
234 new GestureDetector(config_.gesture_detector_config, this, this));
235 }
236 }
237
238 bool ScaleGestureDetector::IsQuickScaleEnabled() const {
239 return quick_scale_enabled_;
240 }
241
242 bool ScaleGestureDetector::IsInProgress() const {
243 return in_progress_;
244 }
245
246 float ScaleGestureDetector::GetFocusX() const {
247 return focus_x_;
248 }
249
250 float ScaleGestureDetector::GetFocusY() const {
251 return focus_y_;
252 }
253
254 float ScaleGestureDetector::GetCurrentSpan() const {
255 return curr_span_;
256 }
257
258 float ScaleGestureDetector::GetCurrentSpanX() const {
259 return curr_span_x_;
260 }
261
262 float ScaleGestureDetector::GetCurrentSpanY() const {
263 return curr_span_y_;
264 }
265
266 float ScaleGestureDetector::GetPreviousSpan() const {
267 return prev_span_;
268 }
269
270 float ScaleGestureDetector::GetPreviousSpanX() const {
271 return prev_span_x_;
272 }
273
274 float ScaleGestureDetector::GetPreviousSpanY() const {
275 return prev_span_y_;
276 }
277
278 float ScaleGestureDetector::GetScaleFactor() const {
279 if (InDoubleTapMode()) {
280 // Drag is moving up; the further away from the gesture
281 // start, the smaller the span should be, the closer,
282 // the larger the span, and therefore the larger the scale
283 const bool scale_up =
284 (event_before_or_above_starting_gesture_event_ &&
285 (curr_span_ < prev_span_)) ||
286 (!event_before_or_above_starting_gesture_event_ &&
287 (curr_span_ > prev_span_));
288 const float span_diff =
289 (std::abs(1.f - (curr_span_ / prev_span_)) * SCALE_FACTOR);
290 return prev_span_ <= 0 ?
291 1.f : (scale_up ? (1.f + span_diff) : (1.f - span_diff));
292 }
293 return prev_span_ > 0 ? curr_span_ / prev_span_ : 1;
294 }
295
296 base::TimeDelta ScaleGestureDetector::GetTimeDelta() const {
297 return curr_time_ - prev_time_;
298 }
299
300 base::TimeTicks ScaleGestureDetector::GetEventTime() const {
301 return curr_time_;
302 }
303
304 bool ScaleGestureDetector::OnDoubleTap(const MotionEvent& ev) {
305 // Double tap: start watching for a swipe
306 double_tap_focus_x_ = ev.GetX();
307 double_tap_focus_y_ = ev.GetY();
308 double_tap_mode_ = DOUBLE_TAP_MODE_IN_PROGRESS;
309 return true;
310 }
311
312 void ScaleGestureDetector::AddTouchHistory(const MotionEvent& ev) {
313 const base::TimeTicks current_time = base::TimeTicks::Now();
314 const int count = ev.GetPointerCount();
315 bool accept =
316 current_time - touch_history_last_accepted_time_ >= TOUCH_STABILIZE_TIME;
317 float total = 0;
318 int sample_count = 0;
319 for (int i = 0; i < count; i++) {
320 const bool has_last_accepted = !std::isnan(touch_history_last_accepted_);
321 const int history_size = ev.GetHistorySize();
322 const int pointersample_count = history_size + 1;
323 for (int h = 0; h < pointersample_count; h++) {
324 float major;
325 if (h < history_size) {
326 major = ev.GetHistoricalTouchMajor(i, h);
327 } else {
328 major = ev.GetTouchMajor(i);
329 }
330 if (major < touch_min_major_) major = touch_min_major_;
331 total += major;
332
333 if (std::isnan(touch_upper_) || major > touch_upper_) {
334 touch_upper_ = major;
335 }
336 if (std::isnan(touch_lower_) || major < touch_lower_) {
337 touch_lower_ = major;
338 }
339
340 if (has_last_accepted) {
341 const float majorDelta = major - touch_history_last_accepted_;
342 const int direction_sig =
343 majorDelta > 0 ? 1 : (majorDelta < 0 ? -1 : 0);
344 if (direction_sig != touch_history_direction_ ||
345 (direction_sig == 0 && touch_history_direction_ == 0)) {
346 touch_history_direction_ = direction_sig;
347 touch_history_last_accepted_time_ =
348 h < history_size ? ev.GetHistoricalEventTime(h)
349 : ev.GetEventTime();
350 accept = false;
351 }
352 }
353 }
354 sample_count += pointersample_count;
355 }
356
357 const float avg = total / sample_count;
358
359 if (accept) {
360 float newAccepted = (touch_upper_ + touch_lower_ + avg) / 3;
361 touch_upper_ = (touch_upper_ + newAccepted) / 2;
362 touch_lower_ = (touch_lower_ + newAccepted) / 2;
363 touch_history_last_accepted_ = newAccepted;
364 touch_history_direction_ = 0;
365 touch_history_last_accepted_time_ = ev.GetEventTime();
366 }
367 }
368
369 void ScaleGestureDetector::ClearTouchHistory() {
370 touch_upper_ = std::numeric_limits<float>::quiet_NaN();
371 touch_lower_ = std::numeric_limits<float>::quiet_NaN();
372 touch_history_last_accepted_ = std::numeric_limits<float>::quiet_NaN();
373 touch_history_direction_ = 0;
374 touch_history_last_accepted_time_ = base::TimeTicks();
375 }
376
377 bool ScaleGestureDetector::InDoubleTapMode() const {
378 return double_tap_mode_ == DOUBLE_TAP_MODE_IN_PROGRESS;
379 }
380
381 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698