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

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

Powered by Google App Engine
This is Rietveld 408576698