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

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

Powered by Google App Engine
This is Rietveld 408576698