| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/events/gesture_detection/scale_gesture_detector.h" | 5 #include "ui/events/gesture_detection/scale_gesture_detector.h" |
| 6 | 6 |
| 7 #include <limits.h> | 7 #include <limits.h> |
| 8 | 8 |
| 9 #include <algorithm> | 9 #include <algorithm> |
| 10 #include <cmath> | 10 #include <cmath> |
| 11 | 11 |
| 12 #include "base/logging.h" | 12 #include "base/logging.h" |
| 13 #include "ui/events/gesture_detection/motion_event.h" | 13 #include "ui/events/gesture_detection/motion_event.h" |
| 14 #include "ui/events/gesture_detection/scale_gesture_listeners.h" | 14 #include "ui/events/gesture_detection/scale_gesture_listeners.h" |
| 15 | 15 |
| 16 using base::TimeDelta; | 16 using base::TimeDelta; |
| 17 using base::TimeTicks; | 17 using base::TimeTicks; |
| 18 | 18 |
| 19 namespace ui { | 19 namespace ui { |
| 20 namespace { | 20 namespace { |
| 21 | 21 |
| 22 // Using a small epsilon when comparing slop distances allows pixel perfect | 22 // Using a small epsilon when comparing slop distances allows pixel perfect |
| 23 // slop determination when using fractional DPI coordinates (assuming the slop | 23 // slop determination when using fractional DPI coordinates (assuming the slop |
| 24 // region and DPI scale are reasonably proportioned). | 24 // region and DPI scale are reasonably proportioned). |
| 25 const float kSlopEpsilon = .05f; | 25 const float kSlopEpsilon = .05f; |
| 26 | 26 |
| 27 const int kTouchStabilizeTimeMs = 128; | |
| 28 | |
| 29 const float kScaleFactor = .5f; | 27 const float kScaleFactor = .5f; |
| 30 | 28 |
| 31 } // namespace | 29 } // namespace |
| 32 | 30 |
| 33 // Note: These constants were taken directly from the default (unscaled) | 31 // Note: These constants were taken directly from the default (unscaled) |
| 34 // versions found in Android's ViewConfiguration. Do not change these default | 32 // versions found in Android's ViewConfiguration. Do not change these default |
| 35 // values without explicitly consulting an OWNER. | 33 // values without explicitly consulting an OWNER. |
| 36 ScaleGestureDetector::Config::Config() | 34 ScaleGestureDetector::Config::Config() |
| 37 : span_slop(16), | 35 : span_slop(16), |
| 38 min_scaling_touch_major(48), | |
| 39 min_scaling_span(200), | 36 min_scaling_span(200), |
| 40 min_pinch_update_span_delta(0), | 37 min_pinch_update_span_delta(0), |
| 41 stylus_scale_enabled(false) {} | 38 stylus_scale_enabled(false) {} |
| 42 | 39 |
| 43 ScaleGestureDetector::Config::~Config() {} | 40 ScaleGestureDetector::Config::~Config() {} |
| 44 | 41 |
| 45 ScaleGestureDetector::ScaleGestureDetector(const Config& config, | 42 ScaleGestureDetector::ScaleGestureDetector(const Config& config, |
| 46 ScaleGestureListener* listener) | 43 ScaleGestureListener* listener) |
| 47 : listener_(listener), | 44 : listener_(listener), |
| 48 stylus_scale_enabled_(config.stylus_scale_enabled), | 45 stylus_scale_enabled_(config.stylus_scale_enabled), |
| 49 focus_x_(0), | 46 focus_x_(0), |
| 50 focus_y_(0), | 47 focus_y_(0), |
| 51 curr_span_(0), | 48 curr_span_(0), |
| 52 prev_span_(0), | 49 prev_span_(0), |
| 53 initial_span_(0), | 50 initial_span_(0), |
| 54 curr_span_x_(0), | 51 curr_span_x_(0), |
| 55 curr_span_y_(0), | 52 curr_span_y_(0), |
| 56 prev_span_x_(0), | 53 prev_span_x_(0), |
| 57 prev_span_y_(0), | 54 prev_span_y_(0), |
| 58 in_progress_(0), | 55 in_progress_(0), |
| 59 span_slop_(0), | 56 span_slop_(0), |
| 60 min_span_(0), | 57 min_span_(0), |
| 61 touch_upper_(0), | |
| 62 touch_lower_(0), | |
| 63 touch_history_last_accepted_(0), | |
| 64 touch_history_direction_(0), | |
| 65 touch_min_major_(0), | |
| 66 touch_max_major_(0), | |
| 67 anchored_scale_start_x_(0), | 58 anchored_scale_start_x_(0), |
| 68 anchored_scale_start_y_(0), | 59 anchored_scale_start_y_(0), |
| 69 anchored_scale_mode_(ANCHORED_SCALE_MODE_NONE), | 60 anchored_scale_mode_(ANCHORED_SCALE_MODE_NONE), |
| 70 event_before_or_above_starting_gesture_event_(false) { | 61 event_before_or_above_starting_gesture_event_(false) { |
| 71 DCHECK(listener_); | 62 DCHECK(listener_); |
| 72 span_slop_ = config.span_slop + kSlopEpsilon; | 63 span_slop_ = config.span_slop + kSlopEpsilon; |
| 73 touch_min_major_ = config.min_scaling_touch_major; | |
| 74 touch_max_major_ = std::min(config.min_scaling_span / std::sqrt(2.f), | |
| 75 2.f * touch_min_major_); | |
| 76 min_span_ = config.min_scaling_span + kSlopEpsilon; | 64 min_span_ = config.min_scaling_span + kSlopEpsilon; |
| 77 ResetTouchHistory(); | |
| 78 } | 65 } |
| 79 | 66 |
| 80 ScaleGestureDetector::~ScaleGestureDetector() {} | 67 ScaleGestureDetector::~ScaleGestureDetector() {} |
| 81 | 68 |
| 82 bool ScaleGestureDetector::OnTouchEvent(const MotionEvent& event) { | 69 bool ScaleGestureDetector::OnTouchEvent(const MotionEvent& event) { |
| 83 curr_time_ = event.GetEventTime(); | 70 curr_time_ = event.GetEventTime(); |
| 84 | 71 |
| 85 const int action = event.GetAction(); | 72 const int action = event.GetAction(); |
| 86 | 73 |
| 87 const int count = static_cast<int>(event.GetPointerCount()); | 74 const int count = static_cast<int>(event.GetPointerCount()); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 101 // Reset any scale in progress with the listener. | 88 // Reset any scale in progress with the listener. |
| 102 // If it's an ACTION_DOWN we're beginning a new event stream. | 89 // If it's an ACTION_DOWN we're beginning a new event stream. |
| 103 // This means the app probably didn't give us all the events. Shame on it. | 90 // This means the app probably didn't give us all the events. Shame on it. |
| 104 if (in_progress_) { | 91 if (in_progress_) { |
| 105 listener_->OnScaleEnd(*this, event); | 92 listener_->OnScaleEnd(*this, event); |
| 106 ResetScaleWithSpan(0); | 93 ResetScaleWithSpan(0); |
| 107 } else if (InAnchoredScaleMode() && stream_complete) { | 94 } else if (InAnchoredScaleMode() && stream_complete) { |
| 108 ResetScaleWithSpan(0); | 95 ResetScaleWithSpan(0); |
| 109 } | 96 } |
| 110 | 97 |
| 111 if (stream_complete) { | 98 if (stream_complete) |
| 112 ResetTouchHistory(); | |
| 113 return true; | 99 return true; |
| 114 } | |
| 115 } | 100 } |
| 116 | 101 |
| 117 if (!in_progress_ && stylus_scale_enabled_ && !InAnchoredScaleMode() && | 102 if (!in_progress_ && stylus_scale_enabled_ && !InAnchoredScaleMode() && |
| 118 !stream_complete && is_stylus_button_down) { | 103 !stream_complete && is_stylus_button_down) { |
| 119 // Start of a stylus scale gesture. | 104 // Start of a stylus scale gesture. |
| 120 anchored_scale_start_x_ = event.GetX(); | 105 anchored_scale_start_x_ = event.GetX(); |
| 121 anchored_scale_start_y_ = event.GetY(); | 106 anchored_scale_start_y_ = event.GetY(); |
| 122 anchored_scale_mode_ = ANCHORED_SCALE_MODE_STYLUS; | 107 anchored_scale_mode_ = ANCHORED_SCALE_MODE_STYLUS; |
| 123 initial_span_ = 0; | 108 initial_span_ = 0; |
| 124 } | 109 } |
| (...skipping 28 matching lines...) Expand all Loading... |
| 153 if (skip_index == i) | 138 if (skip_index == i) |
| 154 continue; | 139 continue; |
| 155 sum_x += event.GetX(i); | 140 sum_x += event.GetX(i); |
| 156 sum_y += event.GetY(i); | 141 sum_y += event.GetY(i); |
| 157 } | 142 } |
| 158 | 143 |
| 159 focus_x = sum_x * inverse_unreleased_point_count; | 144 focus_x = sum_x * inverse_unreleased_point_count; |
| 160 focus_y = sum_y * inverse_unreleased_point_count; | 145 focus_y = sum_y * inverse_unreleased_point_count; |
| 161 } | 146 } |
| 162 | 147 |
| 163 AddTouchHistory(event); | |
| 164 | |
| 165 // Determine average deviation from focal point. | 148 // Determine average deviation from focal point. |
| 166 float dev_sum_x = 0, dev_sum_y = 0; | 149 float dev_sum_x = 0, dev_sum_y = 0; |
| 167 for (int i = 0; i < count; i++) { | 150 for (int i = 0; i < count; i++) { |
| 168 if (skip_index == i) | 151 if (skip_index == i) |
| 169 continue; | 152 continue; |
| 170 | 153 |
| 171 dev_sum_x += std::abs(event.GetX(i) - focus_x); | 154 dev_sum_x += std::abs(event.GetX(i) - focus_x); |
| 172 dev_sum_y += std::abs(event.GetY(i) - focus_y); | 155 dev_sum_y += std::abs(event.GetY(i) - focus_y); |
| 173 } | 156 } |
| 174 // Convert the resulting diameter into a radius, to include touch | |
| 175 // radius in overall deviation. | |
| 176 const float touch_radius = touch_history_last_accepted_ / 2; | |
| 177 | 157 |
| 178 const float dev_x = dev_sum_x * inverse_unreleased_point_count + touch_radius; | 158 const float dev_x = dev_sum_x * inverse_unreleased_point_count; |
| 179 const float dev_y = dev_sum_y * inverse_unreleased_point_count + touch_radius; | 159 const float dev_y = dev_sum_y * inverse_unreleased_point_count; |
| 180 | 160 |
| 181 // Span is the average distance between touch points through the focal point; | 161 // Span is the average distance between touch points through the focal point; |
| 182 // i.e. the diameter of the circle with a radius of the average deviation from | 162 // i.e. the diameter of the circle with a radius of the average deviation from |
| 183 // the focal point. | 163 // the focal point. |
| 184 const float span_x = dev_x * 2; | 164 const float span_x = dev_x * 2; |
| 185 const float span_y = dev_y * 2; | 165 const float span_y = dev_y * 2; |
| 186 float span; | 166 float span; |
| 187 if (InAnchoredScaleMode()) { | 167 if (InAnchoredScaleMode()) { |
| 188 span = span_y; | 168 span = span_y; |
| 189 } else { | 169 } else { |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 288 } | 268 } |
| 289 | 269 |
| 290 bool ScaleGestureDetector::OnDoubleTap(const MotionEvent& ev) { | 270 bool ScaleGestureDetector::OnDoubleTap(const MotionEvent& ev) { |
| 291 // Double tap: start watching for a swipe. | 271 // Double tap: start watching for a swipe. |
| 292 anchored_scale_start_x_ = ev.GetX(); | 272 anchored_scale_start_x_ = ev.GetX(); |
| 293 anchored_scale_start_y_ = ev.GetY(); | 273 anchored_scale_start_y_ = ev.GetY(); |
| 294 anchored_scale_mode_ = ANCHORED_SCALE_MODE_DOUBLE_TAP; | 274 anchored_scale_mode_ = ANCHORED_SCALE_MODE_DOUBLE_TAP; |
| 295 return true; | 275 return true; |
| 296 } | 276 } |
| 297 | 277 |
| 298 void ScaleGestureDetector::AddTouchHistory(const MotionEvent& ev) { | |
| 299 const base::TimeTicks current_time = ev.GetEventTime(); | |
| 300 DCHECK(!current_time.is_null()); | |
| 301 const int count = static_cast<int>(ev.GetPointerCount()); | |
| 302 bool accept = touch_history_last_accepted_time_.is_null() || | |
| 303 (current_time - touch_history_last_accepted_time_) >= | |
| 304 base::TimeDelta::FromMilliseconds(kTouchStabilizeTimeMs); | |
| 305 float total = 0; | |
| 306 int sample_count = 0; | |
| 307 for (int i = 0; i < count; i++) { | |
| 308 const bool has_last_accepted = !std::isnan(touch_history_last_accepted_); | |
| 309 const int history_size = static_cast<int>(ev.GetHistorySize()); | |
| 310 const int pointersample_count = history_size + 1; | |
| 311 for (int h = 0; h < pointersample_count; h++) { | |
| 312 float major; | |
| 313 if (h < history_size) { | |
| 314 major = ev.GetHistoricalTouchMajor(i, h); | |
| 315 } else { | |
| 316 major = ev.GetTouchMajor(i); | |
| 317 } | |
| 318 if (major < touch_min_major_) | |
| 319 major = touch_min_major_; | |
| 320 if (major > touch_max_major_) | |
| 321 major = touch_max_major_; | |
| 322 total += major; | |
| 323 | |
| 324 if (std::isnan(touch_upper_) || major > touch_upper_) { | |
| 325 touch_upper_ = major; | |
| 326 } | |
| 327 if (std::isnan(touch_lower_) || major < touch_lower_) { | |
| 328 touch_lower_ = major; | |
| 329 } | |
| 330 | |
| 331 if (has_last_accepted) { | |
| 332 const float major_delta = major - touch_history_last_accepted_; | |
| 333 const int direction_sig = | |
| 334 major_delta > 0 ? 1 : (major_delta < 0 ? -1 : 0); | |
| 335 if (direction_sig != touch_history_direction_ || | |
| 336 (direction_sig == 0 && touch_history_direction_ == 0)) { | |
| 337 touch_history_direction_ = direction_sig; | |
| 338 touch_history_last_accepted_time_ = h < history_size | |
| 339 ? ev.GetHistoricalEventTime(h) | |
| 340 : ev.GetEventTime(); | |
| 341 accept = false; | |
| 342 } | |
| 343 } | |
| 344 } | |
| 345 sample_count += pointersample_count; | |
| 346 } | |
| 347 | |
| 348 const float avg = total / sample_count; | |
| 349 | |
| 350 if (accept) { | |
| 351 float new_accepted = (touch_upper_ + touch_lower_ + avg) / 3; | |
| 352 touch_upper_ = (touch_upper_ + new_accepted) / 2; | |
| 353 touch_lower_ = (touch_lower_ + new_accepted) / 2; | |
| 354 touch_history_last_accepted_ = new_accepted; | |
| 355 touch_history_direction_ = 0; | |
| 356 touch_history_last_accepted_time_ = ev.GetEventTime(); | |
| 357 } | |
| 358 } | |
| 359 | |
| 360 void ScaleGestureDetector::ResetTouchHistory() { | |
| 361 touch_upper_ = std::numeric_limits<float>::quiet_NaN(); | |
| 362 touch_lower_ = std::numeric_limits<float>::quiet_NaN(); | |
| 363 touch_history_last_accepted_ = std::numeric_limits<float>::quiet_NaN(); | |
| 364 touch_history_direction_ = 0; | |
| 365 touch_history_last_accepted_time_ = base::TimeTicks(); | |
| 366 } | |
| 367 | |
| 368 void ScaleGestureDetector::ResetScaleWithSpan(float span) { | 278 void ScaleGestureDetector::ResetScaleWithSpan(float span) { |
| 369 in_progress_ = false; | 279 in_progress_ = false; |
| 370 initial_span_ = span; | 280 initial_span_ = span; |
| 371 anchored_scale_mode_ = ANCHORED_SCALE_MODE_NONE; | 281 anchored_scale_mode_ = ANCHORED_SCALE_MODE_NONE; |
| 372 } | 282 } |
| 373 | 283 |
| 374 } // namespace ui | 284 } // namespace ui |
| OLD | NEW |