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/snap_scroll_controller.h" | 5 #include "ui/events/gesture_detection/snap_scroll_controller.h" |
6 | 6 |
7 #include <cmath> | 7 #include <cmath> |
8 | 8 |
9 #include "ui/events/gesture_detection/motion_event.h" | 9 #include "ui/events/gesture_detection/motion_event.h" |
10 #include "ui/gfx/display.h" | |
11 | 10 |
12 namespace ui { | 11 namespace ui { |
13 namespace { | 12 namespace { |
14 const int kSnapBound = 16; | |
15 const float kMinSnapChannelDistance = kSnapBound; | |
16 const float kMaxSnapChannelDistance = kMinSnapChannelDistance * 3.f; | |
17 const float kSnapChannelDipsPerScreenDip = kMinSnapChannelDistance / 480.f; | |
18 | 13 |
19 float CalculateChannelDistance(const gfx::Display& display) { | 14 // Minimum ratio between initial X and Y motion to allow snapping. |
20 if (display.bounds().IsEmpty()) | 15 const float kMinSnapRatio = 1.25f; |
21 return kMinSnapChannelDistance; | 16 |
| 17 // Size of the snap rail relative to the initial snap bound threshold. |
| 18 const float kSnapBoundToChannelMultiplier = 1.5f; |
| 19 |
| 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; |
22 | 27 |
23 float screen_size = | 28 float screen_size = |
24 std::abs(hypot(static_cast<float>(display.bounds().width()), | 29 std::abs(hypot(static_cast<float>(display_size.width()), |
25 static_cast<float>(display.bounds().height()))); | 30 static_cast<float>(display_size.height()))); |
26 | 31 |
27 float snap_channel_distance = screen_size * kSnapChannelDipsPerScreenDip; | 32 float snap_channel_distance = screen_size * kSnapChannelDipsPerScreenDip; |
28 return std::max(kMinSnapChannelDistance, | 33 return std::max(kMinChannelDistance, |
29 std::min(kMaxSnapChannelDistance, snap_channel_distance)); | 34 std::min(kMaxChannelDistance, snap_channel_distance)); |
30 } | 35 } |
31 | 36 |
32 } // namespace | 37 } // namespace |
33 | 38 |
34 | 39 SnapScrollController::SnapScrollController(float snap_bound, |
35 SnapScrollController::SnapScrollController(const gfx::Display& display) | 40 const gfx::SizeF& display_size) |
36 : channel_distance_(CalculateChannelDistance(display)), | 41 : snap_bound_(snap_bound), |
37 snap_scroll_mode_(SNAP_NONE), | 42 channel_distance_(CalculateChannelDistance(snap_bound, display_size)), |
38 first_touch_x_(-1), | 43 mode_(SNAP_NONE) { |
39 first_touch_y_(-1), | |
40 distance_x_(0), | |
41 distance_y_(0) {} | |
42 | |
43 SnapScrollController::~SnapScrollController() {} | |
44 | |
45 void SnapScrollController::UpdateSnapScrollMode(float distance_x, | |
46 float distance_y) { | |
47 if (snap_scroll_mode_ == SNAP_HORIZ || snap_scroll_mode_ == SNAP_VERT) { | |
48 distance_x_ += std::abs(distance_x); | |
49 distance_y_ += std::abs(distance_y); | |
50 if (snap_scroll_mode_ == SNAP_HORIZ) { | |
51 if (distance_y_ > channel_distance_) { | |
52 snap_scroll_mode_ = SNAP_NONE; | |
53 } else if (distance_x_ > channel_distance_) { | |
54 distance_x_ = 0; | |
55 distance_y_ = 0; | |
56 } | |
57 } else { | |
58 if (distance_x_ > channel_distance_) { | |
59 snap_scroll_mode_ = SNAP_NONE; | |
60 } else if (distance_y_ > channel_distance_) { | |
61 distance_x_ = 0; | |
62 distance_y_ = 0; | |
63 } | |
64 } | |
65 } | |
66 } | 44 } |
67 | 45 |
68 void SnapScrollController::SetSnapScrollingMode( | 46 SnapScrollController::~SnapScrollController() { |
| 47 } |
| 48 |
| 49 void SnapScrollController::SetSnapScrollMode( |
69 const MotionEvent& event, | 50 const MotionEvent& event, |
70 bool is_scale_gesture_detection_in_progress) { | 51 bool is_scale_gesture_detection_in_progress) { |
71 switch (event.GetAction()) { | 52 switch (event.GetAction()) { |
72 case MotionEvent::ACTION_DOWN: | 53 case MotionEvent::ACTION_DOWN: |
73 snap_scroll_mode_ = SNAP_NONE; | 54 mode_ = SNAP_PENDING; |
74 first_touch_x_ = event.GetX(); | 55 down_position_.set_x(event.GetX()); |
75 first_touch_y_ = event.GetY(); | 56 down_position_.set_y(event.GetY()); |
76 break; | 57 break; |
77 // Set scrolling mode to SNAP_X if scroll towards x-axis exceeds kSnapBound | 58 case MotionEvent::ACTION_MOVE: { |
78 // and movement towards y-axis is trivial. | 59 if (is_scale_gesture_detection_in_progress) |
79 // Set scrolling mode to SNAP_Y if scroll towards y-axis exceeds kSnapBound | 60 break; |
80 // and movement towards x-axis is trivial. | 61 |
81 // Scrolling mode will remain in SNAP_NONE for other conditions. | 62 if (mode_ != SNAP_PENDING) |
82 case MotionEvent::ACTION_MOVE: | 63 break; |
83 if (!is_scale_gesture_detection_in_progress && | 64 |
84 snap_scroll_mode_ == SNAP_NONE) { | 65 // Set scrolling mode to SNAP_X if scroll exceeds |snap_bound_| and the |
85 int x_diff = static_cast<int>(std::abs(event.GetX() - first_touch_x_)); | 66 // ratio of x movement to y movement is sufficiently large. Similarly for |
86 int y_diff = static_cast<int>(std::abs(event.GetY() - first_touch_y_)); | 67 // SNAP_Y and y movement. |
87 if (x_diff > kSnapBound && y_diff < kSnapBound) { | 68 float dx = std::abs(event.GetX() - down_position_.x()); |
88 snap_scroll_mode_ = SNAP_HORIZ; | 69 float dy = std::abs(event.GetY() - down_position_.y()); |
89 } else if (x_diff < kSnapBound && y_diff > kSnapBound) { | 70 float kMinSnapBound = snap_bound_; |
90 snap_scroll_mode_ = SNAP_VERT; | 71 float kMaxSnapBound = snap_bound_ * 2.f; |
91 } | 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; |
92 } | 77 } |
93 break; | 78 |
| 79 if (mode_ == SNAP_PENDING && dx > kMaxSnapBound && dy > kMaxSnapBound) |
| 80 mode_ = SNAP_NONE; |
| 81 } break; |
94 case MotionEvent::ACTION_UP: | 82 case MotionEvent::ACTION_UP: |
95 case MotionEvent::ACTION_CANCEL: | 83 case MotionEvent::ACTION_CANCEL: |
96 first_touch_x_ = -1; | 84 down_position_ = gfx::PointF(); |
97 first_touch_y_ = -1; | 85 accumulated_distance_ = gfx::Vector2dF(); |
98 distance_x_ = 0; | |
99 distance_y_ = 0; | |
100 break; | 86 break; |
101 default: | 87 default: |
102 break; | 88 break; |
103 } | 89 } |
104 } | 90 } |
105 | 91 |
| 92 void SnapScrollController::UpdateSnapScrollMode(float distance_x, |
| 93 float distance_y) { |
| 94 if (!IsSnappingScrolls()) |
| 95 return; |
| 96 |
| 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(); |
| 109 } |
| 110 } |
| 111 |
| 112 bool SnapScrollController::IsSnapVertical() const { |
| 113 return mode_ == SNAP_VERT; |
| 114 } |
| 115 |
| 116 bool SnapScrollController::IsSnapHorizontal() const { |
| 117 return mode_ == SNAP_HORIZ; |
| 118 } |
| 119 |
| 120 bool SnapScrollController::IsSnappingScrolls() const { |
| 121 return IsSnapHorizontal() || IsSnapVertical(); |
| 122 } |
| 123 |
106 } // namespace ui | 124 } // namespace ui |
OLD | NEW |