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
5#include "ui/events/gesture_detection/snap_scroll_controller.h"
6
7#include <cmath>
8
9#include "ui/events/gesture_detection/motion_event.h"
10#include "ui/gfx/display.h"
11
12namespace ui {
13namespace {
14const int kSnapBound = 16;
15const float kMinSnapChannelDistance = kSnapBound;
16const float kMaxSnapChannelDistance = kMinSnapChannelDistance * 3.f;
17const float kSnapChannelDipsPerScreenDip = kMinSnapChannelDistance / 480.f;
18
19float CalculateChannelDistance(const gfx::Display& display) {
20  if (display.bounds().IsEmpty())
21    return kMinSnapChannelDistance;
22
23  float screen_size =
24      std::abs(hypot(static_cast<float>(display.bounds().width()),
25                     static_cast<float>(display.bounds().height())));
26
27  float snap_channel_distance = screen_size * kSnapChannelDipsPerScreenDip;
28  return std::max(kMinSnapChannelDistance,
29                  std::min(kMaxSnapChannelDistance, snap_channel_distance));
30}
31
32}  // namespace
33
34
35SnapScrollController::SnapScrollController(const gfx::Display& display)
36    : channel_distance_(CalculateChannelDistance(display)),
37      snap_scroll_mode_(SNAP_NONE),
38      first_touch_x_(-1),
39      first_touch_y_(-1),
40      distance_x_(0),
41      distance_y_(0) {}
42
43SnapScrollController::~SnapScrollController() {}
44
45void 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}
67
68void SnapScrollController::SetSnapScrollingMode(
69    const MotionEvent& event,
70    bool is_scale_gesture_detection_in_progress) {
71  switch (event.GetAction()) {
72    case MotionEvent::ACTION_DOWN:
73      snap_scroll_mode_ = SNAP_NONE;
74      first_touch_x_ = event.GetX();
75      first_touch_y_ = event.GetY();
76      break;
77    // Set scrolling mode to SNAP_X if scroll towards x-axis exceeds kSnapBound
78    // and movement towards y-axis is trivial.
79    // Set scrolling mode to SNAP_Y if scroll towards y-axis exceeds kSnapBound
80    // and movement towards x-axis is trivial.
81    // Scrolling mode will remain in SNAP_NONE for other conditions.
82    case MotionEvent::ACTION_MOVE:
83      if (!is_scale_gesture_detection_in_progress &&
84          snap_scroll_mode_ == SNAP_NONE) {
85        int x_diff = static_cast<int>(std::abs(event.GetX() - first_touch_x_));
86        int y_diff = static_cast<int>(std::abs(event.GetY() - first_touch_y_));
87        if (x_diff > kSnapBound && y_diff < kSnapBound) {
88          snap_scroll_mode_ = SNAP_HORIZ;
89        } else if (x_diff < kSnapBound && y_diff > kSnapBound) {
90          snap_scroll_mode_ = SNAP_VERT;
91        }
92      }
93      break;
94    case MotionEvent::ACTION_UP:
95    case MotionEvent::ACTION_CANCEL:
96      first_touch_x_ = -1;
97      first_touch_y_ = -1;
98      distance_x_ = 0;
99      distance_y_ = 0;
100      break;
101    default:
102      break;
103  }
104}
105
106}  // namespace ui
107