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.
5 #include "ui/events/gesture_detection/snap_scroll_controller.h"
9 #include "ui/events/gesture_detection/motion_event.h"
14 // Minimum ratio between initial X and Y motion to allow snapping.
15 const float kMinSnapRatio = 1.25f;
17 // Size of the snap rail relative to the initial snap bound threshold.
18 const float kSnapBoundToChannelMultiplier = 1.5f;
20 float CalculateChannelDistance(float snap_bound,
21 const gfx::SizeF& display_size) {
22 const float kMinChannelDistance = snap_bound * kSnapBoundToChannelMultiplier;
23 const float kMaxChannelDistance = kMinChannelDistance * 3.f;
24 const float kSnapChannelDipsPerScreenDip = kMinChannelDistance / 480.f;
25 if (display_size.IsEmpty())
26 return kMinChannelDistance;
29 std::abs(hypot(static_cast<float>(display_size.width()),
30 static_cast<float>(display_size.height())));
32 float snap_channel_distance = screen_size * kSnapChannelDipsPerScreenDip;
33 return std::max(kMinChannelDistance,
34 std::min(kMaxChannelDistance, snap_channel_distance));
39 SnapScrollController::SnapScrollController(float snap_bound,
40 const gfx::SizeF& display_size)
41 : snap_bound_(snap_bound),
42 channel_distance_(CalculateChannelDistance(snap_bound, display_size)),
46 SnapScrollController::~SnapScrollController() {
49 void SnapScrollController::SetSnapScrollMode(
50 const MotionEvent& event,
51 bool is_scale_gesture_detection_in_progress) {
52 switch (event.GetAction()) {
53 case MotionEvent::ACTION_DOWN:
55 down_position_.set_x(event.GetX());
56 down_position_.set_y(event.GetY());
58 case MotionEvent::ACTION_MOVE: {
59 if (is_scale_gesture_detection_in_progress)
62 if (mode_ != SNAP_PENDING)
65 // Set scrolling mode to SNAP_X if scroll exceeds |snap_bound_| and the
66 // ratio of x movement to y movement is sufficiently large. Similarly for
67 // SNAP_Y and y movement.
68 float dx = std::abs(event.GetX() - down_position_.x());
69 float dy = std::abs(event.GetY() - down_position_.y());
70 float kMinSnapBound = snap_bound_;
71 float kMaxSnapBound = snap_bound_ * 2.f;
72 if (dx * dx + dy * dy > kMinSnapBound * kMinSnapBound) {
73 if (!dy || (dx / dy > kMinSnapRatio && dy < kMaxSnapBound))
75 else if (!dx || (dy / dx > kMinSnapRatio && dx < kMaxSnapBound))
79 if (mode_ == SNAP_PENDING && dx > kMaxSnapBound && dy > kMaxSnapBound)
82 case MotionEvent::ACTION_UP:
83 case MotionEvent::ACTION_CANCEL:
84 down_position_ = gfx::PointF();
85 accumulated_distance_ = gfx::Vector2dF();
92 void SnapScrollController::UpdateSnapScrollMode(float distance_x,
94 if (!IsSnappingScrolls())
97 accumulated_distance_ +=
98 gfx::Vector2dF(std::abs(distance_x), std::abs(distance_y));
99 if (mode_ == SNAP_HORIZ) {
100 if (accumulated_distance_.y() > channel_distance_)
102 else if (accumulated_distance_.x() > channel_distance_)
103 accumulated_distance_ = gfx::Vector2dF();
104 } else if (mode_ == SNAP_VERT) {
105 if (accumulated_distance_.x() > channel_distance_)
107 else if (accumulated_distance_.y() > channel_distance_)
108 accumulated_distance_ = gfx::Vector2dF();
112 bool SnapScrollController::IsSnapVertical() const {
113 return mode_ == SNAP_VERT;
116 bool SnapScrollController::IsSnapHorizontal() const {
117 return mode_ == SNAP_HORIZ;
120 bool SnapScrollController::IsSnappingScrolls() const {
121 return IsSnapHorizontal() || IsSnapVertical();