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

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

Powered by Google App Engine
This is Rietveld 408576698