Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2011 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 "views/touchui/gesture_recognizer.h" | |
| 6 | |
| 7 #include "base/logging.h" | |
| 8 #include "ui/base/events.h" | |
| 9 #include "views/events/event.h" | |
| 10 | |
| 11 namespace { | |
| 12 | |
| 13 double maximumTouchDownDurationInSecondsForClick = 0.8; | |
|
rjkroege
2011/10/21 20:02:10
A subsequent CL should connect these changes with
Gajen
2011/10/25 14:32:29
Done.
| |
| 14 double minimumTouchDownDurationInSecondsForClick = 0.01; | |
|
rjkroege
2011/10/21 20:02:10
These ought to be const at the moment. They need
Gajen
2011/10/25 14:32:29
Done.
| |
| 15 double maximumSecondsBetweenDoubleClick = 0.7; | |
| 16 int maximumTouchMoveInPixelsForClick = 20; | |
| 17 float minFlickSpeedSquared = 550.f * 550.f; | |
| 18 | |
| 19 } // namespace | |
| 20 | |
| 21 namespace views { | |
| 22 | |
| 23 //////////////////////////////////////////////////////////////////////////////// | |
| 24 // GestureRecognizer Public: | |
| 25 | |
| 26 GestureRecognizer::GestureRecognizer() | |
| 27 : first_touch_time_(0.0), | |
| 28 state_(GestureRecognizer::GS_NO_GESTURE), | |
| 29 last_touch_time_(0.0), | |
| 30 last_click_time_(0.0), | |
| 31 x_velocity_(0.0), | |
| 32 y_velocity_(0.0), | |
| 33 flags_(0) { | |
| 34 const unsigned int first_finger = 0; | |
| 35 const ui::EventType released = ui::ET_TOUCH_RELEASED; | |
| 36 const ui::EventType pressed = ui::ET_TOUCH_PRESSED; | |
| 37 const ui::EventType moved = ui::ET_TOUCH_MOVED; | |
| 38 const ui::EventType stationary = ui::ET_TOUCH_STATIONARY; | |
| 39 const ui::EventType cancelled = ui::ET_TOUCH_CANCELLED; | |
| 40 | |
| 41 AddEdgeFunction(GS_NO_GESTURE, first_finger, pressed, false, | |
| 42 &GestureRecognizer::TouchDown); | |
| 43 AddEdgeFunction(GS_PENDING_SYNTHETIC_CLICK, first_finger, cancelled, false, | |
| 44 &GestureRecognizer::NoGesture); | |
| 45 AddEdgeFunction(GS_PENDING_SYNTHETIC_CLICK, first_finger, released, false, | |
| 46 &GestureRecognizer::Click); | |
| 47 AddEdgeFunction(GS_PENDING_SYNTHETIC_CLICK, first_finger, moved, false, | |
| 48 &GestureRecognizer::IsClickOrScroll); | |
| 49 AddEdgeFunction(GS_PENDING_SYNTHETIC_CLICK, first_finger, stationary, false, | |
| 50 &GestureRecognizer::IsClickOrScroll); | |
| 51 AddEdgeFunction(GS_SCROLL, first_finger, moved, false, | |
| 52 &GestureRecognizer::InScroll); | |
| 53 AddEdgeFunction(GS_SCROLL, first_finger, released, false, | |
| 54 &GestureRecognizer::ScrollEnd); | |
| 55 AddEdgeFunction(GS_SCROLL, first_finger, cancelled, false, | |
| 56 &GestureRecognizer::ScrollEnd); | |
| 57 } | |
| 58 | |
| 59 GestureRecognizer::~GestureRecognizer() { | |
| 60 } | |
| 61 | |
| 62 GestureRecognizer::Gestures* GestureRecognizer::ProcessTouchEventForGesture( | |
| 63 const TouchEvent& event, | |
| 64 bool touch_handled) { | |
| 65 flags_ = event.flags(); | |
| 66 scoped_ptr<Gestures> gestures(new Gestures()); | |
| 67 UpdateValues(event); | |
| 68 GestureTransitionFunctionMap::iterator it = edge_functions_.find( | |
| 69 Signature(state_, event.identity(), event.type(), touch_handled)); | |
| 70 if (edge_functions_.end() != it) { | |
| 71 GestureTransitionFunction ef = it->second; | |
| 72 ((*this).*ef)(event, gestures.get()); | |
| 73 } | |
| 74 return gestures.release(); | |
| 75 } | |
| 76 | |
| 77 void GestureRecognizer::Reset() { | |
| 78 first_touch_time_ = 0.0; | |
| 79 state_ = GestureRecognizer::GS_NO_GESTURE; | |
| 80 last_touch_time_ = 0.0; | |
| 81 last_touch_position_.SetPoint(0, 0); | |
| 82 x_velocity_ = 0.0; | |
| 83 y_velocity_ = 0.0; | |
| 84 } | |
| 85 | |
| 86 //////////////////////////////////////////////////////////////////////////////// | |
| 87 // GestureRecognizer Private: | |
| 88 | |
| 89 GestureRecognizer::TouchState GestureRecognizer::TouchEventTypeToTouchState( | |
| 90 ui::EventType type) { | |
| 91 switch (type) { | |
| 92 case ui::ET_TOUCH_RELEASED: return TS_RELEASED; | |
| 93 case ui::ET_TOUCH_PRESSED: return TS_PRESSED; | |
| 94 case ui::ET_TOUCH_MOVED: return TS_MOVED; | |
| 95 case ui::ET_TOUCH_STATIONARY: return TS_STATIONARY; | |
| 96 case ui::ET_TOUCH_CANCELLED: return TS_CANCELLED; | |
| 97 default: | |
| 98 VLOG(1) << "Unknown Touch Event type"; | |
| 99 } | |
| 100 return TS_UNKNOWN; | |
| 101 } | |
| 102 | |
| 103 // Builds a signature. Signatures are assembled by joining together | |
| 104 // multiple bits. | |
| 105 // 1 LSB bit so that the computed signature is always greater than 0 | |
| 106 // 3 bits for the |type|. | |
| 107 // 1 bit for |touch_handled| | |
| 108 // 12 bits for |touch_id| | |
| 109 // 15 bits for the |gesture_state|. | |
| 110 unsigned int GestureRecognizer::Signature(GestureState gesture_state, | |
| 111 unsigned int touch_id, ui::EventType type, | |
| 112 bool touch_handled) { | |
| 113 CHECK((touch_id & 0xfff) == touch_id); | |
| 114 TouchState touch_state = TouchEventTypeToTouchState(type); | |
| 115 return 1 + ((touch_state & 0x7) << 1 | (touch_handled ? 1 << 4 : 0) | | |
| 116 ((touch_id & 0xfff) << 5) | (gesture_state << 17)); | |
| 117 } | |
| 118 | |
| 119 void GestureRecognizer::AddEdgeFunction(GestureState state, | |
| 120 unsigned int touch_id, | |
| 121 ui::EventType type, | |
| 122 bool touch_handled, | |
| 123 GestureTransitionFunction function) { | |
| 124 edge_functions_[Signature(state, touch_id, type, touch_handled)] = function; | |
| 125 } | |
| 126 | |
| 127 bool GestureRecognizer::IsInClickTimeWindow() { | |
| 128 double duration(last_touch_time_ - first_touch_time_); | |
| 129 return duration >= minimumTouchDownDurationInSecondsForClick && | |
| 130 duration < maximumTouchDownDurationInSecondsForClick; | |
| 131 } | |
| 132 | |
| 133 bool GestureRecognizer::IsInSecondClickTimeWindow() { | |
| 134 double duration(last_touch_time_ - last_click_time_); | |
| 135 return duration < maximumSecondsBetweenDoubleClick; | |
| 136 } | |
| 137 | |
| 138 bool GestureRecognizer::IsInsideManhattanSquare(const TouchEvent& event) { | |
| 139 int manhattanDistance = abs(event.x() - first_touch_position_.x()) + | |
| 140 abs(event.y() - first_touch_position_.y()); | |
| 141 return manhattanDistance < maximumTouchMoveInPixelsForClick; | |
| 142 } | |
| 143 | |
| 144 bool GestureRecognizer::IsSecondClickInsideManhattanSquare( | |
| 145 const TouchEvent& event) { | |
| 146 int manhattanDistance = abs(event.x() - last_click_position_.x()) + | |
| 147 abs(event.y() - last_click_position_.y()); | |
| 148 return manhattanDistance < maximumTouchMoveInPixelsForClick; | |
| 149 } | |
| 150 | |
| 151 bool GestureRecognizer::IsOverMinFlickSpeed() { | |
| 152 return (x_velocity_ * x_velocity_ + y_velocity_ * y_velocity_) > | |
| 153 minFlickSpeedSquared; | |
| 154 } | |
| 155 | |
| 156 void GestureRecognizer::AppendTapDownGestureEvent(const TouchEvent& event, | |
| 157 Gestures* gestures) { | |
| 158 gestures->push_back(new GestureEvent(ui::ET_GESTURE_TAP_DOWN, | |
| 159 first_touch_position_.x(), | |
|
rjkroege
2011/10/21 20:02:10
Bad indent I think.
Gajen
2011/10/25 14:32:29
Done.
Gajen
2011/10/25 14:32:29
Done.
| |
| 160 first_touch_position_.y(), | |
| 161 event.flags(), | |
| 162 base::Time::FromDoubleT(last_touch_time_), | |
| 163 0.f, 0.f)); | |
| 164 } | |
| 165 | |
| 166 void GestureRecognizer::AppendClickGestureEvent(const TouchEvent& event, | |
| 167 Gestures* gestures) { | |
| 168 gestures->push_back(new GestureEvent(ui::ET_GESTURE_TAP, | |
| 169 first_touch_position_.x(), | |
| 170 first_touch_position_.y(), | |
| 171 event.flags(), | |
| 172 base::Time::FromDoubleT(last_touch_time_), | |
| 173 0.f, 0.f)); | |
| 174 } | |
| 175 | |
| 176 void GestureRecognizer::AppendDoubleClickGestureEvent(const TouchEvent& event, | |
| 177 Gestures* gestures) { | |
| 178 gestures->push_back(new GestureEvent(ui::ET_GESTURE_DOUBLE_TAP, | |
| 179 first_touch_position_.x(), | |
| 180 first_touch_position_.y(), | |
| 181 event.flags(), | |
| 182 base::Time::FromDoubleT(last_touch_time_), | |
| 183 0.f, 0.f)); | |
| 184 } | |
| 185 | |
| 186 void GestureRecognizer::AppendScrollGestureBegin(const TouchEvent& event, | |
| 187 Gestures* gestures) { | |
| 188 gestures->push_back(new GestureEvent(ui::ET_GESTURE_SCROLL_BEGIN, | |
| 189 event.x(), | |
| 190 event.y(), | |
| 191 event.flags(), | |
| 192 base::Time::FromDoubleT(last_touch_time_), | |
| 193 0.f, 0.f)); | |
| 194 } | |
| 195 | |
| 196 void GestureRecognizer::AppendScrollGestureEnd(const TouchEvent& event, | |
| 197 Gestures* gestures, | |
| 198 float x_velocity, | |
| 199 float y_velocity) { | |
| 200 gestures->push_back(new GestureEvent(ui::ET_GESTURE_SCROLL_END, | |
| 201 event.x(), | |
| 202 event.y(), | |
| 203 event.flags(), | |
| 204 base::Time::FromDoubleT(last_touch_time_), | |
| 205 x_velocity, y_velocity)); | |
| 206 } | |
| 207 | |
| 208 void GestureRecognizer:: AppendScrollGestureUpdate(const TouchEvent& event, | |
| 209 Gestures* gestures) { | |
| 210 float delta_x(event.x() - first_touch_position_.x()); | |
| 211 float delta_y(event.y() - first_touch_position_.y()); | |
| 212 | |
| 213 gestures->push_back(new GestureEvent(ui::ET_GESTURE_SCROLL_UPDATE, | |
| 214 event.x(), | |
| 215 event.y(), | |
| 216 event.flags(), | |
| 217 base::Time::FromDoubleT(last_touch_time_), | |
| 218 delta_x, delta_y)); | |
| 219 | |
| 220 first_touch_position_ = event.location(); | |
| 221 } | |
| 222 | |
| 223 void GestureRecognizer::UpdateValues(const TouchEvent& event) { | |
| 224 if (state_ != GS_NO_GESTURE && event.type() == ui::ET_TOUCH_MOVED) { | |
| 225 double interval(event.time_stamp().ToDoubleT() - last_touch_time_); | |
| 226 x_velocity_ = (event.x() - last_touch_position_.x()) / interval; | |
| 227 y_velocity_ = (event.y() - last_touch_position_.y()) / interval; | |
| 228 } | |
| 229 last_touch_time_ = event.time_stamp().ToDoubleT(); | |
| 230 last_touch_position_ = event.location(); | |
| 231 if (state_ == GS_NO_GESTURE) { | |
| 232 first_touch_time_ = last_touch_time_; | |
| 233 first_touch_position_ = event.location(); | |
| 234 x_velocity_ = 0.0; | |
| 235 y_velocity_ = 0.0; | |
| 236 } | |
| 237 } | |
| 238 | |
| 239 bool GestureRecognizer::Click(const TouchEvent& event, Gestures* gestures) { | |
| 240 bool gesture_added = false; | |
| 241 if (IsInClickTimeWindow() && IsInsideManhattanSquare(event)) { | |
| 242 gesture_added = true; | |
| 243 AppendClickGestureEvent(event, gestures); | |
| 244 if (IsInSecondClickTimeWindow() && | |
| 245 IsSecondClickInsideManhattanSquare(event)) | |
| 246 AppendDoubleClickGestureEvent(event, gestures); | |
| 247 last_click_time_ = last_touch_time_; | |
| 248 last_click_position_ = last_touch_position_; | |
| 249 } | |
| 250 Reset(); | |
| 251 return gesture_added; | |
| 252 } | |
| 253 | |
| 254 bool GestureRecognizer::IsClickOrScroll(const TouchEvent& event, | |
| 255 Gestures* gestures) { | |
| 256 if (IsInClickTimeWindow() && IsInsideManhattanSquare(event)) { | |
| 257 SetState(GS_PENDING_SYNTHETIC_CLICK); | |
| 258 return false; | |
| 259 } | |
| 260 if (event.type() == ui::ET_TOUCH_MOVED && !IsInsideManhattanSquare(event)) { | |
| 261 AppendScrollGestureBegin(event, gestures); | |
| 262 AppendScrollGestureUpdate(event, gestures); | |
| 263 SetState(GS_SCROLL); | |
| 264 return true; | |
| 265 } | |
| 266 return false; | |
| 267 } | |
| 268 | |
| 269 bool GestureRecognizer::InScroll(const TouchEvent& event, Gestures* gestures) { | |
| 270 AppendScrollGestureUpdate(event, gestures); | |
| 271 return true; | |
| 272 } | |
| 273 | |
| 274 bool GestureRecognizer::NoGesture(const TouchEvent&, Gestures*) { | |
| 275 Reset(); | |
| 276 return false; | |
| 277 } | |
| 278 | |
| 279 bool GestureRecognizer::TouchDown(const TouchEvent& event, Gestures* gestures) { | |
| 280 AppendTapDownGestureEvent(event, gestures); | |
| 281 SetState(GS_PENDING_SYNTHETIC_CLICK); | |
| 282 return false; | |
| 283 } | |
| 284 | |
| 285 bool GestureRecognizer::ScrollEnd(const TouchEvent& event, Gestures* gestures) { | |
| 286 if (IsOverMinFlickSpeed() && event.type() != ui::ET_TOUCH_CANCELLED) | |
| 287 AppendScrollGestureEnd(event, gestures, x_velocity_, y_velocity_); | |
| 288 else | |
| 289 AppendScrollGestureEnd(event, gestures, 0.f, 0.f); | |
| 290 SetState(GS_NO_GESTURE); | |
| 291 Reset(); | |
| 292 return false; | |
| 293 } | |
| 294 | |
| 295 } // namespace views | |
| OLD | NEW |