1 // Copyright (c) 2012 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/base/gestures/gesture_point.h" 6 7 #include <cmath> 8 9 #include "base/basictypes.h" 10 #include "ui/base/events/event.h" 11 #include "ui/base/events/event_constants.h" 12 #include "ui/base/gestures/gesture_configuration.h" 13 #include "ui/base/gestures/gesture_types.h" 14 #include "ui/base/gestures/gesture_util.h" 15 16 namespace ui { 17 18 GesturePoint::GesturePoint() 19 : first_touch_time_(0.0), 20 second_last_touch_time_(0.0), 21 last_touch_time_(0.0), 22 second_last_tap_time_(0.0), 23 last_tap_time_(0.0), 24 velocity_calculator_( 25 GestureConfiguration::points_buffered_for_velocity()), 26 point_id_(-1), 27 touch_id_(-1) { 28 } 29 30 GesturePoint::~GesturePoint() {} 31 32 void GesturePoint::Reset() { 33 first_touch_time_ = second_last_touch_time_ = last_touch_time_ = 0.0; 34 ResetVelocity(); 35 point_id_ = -1; 36 clear_enclosing_rectangle(); 37 } 38 39 void GesturePoint::ResetVelocity() { 40 velocity_calculator_.ClearHistory(); 41 same_direction_count_ = gfx::Vector2d(); 42 } 43 44 gfx::Vector2d GesturePoint::ScrollDelta() { 45 return last_touch_position_ - second_last_touch_position_; 46 } 47 48 void GesturePoint::UpdateValues(const TouchEvent& event) { 49 const int64 event_timestamp_microseconds = 50 event.time_stamp().InMicroseconds(); 51 if (event.type() == ui::ET_TOUCH_MOVED) { 52 velocity_calculator_.PointSeen(event.location().x(), 53 event.location().y(), 54 event_timestamp_microseconds); 55 gfx::Vector2d sd(ScrollVelocityDirection(velocity_calculator_.XVelocity()), 56 ScrollVelocityDirection(velocity_calculator_.YVelocity())); 57 same_direction_count_ = same_direction_count_ + sd; 58 } 59 60 last_touch_time_ = event.time_stamp().InSecondsF(); 61 last_touch_position_ = event.location(); 62 63 if (event.type() == ui::ET_TOUCH_PRESSED) { 64 ResetVelocity(); 65 clear_enclosing_rectangle(); 66 first_touch_time_ = last_touch_time_; 67 first_touch_position_ = event.location(); 68 second_last_touch_position_ = last_touch_position_; 69 second_last_touch_time_ = last_touch_time_; 70 velocity_calculator_.PointSeen(event.location().x(), 71 event.location().y(), 72 event_timestamp_microseconds); 73 } 74 75 UpdateEnclosingRectangle(event); 76 } 77 78 void GesturePoint::UpdateForTap() { 79 // Update the tap-position and time, and reset every other state. 80 second_last_tap_position_ = last_tap_position_; 81 second_last_tap_time_ = last_tap_time_; 82 last_tap_time_ = last_touch_time_; 83 last_tap_position_ = last_touch_position_; 84 } 85 86 void GesturePoint::UpdateForScroll() { 87 second_last_touch_position_ = last_touch_position_; 88 second_last_touch_time_ = last_touch_time_; 89 same_direction_count_ = gfx::Vector2d(); 90 } 91 92 bool GesturePoint::IsInClickWindow(const TouchEvent& event) const { 93 return IsInClickTimeWindow() && IsInsideManhattanSquare(event); 94 } 95 96 bool GesturePoint::IsInDoubleClickWindow(const TouchEvent& event) const { 97 return IsInClickAggregateTimeWindow(last_tap_time_, last_touch_time_) && 98 IsPointInsideManhattanSquare(event.location(), last_tap_position_); 99 } 100 101 bool GesturePoint::IsInTripleClickWindow(const TouchEvent& event) const { 102 return IsInClickAggregateTimeWindow(last_tap_time_, last_touch_time_) && 103 IsInClickAggregateTimeWindow(second_last_tap_time_, last_tap_time_) && 104 IsPointInsideManhattanSquare(event.location(), last_tap_position_) && 105 IsPointInsideManhattanSquare(last_tap_position_, 106 second_last_tap_position_); 107 } 108 109 bool GesturePoint::IsInScrollWindow(const TouchEvent& event) const { 110 return event.type() == ui::ET_TOUCH_MOVED && 111 !IsInsideManhattanSquare(event); 112 } 113 114 bool GesturePoint::IsInFlickWindow(const TouchEvent& event) { 115 return IsOverMinFlickSpeed() && 116 event.type() != ui::ET_TOUCH_CANCELLED; 117 } 118 119 int GesturePoint::ScrollVelocityDirection(float v) { 120 if (v < -GestureConfiguration::min_scroll_velocity()) 121 return -1; 122 else if (v > GestureConfiguration::min_scroll_velocity()) 123 return 1; 124 else 125 return 0; 126 } 127 128 bool GesturePoint::DidScroll(const TouchEvent& event, int dist) const { 129 gfx::Vector2d d = last_touch_position_ - second_last_touch_position_; 130 return abs(d.x()) > dist || abs(d.y()) > dist; 131 } 132 133 bool GesturePoint::IsConsistentScrollingActionUnderway() { 134 int me = GestureConfiguration::min_scroll_successive_velocity_events(); 135 if (abs(same_direction_count_.x()) >= me || 136 abs(same_direction_count_.y()) >= me) 137 return true; 138 return false; 139 } 140 141 bool GesturePoint::IsInHorizontalRailWindow() const { 142 gfx::Vector2d d = last_touch_position_ - second_last_touch_position_; 143 return abs(d.x()) > 144 GestureConfiguration::rail_start_proportion() * abs(d.y()); 145 } 146 147 bool GesturePoint::IsInVerticalRailWindow() const { 148 gfx::Vector2d d = last_touch_position_ - second_last_touch_position_; 149 return abs(d.y()) > 150 GestureConfiguration::rail_start_proportion() * abs(d.x()); 151 } 152 153 bool GesturePoint::BreaksHorizontalRail() { 154 float vx = XVelocity(); 155 float vy = YVelocity(); 156 return fabs(vy) > GestureConfiguration::rail_break_proportion() * fabs(vx) + 157 GestureConfiguration::min_rail_break_velocity(); 158 } 159 160 bool GesturePoint::BreaksVerticalRail() { 161 float vx = XVelocity(); 162 float vy = YVelocity(); 163 return fabs(vx) > GestureConfiguration::rail_break_proportion() * fabs(vy) + 164 GestureConfiguration::min_rail_break_velocity(); 165 } 166 167 bool GesturePoint::IsInClickTimeWindow() const { 168 double duration = last_touch_time_ - first_touch_time_; 169 return duration >= 170 GestureConfiguration::min_touch_down_duration_in_seconds_for_click() && 171 duration < 172 GestureConfiguration::max_touch_down_duration_in_seconds_for_click(); 173 } 174 175 bool GesturePoint::IsInClickAggregateTimeWindow(double before, 176 double after) const { 177 double duration = after - before; 178 return duration < GestureConfiguration::max_seconds_between_double_click(); 179 } 180 181 bool GesturePoint::IsInsideManhattanSquare(const TouchEvent& event) const { 182 return ui::gestures::IsInsideManhattanSquare(event.location(), 183 first_touch_position_); 184 } 185 186 bool GesturePoint::IsPointInsideManhattanSquare(gfx::Point p1, 187 gfx::Point p2) const { 188 int manhattan_distance = abs(p1.x() - p2.x()) + abs(p1.y() - p2.y()); 189 return manhattan_distance < 190 GestureConfiguration::max_distance_between_taps_for_double_tap(); 191 } 192 193 bool GesturePoint::IsOverMinFlickSpeed() { 194 return velocity_calculator_.VelocitySquared() > 195 GestureConfiguration::min_flick_speed_squared(); 196 } 197 198 void GesturePoint::UpdateEnclosingRectangle(const TouchEvent& event) { 199 int radius; 200 201 // Ignore this TouchEvent if it has a radius larger than the maximum 202 // allowed radius size. 203 if (event.radius_x() > GestureConfiguration::max_radius() || 204 event.radius_y() > GestureConfiguration::max_radius()) 205 return; 206 207 // If the device provides at least one of the radius values, take the larger 208 // of the two and use this as both the x radius and the y radius of the 209 // touch region. Otherwise use the default radius value. 210 // TODO(tdanderson): Implement a more specific check for the exact 211 // information provided by the device (0-2 radii values, force, angle) and 212 // use this to compute a more representative rectangular touch region. 213 if (event.radius_x() || event.radius_y()) 214 radius = std::max(event.radius_x(), event.radius_y()); 215 else 216 radius = GestureConfiguration::default_radius(); 217 218 gfx::Rect rect(event.location().x() - radius, 219 event.location().y() - radius, 220 radius * 2, 221 radius * 2); 222 if (IsInClickWindow(event)) 223 enclosing_rect_.Union(rect); 224 else 225 enclosing_rect_ = rect; 226 } 227 228 } // namespace ui 229