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 #include "views/widget/root_view.h" | |
| 11 #include "views/widget/widget.h" | |
| 12 | |
| 13 namespace { | |
| 14 // TODO(Gajen): Make these configurable in sync with this CL http://code.google. | |
| 15 // com/p/chromium/issues/detail?id=100773. | |
| 16 const double kMaximumTouchDownDurationInSecondsForClick = 0.8; | |
| 17 const double kMinimumTouchDownDurationInSecondsForClick = 0.01; | |
| 18 const double kMaximumSecondsBetweenDoubleClick = 0.7; | |
| 19 const int kMaximumTouchMoveInPixelsForClick = 20; | |
| 20 const float kMinFlickSpeedSquared = 550.f * 550.f; | |
| 21 | |
| 22 } // namespace | |
| 23 | |
| 24 namespace views { | |
| 25 | |
| 26 GestureRecognizer::PassGestureEvent::PassGestureEvent(GestureEvent* event) | |
| 27 : event_(event) { | |
| 28 } | |
| 29 | |
| 30 GestureRecognizer::PassGestureEvent::~PassGestureEvent() { | |
| 31 free(event_); | |
| 32 event_ = NULL; | |
| 33 } | |
| 34 | |
| 35 GestureRecognizer::PassGestureEvent::PassGestureEvent( | |
|
rjkroege
2011/11/09 02:43:30
This seems unnecessary.
Gajen
2011/11/10 15:52:55
Done.
| |
| 36 const PassGestureEvent& event) { | |
| 37 event_ = new GestureEvent( | |
| 38 event.GetGestureEvent()->type(), | |
| 39 event.GetGestureEvent()->x(), | |
| 40 event.GetGestureEvent()->y(), | |
| 41 event.GetGestureEvent()->flags(), | |
| 42 event.GetGestureEvent()->time_stamp(), | |
| 43 event.GetGestureEvent()->delta_x(), | |
| 44 event.GetGestureEvent()->delta_y()); | |
| 45 } | |
| 46 | |
| 47 void GestureRecognizer::PassGestureEvent::operator=( | |
| 48 const PassGestureEvent& event) { | |
| 49 event_ = new GestureEvent( | |
| 50 event.GetGestureEvent()->type(), | |
| 51 event.GetGestureEvent()->x(), | |
| 52 event.GetGestureEvent()->y(), | |
| 53 event.GetGestureEvent()->flags(), | |
| 54 event.GetGestureEvent()->time_stamp(), | |
| 55 event.GetGestureEvent()->delta_x(), | |
| 56 event.GetGestureEvent()->delta_y()); | |
| 57 } | |
| 58 | |
| 59 //////////////////////////////////////////////////////////////////////////////// | |
| 60 // GestureRecognizer Public: | |
| 61 | |
| 62 GestureRecognizer::GestureRecognizer() | |
| 63 : first_touch_time_(0.0), | |
| 64 state_(GestureRecognizer::GS_NO_GESTURE), | |
| 65 last_touch_time_(0.0), | |
| 66 last_click_time_(0.0), | |
| 67 x_velocity_(0.0), | |
| 68 y_velocity_(0.0), | |
| 69 flags_(0) { | |
| 70 const unsigned int first_finger = 0; | |
| 71 const ui::EventType released = ui::ET_TOUCH_RELEASED; | |
| 72 const ui::EventType pressed = ui::ET_TOUCH_PRESSED; | |
| 73 const ui::EventType moved = ui::ET_TOUCH_MOVED; | |
| 74 const ui::EventType stationary = ui::ET_TOUCH_STATIONARY; | |
| 75 const ui::EventType cancelled = ui::ET_TOUCH_CANCELLED; | |
| 76 | |
| 77 AddEdgeFunction(GS_NO_GESTURE, first_finger, pressed, false, | |
| 78 &GestureRecognizer::TouchDown); | |
| 79 AddEdgeFunction(GS_PENDING_SYNTHETIC_CLICK, first_finger, cancelled, false, | |
| 80 &GestureRecognizer::NoGesture); | |
| 81 AddEdgeFunction(GS_PENDING_SYNTHETIC_CLICK, first_finger, released, false, | |
| 82 &GestureRecognizer::Click); | |
| 83 AddEdgeFunction(GS_PENDING_SYNTHETIC_CLICK, first_finger, moved, false, | |
| 84 &GestureRecognizer::IsClickOrScroll); | |
| 85 AddEdgeFunction(GS_PENDING_SYNTHETIC_CLICK, first_finger, stationary, false, | |
| 86 &GestureRecognizer::IsClickOrScroll); | |
| 87 AddEdgeFunction(GS_SCROLL, first_finger, moved, false, | |
| 88 &GestureRecognizer::InScroll); | |
| 89 AddEdgeFunction(GS_SCROLL, first_finger, released, false, | |
| 90 &GestureRecognizer::ScrollEnd); | |
| 91 AddEdgeFunction(GS_SCROLL, first_finger, cancelled, false, | |
| 92 &GestureRecognizer::ScrollEnd); | |
| 93 } | |
| 94 | |
| 95 GestureRecognizer::~GestureRecognizer() { | |
| 96 } | |
| 97 | |
| 98 GestureRecognizer* GestureRecognizer::GetInstance() { | |
| 99 return Singleton<GestureRecognizer>::get(); | |
| 100 } | |
| 101 | |
| 102 bool GestureRecognizer::ProcessTouchEventForGesture(const TouchEvent& event, | |
| 103 View* source, | |
| 104 ui::TouchStatus status) { | |
| 105 if (status != ui::TOUCH_STATUS_UNKNOWN) | |
| 106 return false; // The event was consumed by a touch sequence. | |
| 107 | |
| 108 // Get the GestureEvent list processed from GestureManager. | |
| 109 scoped_ptr<Gestures> gestures; | |
| 110 gestures.reset(StartGestureRecognitionProcess(event, | |
| 111 false)); | |
| 112 if (reinterpret_cast<views::internal::RootView* >( | |
|
rjkroege
2011/11/09 02:43:30
this code can be moved into the RootView.
Gajen
2011/11/10 15:52:55
Done.
| |
| 113 source->GetWidget()->GetRootView())->ForwardGestureEvents( | |
| 114 gestures.release())) { | |
| 115 // All gesture events got comsumed, hence no need of Mouse emulation. | |
| 116 return false; | |
| 117 } else { | |
| 118 // For views not handling GestureEvents would need mouse events. | |
| 119 return GestureManager::ProcessTouchEventForGesture(event, source, status); | |
| 120 } | |
| 121 } | |
| 122 | |
| 123 void GestureRecognizer::Reset() { | |
| 124 first_touch_time_ = 0.0; | |
| 125 state_ = GestureRecognizer::GS_NO_GESTURE; | |
| 126 last_touch_time_ = 0.0; | |
| 127 last_touch_position_.SetPoint(0, 0); | |
| 128 x_velocity_ = 0.0; | |
| 129 y_velocity_ = 0.0; | |
| 130 } | |
| 131 | |
| 132 //////////////////////////////////////////////////////////////////////////////// | |
| 133 // GestureRecognizer Private: | |
| 134 | |
| 135 GestureRecognizer::Gestures* GestureRecognizer::StartGestureRecognitionProcess( | |
| 136 const TouchEvent& event, | |
| 137 bool touch_handled) { | |
| 138 flags_ = event.flags(); | |
| 139 scoped_ptr<Gestures> gestures(new Gestures()); | |
| 140 UpdateValues(event); | |
| 141 GestureTransitionFunctionMap::iterator it = edge_functions_.find( | |
| 142 Signature(state_, event.identity(), event.type(), touch_handled)); | |
| 143 if (edge_functions_.end() != it) { | |
| 144 GestureTransitionFunction ef = it->second; | |
| 145 ((*this).*ef)(event, gestures.get()); | |
| 146 } | |
| 147 return gestures.release(); | |
| 148 } | |
| 149 | |
| 150 GestureRecognizer::TouchState GestureRecognizer::TouchEventTypeToTouchState( | |
| 151 ui::EventType type) { | |
| 152 switch (type) { | |
| 153 case ui::ET_TOUCH_RELEASED: return TS_RELEASED; | |
| 154 case ui::ET_TOUCH_PRESSED: return TS_PRESSED; | |
| 155 case ui::ET_TOUCH_MOVED: return TS_MOVED; | |
| 156 case ui::ET_TOUCH_STATIONARY: return TS_STATIONARY; | |
| 157 case ui::ET_TOUCH_CANCELLED: return TS_CANCELLED; | |
| 158 default: | |
| 159 VLOG(1) << "Unknown Touch Event type"; | |
| 160 } | |
| 161 return TS_UNKNOWN; | |
| 162 } | |
| 163 | |
| 164 // Builds a signature. Signatures are assembled by joining together | |
| 165 // multiple bits. | |
| 166 // 1 LSB bit so that the computed signature is always greater than 0 | |
| 167 // 3 bits for the |type|. | |
| 168 // 1 bit for |touch_handled| | |
| 169 // 12 bits for |touch_id| | |
| 170 // 15 bits for the |gesture_state|. | |
| 171 unsigned int GestureRecognizer::Signature(GestureState gesture_state, | |
| 172 unsigned int touch_id, ui::EventType type, | |
| 173 bool touch_handled) { | |
| 174 CHECK((touch_id & 0xfff) == touch_id); | |
| 175 TouchState touch_state = TouchEventTypeToTouchState(type); | |
| 176 return 1 + ((touch_state & 0x7) << 1 | (touch_handled ? 1 << 4 : 0) | | |
| 177 ((touch_id & 0xfff) << 5) | (gesture_state << 17)); | |
| 178 } | |
| 179 | |
| 180 void GestureRecognizer::AddEdgeFunction(GestureState state, | |
| 181 unsigned int touch_id, | |
| 182 ui::EventType type, | |
| 183 bool touch_handled, | |
| 184 GestureTransitionFunction function) { | |
| 185 edge_functions_[Signature(state, touch_id, type, touch_handled)] = function; | |
| 186 } | |
| 187 | |
| 188 bool GestureRecognizer::IsInClickTimeWindow() { | |
| 189 double duration(last_touch_time_ - first_touch_time_); | |
| 190 return duration >= kMinimumTouchDownDurationInSecondsForClick && | |
| 191 duration < kMaximumTouchDownDurationInSecondsForClick; | |
| 192 } | |
| 193 | |
| 194 bool GestureRecognizer::IsInSecondClickTimeWindow() { | |
| 195 double duration(last_touch_time_ - last_click_time_); | |
| 196 return duration < kMaximumSecondsBetweenDoubleClick; | |
| 197 } | |
| 198 | |
| 199 bool GestureRecognizer::IsInsideManhattanSquare(const TouchEvent& event) { | |
| 200 int manhattanDistance = abs(event.x() - first_touch_position_.x()) + | |
| 201 abs(event.y() - first_touch_position_.y()); | |
| 202 return manhattanDistance < kMaximumTouchMoveInPixelsForClick; | |
| 203 } | |
| 204 | |
| 205 bool GestureRecognizer::IsSecondClickInsideManhattanSquare( | |
| 206 const TouchEvent& event) { | |
| 207 int manhattanDistance = abs(event.x() - last_click_position_.x()) + | |
| 208 abs(event.y() - last_click_position_.y()); | |
| 209 return manhattanDistance < kMaximumTouchMoveInPixelsForClick; | |
| 210 } | |
| 211 | |
| 212 bool GestureRecognizer::IsOverMinFlickSpeed() { | |
| 213 return (x_velocity_ * x_velocity_ + y_velocity_ * y_velocity_) > | |
| 214 kMinFlickSpeedSquared; | |
| 215 } | |
| 216 | |
| 217 void GestureRecognizer::AppendTapDownGestureEvent(const TouchEvent& event, | |
| 218 Gestures* gestures) { | |
| 219 gestures->push_back(PassGestureEvent(new GestureEvent(ui::ET_GESTURE_TAP_DOWN, | |
| 220 first_touch_position_.x(), | |
| 221 first_touch_position_.y(), | |
| 222 event.flags(), | |
| 223 base::Time::FromDoubleT(last_touch_time_), | |
| 224 0.f, 0.f))); | |
| 225 } | |
| 226 | |
| 227 void GestureRecognizer::AppendClickGestureEvent(const TouchEvent& event, | |
| 228 Gestures* gestures) { | |
| 229 gestures->push_back(PassGestureEvent(new GestureEvent(ui::ET_GESTURE_TAP, | |
| 230 first_touch_position_.x(), | |
| 231 first_touch_position_.y(), | |
| 232 event.flags(), | |
| 233 base::Time::FromDoubleT(last_touch_time_), | |
| 234 0.f, 0.f))); | |
| 235 } | |
| 236 | |
| 237 void GestureRecognizer::AppendDoubleClickGestureEvent(const TouchEvent& event, | |
| 238 Gestures* gestures) { | |
| 239 gestures->push_back(PassGestureEvent(new GestureEvent( | |
| 240 ui::ET_GESTURE_DOUBLE_TAP, | |
| 241 first_touch_position_.x(), | |
| 242 first_touch_position_.y(), | |
| 243 event.flags(), | |
| 244 base::Time::FromDoubleT(last_touch_time_), | |
| 245 0.f, 0.f))); | |
| 246 } | |
| 247 | |
| 248 void GestureRecognizer::AppendScrollGestureBegin(const TouchEvent& event, | |
| 249 Gestures* gestures) { | |
| 250 gestures->push_back(PassGestureEvent(new GestureEvent( | |
| 251 ui::ET_GESTURE_SCROLL_BEGIN, | |
| 252 event.x(), | |
| 253 event.y(), | |
| 254 event.flags(), | |
| 255 base::Time::FromDoubleT(last_touch_time_), | |
| 256 0.f, 0.f))); | |
| 257 } | |
| 258 | |
| 259 void GestureRecognizer::AppendScrollGestureEnd(const TouchEvent& event, | |
| 260 Gestures* gestures, | |
| 261 float x_velocity, | |
| 262 float y_velocity) { | |
| 263 gestures->push_back(PassGestureEvent(new GestureEvent( | |
| 264 ui::ET_GESTURE_SCROLL_END, | |
| 265 event.x(), | |
| 266 event.y(), | |
| 267 event.flags(), | |
| 268 base::Time::FromDoubleT(last_touch_time_), | |
| 269 x_velocity, y_velocity))); | |
| 270 } | |
| 271 | |
| 272 void GestureRecognizer:: AppendScrollGestureUpdate(const TouchEvent& event, | |
| 273 Gestures* gestures) { | |
| 274 float delta_x(event.x() - first_touch_position_.x()); | |
| 275 float delta_y(event.y() - first_touch_position_.y()); | |
| 276 | |
| 277 gestures->push_back(PassGestureEvent(new GestureEvent( | |
| 278 ui::ET_GESTURE_SCROLL_UPDATE, | |
| 279 event.x(), | |
| 280 event.y(), | |
| 281 event.flags(), | |
| 282 base::Time::FromDoubleT(last_touch_time_), | |
| 283 delta_x, delta_y))); | |
| 284 | |
| 285 first_touch_position_ = event.location(); | |
| 286 } | |
| 287 | |
| 288 void GestureRecognizer::UpdateValues(const TouchEvent& event) { | |
| 289 if (state_ != GS_NO_GESTURE && event.type() == ui::ET_TOUCH_MOVED) { | |
| 290 double interval(event.time_stamp().ToDoubleT() - last_touch_time_); | |
| 291 x_velocity_ = (event.x() - last_touch_position_.x()) / interval; | |
| 292 y_velocity_ = (event.y() - last_touch_position_.y()) / interval; | |
| 293 } | |
| 294 last_touch_time_ = event.time_stamp().ToDoubleT(); | |
| 295 last_touch_position_ = event.location(); | |
| 296 if (state_ == GS_NO_GESTURE) { | |
| 297 first_touch_time_ = last_touch_time_; | |
| 298 first_touch_position_ = event.location(); | |
| 299 x_velocity_ = 0.0; | |
| 300 y_velocity_ = 0.0; | |
| 301 } | |
| 302 } | |
| 303 | |
| 304 bool GestureRecognizer::Click(const TouchEvent& event, Gestures* gestures) { | |
| 305 bool gesture_added = false; | |
| 306 if (IsInClickTimeWindow() && IsInsideManhattanSquare(event)) { | |
| 307 gesture_added = true; | |
| 308 AppendClickGestureEvent(event, gestures); | |
| 309 if (IsInSecondClickTimeWindow() && | |
| 310 IsSecondClickInsideManhattanSquare(event)) | |
| 311 AppendDoubleClickGestureEvent(event, gestures); | |
| 312 last_click_time_ = last_touch_time_; | |
| 313 last_click_position_ = last_touch_position_; | |
| 314 } | |
| 315 Reset(); | |
| 316 return gesture_added; | |
| 317 } | |
| 318 | |
| 319 bool GestureRecognizer::IsClickOrScroll(const TouchEvent& event, | |
| 320 Gestures* gestures) { | |
| 321 if (IsInClickTimeWindow() && IsInsideManhattanSquare(event)) { | |
| 322 SetState(GS_PENDING_SYNTHETIC_CLICK); | |
| 323 return false; | |
| 324 } | |
| 325 if (event.type() == ui::ET_TOUCH_MOVED && !IsInsideManhattanSquare(event)) { | |
| 326 AppendScrollGestureBegin(event, gestures); | |
| 327 AppendScrollGestureUpdate(event, gestures); | |
| 328 SetState(GS_SCROLL); | |
| 329 return true; | |
| 330 } | |
| 331 return false; | |
| 332 } | |
| 333 | |
| 334 bool GestureRecognizer::InScroll(const TouchEvent& event, Gestures* gestures) { | |
| 335 AppendScrollGestureUpdate(event, gestures); | |
| 336 return true; | |
| 337 } | |
| 338 | |
| 339 bool GestureRecognizer::NoGesture(const TouchEvent&, Gestures*) { | |
| 340 Reset(); | |
| 341 return false; | |
| 342 } | |
| 343 | |
| 344 bool GestureRecognizer::TouchDown(const TouchEvent& event, Gestures* gestures) { | |
| 345 AppendTapDownGestureEvent(event, gestures); | |
| 346 SetState(GS_PENDING_SYNTHETIC_CLICK); | |
| 347 return false; | |
| 348 } | |
| 349 | |
| 350 bool GestureRecognizer::ScrollEnd(const TouchEvent& event, Gestures* gestures) { | |
| 351 if (IsOverMinFlickSpeed() && event.type() != ui::ET_TOUCH_CANCELLED) | |
| 352 AppendScrollGestureEnd(event, gestures, x_velocity_, y_velocity_); | |
| 353 else | |
| 354 AppendScrollGestureEnd(event, gestures, 0.f, 0.f); | |
| 355 SetState(GS_NO_GESTURE); | |
| 356 Reset(); | |
| 357 return false; | |
| 358 } | |
| 359 | |
| 360 } // namespace views | |
| OLD | NEW |