Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(476)

Side by Side Diff: views/touchui/gesture_recognizer.cc

Issue 8364039: Initial views touchui GestureRecognizer support (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: Added gesture tests in view_unittest Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698