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();