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

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

Powered by Google App Engine
This is Rietveld 408576698