Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / ui / events / gesture_detection / snap_scroll_controller.cc
blob9ecf6b108c8a71030bc282bda1dbc1c5f065696a
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"
7 #include <cmath>
9 #include "ui/events/gesture_detection/motion_event.h"
11 namespace ui {
12 namespace {
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;
28 float screen_size =
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));
37 } // namespace
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)),
43 mode_(SNAP_NONE) {
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:
54 mode_ = SNAP_PENDING;
55 down_position_.set_x(event.GetX());
56 down_position_.set_y(event.GetY());
57 break;
58 case MotionEvent::ACTION_MOVE: {
59 if (is_scale_gesture_detection_in_progress)
60 break;
62 if (mode_ != SNAP_PENDING)
63 break;
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))
74 mode_ = SNAP_HORIZ;
75 else if (!dx || (dy / dx > kMinSnapRatio && dx < kMaxSnapBound))
76 mode_ = SNAP_VERT;
79 if (mode_ == SNAP_PENDING && dx > kMaxSnapBound && dy > kMaxSnapBound)
80 mode_ = SNAP_NONE;
81 } break;
82 case MotionEvent::ACTION_UP:
83 case MotionEvent::ACTION_CANCEL:
84 down_position_ = gfx::PointF();
85 accumulated_distance_ = gfx::Vector2dF();
86 break;
87 default:
88 break;
92 void SnapScrollController::UpdateSnapScrollMode(float distance_x,
93 float distance_y) {
94 if (!IsSnappingScrolls())
95 return;
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_)
101 mode_ = SNAP_NONE;
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_)
106 mode_ = SNAP_NONE;
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();
124 } // namespace ui