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

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

Powered by Google App Engine
This is Rietveld 408576698