scale_gesture_detector.cc 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  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. #include "ui/events/gesture_detection/scale_gesture_detector.h"
  5. #include <limits.h>
  6. #include <algorithm>
  7. #include <cmath>
  8. #include "base/check.h"
  9. #include "ui/events/gesture_detection/motion_event.h"
  10. #include "ui/events/gesture_detection/scale_gesture_listeners.h"
  11. using base::TimeTicks;
  12. namespace ui {
  13. namespace {
  14. const float kScaleFactor = .5f;
  15. // Using a small epsilon when comparing slop distances allows pixel
  16. // perfect slop determination when using fractional DPI coordinates
  17. // (assuming the slop region and DPI scale are reasonably
  18. // proportioned).
  19. const float kSlopEpsilon = .05f;
  20. } // namespace
  21. // Note: These constants were taken directly from the default (unscaled)
  22. // versions found in Android's ViewConfiguration. Do not change these default
  23. // values without explicitly consulting an OWNER.
  24. ScaleGestureDetector::Config::Config()
  25. : span_slop(16),
  26. min_scaling_span(200),
  27. min_pinch_update_span_delta(0),
  28. stylus_scale_enabled(false) {}
  29. ScaleGestureDetector::Config::~Config() {}
  30. ScaleGestureDetector::ScaleGestureDetector(const Config& config,
  31. ScaleGestureListener* listener)
  32. : listener_(listener),
  33. stylus_scale_enabled_(config.stylus_scale_enabled),
  34. focus_x_(0),
  35. focus_y_(0),
  36. curr_span_(0),
  37. prev_span_(0),
  38. initial_span_(0),
  39. curr_span_x_(0),
  40. curr_span_y_(0),
  41. prev_span_x_(0),
  42. prev_span_y_(0),
  43. in_progress_(false),
  44. span_slop_(0),
  45. min_span_(0),
  46. anchored_scale_start_x_(0),
  47. anchored_scale_start_y_(0),
  48. anchored_scale_mode_(ANCHORED_SCALE_MODE_NONE),
  49. event_before_or_above_starting_gesture_event_(false) {
  50. DCHECK(listener_);
  51. span_slop_ = config.span_slop;
  52. min_span_ = config.min_scaling_span;
  53. }
  54. ScaleGestureDetector::~ScaleGestureDetector() {}
  55. bool ScaleGestureDetector::OnTouchEvent(const MotionEvent& event) {
  56. curr_time_ = event.GetEventTime();
  57. const MotionEvent::Action action = event.GetAction();
  58. const int count = static_cast<int>(event.GetPointerCount());
  59. const bool is_stylus_button_down =
  60. (event.GetButtonState() & MotionEvent::BUTTON_STYLUS_PRIMARY) != 0;
  61. const bool anchored_scale_cancelled =
  62. anchored_scale_mode_ == ANCHORED_SCALE_MODE_STYLUS &&
  63. !is_stylus_button_down;
  64. const bool stream_complete =
  65. action == MotionEvent::Action::UP ||
  66. action == MotionEvent::Action::CANCEL || anchored_scale_cancelled ||
  67. (action == MotionEvent::Action::POINTER_DOWN && InAnchoredScaleMode());
  68. if (action == MotionEvent::Action::DOWN || stream_complete) {
  69. // Reset any scale in progress with the listener.
  70. // If it's an ACTION_DOWN we're beginning a new event stream.
  71. // This means the app probably didn't give us all the events. Shame on it.
  72. if (in_progress_) {
  73. listener_->OnScaleEnd(*this, event);
  74. ResetScaleWithSpan(0);
  75. } else if (InAnchoredScaleMode() && stream_complete) {
  76. ResetScaleWithSpan(0);
  77. }
  78. if (stream_complete)
  79. return true;
  80. }
  81. if (!in_progress_ && stylus_scale_enabled_ && !InAnchoredScaleMode() &&
  82. !stream_complete && is_stylus_button_down) {
  83. // Start of a stylus scale gesture.
  84. anchored_scale_start_x_ = event.GetX();
  85. anchored_scale_start_y_ = event.GetY();
  86. anchored_scale_mode_ = ANCHORED_SCALE_MODE_STYLUS;
  87. initial_span_ = 0;
  88. }
  89. const bool config_changed = action == MotionEvent::Action::DOWN ||
  90. action == MotionEvent::Action::POINTER_UP ||
  91. action == MotionEvent::Action::POINTER_DOWN ||
  92. anchored_scale_cancelled;
  93. const bool pointer_up = action == MotionEvent::Action::POINTER_UP;
  94. const int skip_index = pointer_up ? event.GetActionIndex() : -1;
  95. // Determine focal point.
  96. float sum_x = 0, sum_y = 0;
  97. const int unreleased_point_count = pointer_up ? count - 1 : count;
  98. const float inverse_unreleased_point_count = 1.0f / unreleased_point_count;
  99. float focus_x;
  100. float focus_y;
  101. if (InAnchoredScaleMode()) {
  102. // In double tap mode, the focal pt is always where the double tap
  103. // gesture started.
  104. focus_x = anchored_scale_start_x_;
  105. focus_y = anchored_scale_start_y_;
  106. if (event.GetY() < focus_y) {
  107. event_before_or_above_starting_gesture_event_ = true;
  108. } else {
  109. event_before_or_above_starting_gesture_event_ = false;
  110. }
  111. } else {
  112. for (int i = 0; i < count; i++) {
  113. if (skip_index == i)
  114. continue;
  115. sum_x += event.GetX(i);
  116. sum_y += event.GetY(i);
  117. }
  118. focus_x = sum_x * inverse_unreleased_point_count;
  119. focus_y = sum_y * inverse_unreleased_point_count;
  120. }
  121. // Determine average deviation from focal point.
  122. float dev_sum_x = 0, dev_sum_y = 0;
  123. for (int i = 0; i < count; i++) {
  124. if (skip_index == i)
  125. continue;
  126. dev_sum_x += std::abs(event.GetX(i) - focus_x);
  127. dev_sum_y += std::abs(event.GetY(i) - focus_y);
  128. }
  129. const float dev_x = dev_sum_x * inverse_unreleased_point_count;
  130. const float dev_y = dev_sum_y * inverse_unreleased_point_count;
  131. // Span is the average distance between touch points through the focal point;
  132. // i.e. the diameter of the circle with a radius of the average deviation from
  133. // the focal point.
  134. const float span_x = dev_x * 2;
  135. const float span_y = dev_y * 2;
  136. float span;
  137. if (InAnchoredScaleMode()) {
  138. span = span_y;
  139. } else {
  140. span = std::sqrt(span_x * span_x + span_y * span_y);
  141. }
  142. // Dispatch begin/end events as needed.
  143. // If the configuration changes, notify the app to reset its current state by
  144. // beginning a fresh scale event stream.
  145. const bool was_in_progress = in_progress_;
  146. focus_x_ = focus_x;
  147. focus_y_ = focus_y;
  148. if (!InAnchoredScaleMode() && in_progress_ && config_changed) {
  149. listener_->OnScaleEnd(*this, event);
  150. ResetScaleWithSpan(span);
  151. }
  152. if (config_changed) {
  153. prev_span_x_ = curr_span_x_ = span_x;
  154. prev_span_y_ = curr_span_y_ = span_y;
  155. initial_span_ = prev_span_ = curr_span_ = span;
  156. }
  157. const float min_span = InAnchoredScaleMode() ? span_slop_ : min_span_;
  158. bool span_exceeds_min_span = span >= min_span + kSlopEpsilon ||
  159. initial_span_ >= min_span + kSlopEpsilon;
  160. if (!in_progress_ && span_exceeds_min_span &&
  161. (was_in_progress ||
  162. std::abs(span - initial_span_) > span_slop_ + kSlopEpsilon)) {
  163. float zoom_sign = span > initial_span_ ? 1 : -1;
  164. prev_span_x_ = curr_span_x_ = span_x;
  165. prev_span_y_ = curr_span_y_ = span_y;
  166. curr_span_ = span;
  167. // To ensure we don't lose any delta when the first event crosses the min
  168. // and slop thresholds, the prev_span on the first update will be the point
  169. // at which zooming would have started.
  170. prev_span_ = std::max(initial_span_ + zoom_sign * span_slop_, min_span);
  171. prev_time_ = curr_time_;
  172. in_progress_ = listener_->OnScaleBegin(*this, event);
  173. }
  174. // Handle motion; focal point and span/scale factor are changing.
  175. if (action == MotionEvent::Action::MOVE) {
  176. curr_span_x_ = span_x;
  177. curr_span_y_ = span_y;
  178. curr_span_ = span;
  179. bool update_prev = true;
  180. if (in_progress_)
  181. update_prev = listener_->OnScale(*this, event);
  182. if (update_prev) {
  183. prev_span_x_ = curr_span_x_;
  184. prev_span_y_ = curr_span_y_;
  185. prev_span_ = curr_span_;
  186. prev_time_ = curr_time_;
  187. }
  188. }
  189. if (!InAnchoredScaleMode() && in_progress_ &&
  190. span < min_span_ + kSlopEpsilon) {
  191. listener_->OnScaleEnd(*this, event);
  192. ResetScaleWithSpan(span);
  193. }
  194. return true;
  195. }
  196. bool ScaleGestureDetector::IsInProgress() const { return in_progress_; }
  197. bool ScaleGestureDetector::InAnchoredScaleMode() const {
  198. return anchored_scale_mode_ != ANCHORED_SCALE_MODE_NONE;
  199. }
  200. float ScaleGestureDetector::GetFocusX() const { return focus_x_; }
  201. float ScaleGestureDetector::GetFocusY() const { return focus_y_; }
  202. float ScaleGestureDetector::GetCurrentSpan() const { return curr_span_; }
  203. float ScaleGestureDetector::GetCurrentSpanX() const { return curr_span_x_; }
  204. float ScaleGestureDetector::GetCurrentSpanY() const { return curr_span_y_; }
  205. float ScaleGestureDetector::GetPreviousSpan() const { return prev_span_; }
  206. float ScaleGestureDetector::GetPreviousSpanX() const { return prev_span_x_; }
  207. float ScaleGestureDetector::GetPreviousSpanY() const { return prev_span_y_; }
  208. float ScaleGestureDetector::GetScaleFactor() const {
  209. float curr_span = curr_span_;
  210. if (InAnchoredScaleMode()) {
  211. // Drag is moving up; the further away from the gesture start, the smaller
  212. // the span should be, the closer, the larger the span, and therefore the
  213. // larger the scale.
  214. const bool scale_up = (event_before_or_above_starting_gesture_event_ &&
  215. (curr_span < prev_span_)) ||
  216. (!event_before_or_above_starting_gesture_event_ &&
  217. (curr_span > prev_span_));
  218. const float span_diff =
  219. (std::abs(1.f - (curr_span / prev_span_)) * kScaleFactor);
  220. return prev_span_ <= 0 ? 1.f
  221. : (scale_up ? (1.f + span_diff) : (1.f - span_diff));
  222. }
  223. // If this will be the last update because this event crossed the min
  224. // threshold, calculate the update as if the event stopped right at the
  225. // boundary.
  226. if (curr_span < min_span_ + kSlopEpsilon)
  227. curr_span = min_span_;
  228. return prev_span_ > 0 ? curr_span / prev_span_ : 1;
  229. }
  230. base::TimeDelta ScaleGestureDetector::GetTimeDelta() const {
  231. return curr_time_ - prev_time_;
  232. }
  233. base::TimeTicks ScaleGestureDetector::GetEventTime() const {
  234. return curr_time_;
  235. }
  236. bool ScaleGestureDetector::OnDoubleTap(const MotionEvent& ev) {
  237. // Double tap: start watching for a swipe.
  238. anchored_scale_start_x_ = ev.GetX();
  239. anchored_scale_start_y_ = ev.GetY();
  240. anchored_scale_mode_ = ANCHORED_SCALE_MODE_DOUBLE_TAP;
  241. return true;
  242. }
  243. void ScaleGestureDetector::ResetScaleWithSpan(float span) {
  244. in_progress_ = false;
  245. initial_span_ = span;
  246. anchored_scale_mode_ = ANCHORED_SCALE_MODE_NONE;
  247. }
  248. } // namespace ui