summaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authorsadrul@chromium.org <sadrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-28 00:24:30 +0000
committersadrul@chromium.org <sadrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-28 00:24:30 +0000
commit0889c17954b71b24b45a21032cbdada706730a7a (patch)
tree787d61ceaf9021414e9c1675d3c6d4249c11f8c8 /ui
parent2955a8e3b08a0b68058a0b268ccaec8729cbcbf7 (diff)
downloadchromium_src-0889c17954b71b24b45a21032cbdada706730a7a.zip
chromium_src-0889c17954b71b24b45a21032cbdada706730a7a.tar.gz
chromium_src-0889c17954b71b24b45a21032cbdada706730a7a.tar.bz2
aura: Implementation of queued/asynchronous gesture recognition.
The major changes: * Rename existing GestureRecognizer to GestureSequence. * Add a new GestureRecognizer interface (with the same API as the previous GR). * Implement GestureRecognizerAura that uses GestureSequence(s) for recognition gestures. BUG=110230 TEST=aura_unittests:GestureRecognizerTest.AsynchronousGestureRecognition Review URL: https://chromiumcodereview.appspot.com/9129012 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@119533 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui')
-rw-r--r--ui/aura/aura.gyp3
-rw-r--r--ui/aura/event.cc11
-rw-r--r--ui/aura/event.h4
-rw-r--r--ui/aura/gestures/gesture_recognizer.h150
-rw-r--r--ui/aura/gestures/gesture_recognizer_aura.cc (renamed from ui/aura/gestures/gesture_recognizer.cc)192
-rw-r--r--ui/aura/gestures/gesture_recognizer_aura.h190
-rw-r--r--ui/aura/gestures/gesture_recognizer_unittest.cc95
-rw-r--r--ui/aura/root_window.cc2
8 files changed, 441 insertions, 206 deletions
diff --git a/ui/aura/aura.gyp b/ui/aura/aura.gyp
index 19f2828..4c138c8 100644
--- a/ui/aura/aura.gyp
+++ b/ui/aura/aura.gyp
@@ -46,8 +46,9 @@
'client/window_move_client.h',
'client/window_types.h',
'cursor.h',
- 'gestures/gesture_recognizer.cc',
'gestures/gesture_recognizer.h',
+ 'gestures/gesture_recognizer_aura.cc',
+ 'gestures/gesture_recognizer_aura.h',
'root_window_host.h',
'root_window_host_linux.cc',
'root_window_host_mac.h',
diff --git a/ui/aura/event.cc b/ui/aura/event.cc
index 6723e38..c2a0510 100644
--- a/ui/aura/event.cc
+++ b/ui/aura/event.cc
@@ -272,6 +272,17 @@ TouchEvent::TouchEvent(ui::EventType type,
force_(0.0f) {
}
+TouchEvent* TouchEvent::Copy() const {
+#if defined(OS_WIN)
+ if (native_event().message)
+ return new TouchEvent(::CopyNativeEvent(native_event()));
+#else
+ if (native_event())
+ return new TouchEvent(::CopyNativeEvent(native_event()));
+#endif
+ return new TouchEvent(*this, NULL, NULL);
+}
+
KeyEvent::KeyEvent(const base::NativeEvent& native_event, bool is_char)
: Event(native_event,
ui::EventTypeFromNative(native_event),
diff --git a/ui/aura/event.h b/ui/aura/event.h
index 04fe46d..48f3c81 100644
--- a/ui/aura/event.h
+++ b/ui/aura/event.h
@@ -194,6 +194,10 @@ class AURA_EXPORT TouchEvent : public LocatedEvent {
float rotation_angle() const { return rotation_angle_; }
float force() const { return force_; }
+ // Returns a copy of this touch event. Used when queueing events for
+ // asynchronous gesture recognition.
+ TouchEvent* Copy() const;
+
private:
// The identity (typically finger) of the touch starting at 0 and incrementing
// for each separable additional touch that the hardware can detect.
diff --git a/ui/aura/gestures/gesture_recognizer.h b/ui/aura/gestures/gesture_recognizer.h
index 28c472c..300ced4 100644
--- a/ui/aura/gestures/gesture_recognizer.h
+++ b/ui/aura/gestures/gesture_recognizer.h
@@ -6,174 +6,48 @@
#define UI_AURA_GESTURES_GESTURE_RECOGNIZER_H_
#pragma once
-#include <map>
#include <vector>
#include "base/memory/linked_ptr.h"
#include "ui/aura/aura_export.h"
#include "ui/base/events.h"
-#include "ui/gfx/point.h"
namespace aura {
-class TouchEvent;
class GestureEvent;
+class TouchEvent;
+class Window;
-// A GestureRecognizer recognizes gestures from touch sequences.
+// A GestureRecognizer is an abstract base class for conversion of touch events
+// into gestures.
class AURA_EXPORT GestureRecognizer {
public:
- // Gesture state.
- enum GestureState {
- GS_NO_GESTURE,
- GS_PENDING_SYNTHETIC_CLICK,
- GS_SCROLL,
- };
-
- // ui::EventType is mapped to TouchState so it can fit into 3 bits of
- // Signature.
- enum TouchState {
- TS_RELEASED,
- TS_PRESSED,
- TS_MOVED,
- TS_STATIONARY,
- TS_CANCELLED,
- TS_UNKNOWN,
- };
+ static GestureRecognizer* Create();
// List of GestureEvent*.
typedef std::vector<linked_ptr<GestureEvent> > Gestures;
- GestureRecognizer();
- virtual ~GestureRecognizer();
+ virtual ~GestureRecognizer() {}
// Invoked for each touch event that could contribute to the current gesture.
// Returns list of zero or more GestureEvents identified after processing
// TouchEvent.
// Caller would be responsible for freeing up Gestures.
virtual Gestures* ProcessTouchEventForGesture(const TouchEvent& event,
- ui::TouchStatus status);
+ ui::TouchStatus status) = 0;
// Touch-events can be queued to be played back at a later time. The queues
// are identified by the target window.
virtual void QueueTouchEventForGesture(Window* window,
- const TouchEvent& event);
+ const TouchEvent& event) = 0;
// Process the touch-event in the queue for the window. Returns a list of
// zero or more GestureEvents identified after processing the queueud
// TouchEvent. Caller is responsible for freeing up Gestures.
- virtual Gestures* AdvanceTouchQueue(Window* window, bool processed);
-
- // Reset the touch events in the queue for the window.
- virtual void FlushTouchQueue(Window* window);
-
- // Clears the GestureRecognizer to its initial state.
- virtual void Reset();
-
- // Accessor function.
- GestureState GetState() const { return state_; }
-
- private:
- // Gesture signature types for different values of combination (GestureState,
- // touch_id, ui::EventType, touch_handled), see GestureRecognizer::Signature()
- // for more info.
- //
- // Note: New addition of types should be placed as per their Signature value.
- enum GestureSignatureType {
- // For input combination (GS_NO_GESTURE, 0, ui::ET_TOUCH_PRESSED, false).
- GST_NO_GESTURE_FIRST_PRESSED = 0x00000003,
-
- // (GS_PENDING_SYNTHETIC_CLICK, 0, ui::ET_TOUCH_RELEASED, false).
- GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED = 0x00020001,
-
- // (GS_PENDING_SYNTHETIC_CLICK, 0, ui::ET_TOUCH_MOVED, false).
- GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED = 0x00020005,
-
- // (GS_PENDING_SYNTHETIC_CLICK, 0, ui::ET_TOUCH_STATIONARY, false).
- GST_PENDING_SYNTHETIC_CLICK_FIRST_STATIONARY = 0x00020007,
-
- // (GS_PENDING_SYNTHETIC_CLICK, 0, ui::ET_TOUCH_CANCELLED, false).
- GST_PENDING_SYNTHETIC_CLICK_FIRST_CANCELLED = 0x00020009,
-
- // (GS_SCROLL, 0, ui::ET_TOUCH_RELEASED, false).
- GST_SCROLL_FIRST_RELEASED = 0x00040001,
-
- // (GS_SCROLL, 0, ui::ET_TOUCH_MOVED, false).
- GST_SCROLL_FIRST_MOVED = 0x00040005,
-
- // (GS_SCROLL, 0, ui::ET_TOUCH_CANCELLED, false).
- GST_SCROLL_FIRST_CANCELLED = 0x00040009,
- };
-
- // Builds a signature. Signatures are assembled by joining together
- // multiple bits.
- // 1 LSB bit so that the computed signature is always greater than 0
- // 3 bits for the |type|.
- // 1 bit for |touch_handled|
- // 12 bits for |touch_id|
- // 15 bits for the |gesture_state|.
- static unsigned int Signature(GestureState state,
- unsigned int touch_id, ui::EventType type,
- bool touch_handled);
-
- // Various statistical functions to manipulate gestures.
- bool IsInClickTimeWindow();
- bool IsInSecondClickTimeWindow();
- bool IsInsideManhattanSquare(const TouchEvent& event);
- bool IsSecondClickInsideManhattanSquare(const TouchEvent& event);
- bool IsOverMinFlickSpeed();
-
- // Functions to be called to add GestureEvents, after succesful recognition.
- void AppendTapDownGestureEvent(const TouchEvent& event, Gestures* gestures);
- void AppendClickGestureEvent(const TouchEvent& event, Gestures* gestures);
- void AppendDoubleClickGestureEvent(const TouchEvent& event,
- Gestures* gestures);
- void AppendScrollGestureBegin(const TouchEvent& event, Gestures* gestures);
- void AppendScrollGestureEnd(const TouchEvent& event,
- Gestures* gestures,
- float x_velocity, float y_velocity);
- void AppendScrollGestureUpdate(const TouchEvent& event, Gestures* gestures);
-
- void UpdateValues(const TouchEvent& event);
- void SetState(const GestureState state ) { state_ = state; }
-
- // Various GestureTransitionFunctions for a signature.
- // There is, 1:many mapping from GestureTransitionFunction to Signature
- // But a Signature have only one GestureTransitionFunction.
- bool Click(const TouchEvent& event, Gestures* gestures);
- bool InClickOrScroll(const TouchEvent& event, Gestures* gestures);
- bool InScroll(const TouchEvent& event, Gestures* gestures);
- bool NoGesture(const TouchEvent& event, Gestures* gestures);
- bool TouchDown(const TouchEvent& event, Gestures* gestures);
- bool ScrollEnd(const TouchEvent& event, Gestures* gestures);
-
- // Location of first touch event in a touch sequence.
- gfx::Point first_touch_position_;
-
- // Time of first touch event in a touch sequence.
- double first_touch_time_;
-
- // Current state of gesture recognizer.
- GestureState state_;
-
- // Time of current touch event in a touch sequence.
- double last_touch_time_;
-
- // Time of click gesture.
- double last_click_time_;
-
- // Location of click gesture.
- gfx::Point last_click_position_;
-
- // Location of current touch event in a touch sequence.
- gfx::Point last_touch_position_;
-
- // Velocity in x and y direction.
- float x_velocity_;
- float y_velocity_;
-
- // ui::EventFlags.
- int flags_;
+ virtual Gestures* AdvanceTouchQueue(Window* window,
+ bool processed) = 0;
- DISALLOW_COPY_AND_ASSIGN(GestureRecognizer);
+ // Flushes the touch event queue (or removes the queue) for the window.
+ virtual void FlushTouchQueue(Window* window) = 0;
};
} // namespace aura
diff --git a/ui/aura/gestures/gesture_recognizer.cc b/ui/aura/gestures/gesture_recognizer_aura.cc
index 5de5ebc4..7b8b528 100644
--- a/ui/aura/gestures/gesture_recognizer.cc
+++ b/ui/aura/gestures/gesture_recognizer_aura.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "ui/aura/gestures/gesture_recognizer.h"
+#include "ui/aura/gestures/gesture_recognizer_aura.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
@@ -11,14 +11,30 @@
#include "ui/base/events.h"
namespace {
-// TODO(Gajen): Make these configurable in sync with this CL http://code.google.
-// com/p/chromium/issues/detail?id=100773.
+// TODO(girard): Make these configurable in sync with this CL
+// http://crbug.com/100773
const double kMaximumTouchDownDurationInSecondsForClick = 0.8;
const double kMinimumTouchDownDurationInSecondsForClick = 0.01;
const double kMaximumSecondsBetweenDoubleClick = 0.7;
const int kMaximumTouchMoveInPixelsForClick = 20;
const float kMinFlickSpeedSquared = 550.f * 550.f;
+// This is used to pop a std::queue when returning from a function.
+class ScopedPop {
+ public:
+ ScopedPop(std::queue<aura::TouchEvent*>* queue) : queue_(queue) {
+ }
+
+ ~ScopedPop() {
+ delete queue_->front();
+ queue_->pop();
+ }
+
+ private:
+ std::queue<aura::TouchEvent*>* queue_;
+ DISALLOW_COPY_AND_ASSIGN(ScopedPop);
+};
+
} // namespace
namespace aura {
@@ -26,32 +42,32 @@ namespace aura {
namespace {
// Get equivalent TouchState from EventType |type|.
-GestureRecognizer::TouchState TouchEventTypeToTouchState(ui::EventType type) {
+GestureSequence::TouchState TouchEventTypeToTouchState(ui::EventType type) {
switch (type) {
case ui::ET_TOUCH_RELEASED:
- return GestureRecognizer::TS_RELEASED;
+ return GestureSequence::TS_RELEASED;
case ui::ET_TOUCH_PRESSED:
- return GestureRecognizer::TS_PRESSED;
+ return GestureSequence::TS_PRESSED;
case ui::ET_TOUCH_MOVED:
- return GestureRecognizer::TS_MOVED;
+ return GestureSequence::TS_MOVED;
case ui::ET_TOUCH_STATIONARY:
- return GestureRecognizer::TS_STATIONARY;
+ return GestureSequence::TS_STATIONARY;
case ui::ET_TOUCH_CANCELLED:
- return GestureRecognizer::TS_CANCELLED;
+ return GestureSequence::TS_CANCELLED;
default:
VLOG(1) << "Unknown Touch Event type";
}
- return GestureRecognizer::TS_UNKNOWN;
+ return GestureSequence::TS_UNKNOWN;
}
} // namespace
////////////////////////////////////////////////////////////////////////////////
-// GestureRecognizer Public:
+// GestureSequence Public:
-GestureRecognizer::GestureRecognizer()
+GestureSequence::GestureSequence()
: first_touch_time_(0.0),
- state_(GestureRecognizer::GS_NO_GESTURE),
+ state_(GestureSequence::GS_NO_GESTURE),
last_touch_time_(0.0),
last_click_time_(0.0),
x_velocity_(0.0),
@@ -59,10 +75,10 @@ GestureRecognizer::GestureRecognizer()
flags_(0) {
}
-GestureRecognizer::~GestureRecognizer() {
+GestureSequence::~GestureSequence() {
}
-GestureRecognizer::Gestures* GestureRecognizer::ProcessTouchEventForGesture(
+GestureSequence::Gestures* GestureSequence::ProcessTouchEventForGesture(
const TouchEvent& event,
ui::TouchStatus status) {
if (status != ui::TOUCH_STATUS_UNKNOWN)
@@ -95,27 +111,9 @@ GestureRecognizer::Gestures* GestureRecognizer::ProcessTouchEventForGesture(
return gestures.release();
}
-void GestureRecognizer::QueueTouchEventForGesture(Window* window,
- const TouchEvent& event) {
- // TODO(sad):
- NOTIMPLEMENTED();
-}
-
-GestureRecognizer::Gestures* GestureRecognizer::AdvanceTouchQueue(
- Window* window, bool processed) {
- // TODO(sad):
- NOTIMPLEMENTED();
- return NULL;
-}
-
-void GestureRecognizer::FlushTouchQueue(Window* window) {
- // TODO(sad):
- NOTIMPLEMENTED();
-}
-
-void GestureRecognizer::Reset() {
+void GestureSequence::Reset() {
first_touch_time_ = 0.0;
- state_ = GestureRecognizer::GS_NO_GESTURE;
+ state_ = GestureSequence::GS_NO_GESTURE;
last_touch_time_ = 0.0;
last_touch_position_.SetPoint(0, 0);
x_velocity_ = 0.0;
@@ -123,48 +121,49 @@ void GestureRecognizer::Reset() {
}
////////////////////////////////////////////////////////////////////////////////
-// GestureRecognizer Private:
+// GestureSequence Private:
-unsigned int GestureRecognizer::Signature(GestureState gesture_state,
- unsigned int touch_id, ui::EventType type,
- bool touch_handled) {
+unsigned int GestureSequence::Signature(GestureState gesture_state,
+ unsigned int touch_id,
+ ui::EventType type,
+ bool touch_handled) {
CHECK((touch_id & 0xfff) == touch_id);
TouchState touch_state = TouchEventTypeToTouchState(type);
return 1 + ((touch_state & 0x7) << 1 | (touch_handled ? 1 << 4 : 0) |
((touch_id & 0xfff) << 5) | (gesture_state << 17));
}
-bool GestureRecognizer::IsInClickTimeWindow() {
+bool GestureSequence::IsInClickTimeWindow() {
double duration(last_touch_time_ - first_touch_time_);
return duration >= kMinimumTouchDownDurationInSecondsForClick &&
duration < kMaximumTouchDownDurationInSecondsForClick;
}
-bool GestureRecognizer::IsInSecondClickTimeWindow() {
+bool GestureSequence::IsInSecondClickTimeWindow() {
double duration(last_touch_time_ - last_click_time_);
return duration < kMaximumSecondsBetweenDoubleClick;
}
-bool GestureRecognizer::IsInsideManhattanSquare(const TouchEvent& event) {
+bool GestureSequence::IsInsideManhattanSquare(const TouchEvent& event) {
int manhattanDistance = abs(event.x() - first_touch_position_.x()) +
abs(event.y() - first_touch_position_.y());
return manhattanDistance < kMaximumTouchMoveInPixelsForClick;
}
-bool GestureRecognizer::IsSecondClickInsideManhattanSquare(
+bool GestureSequence::IsSecondClickInsideManhattanSquare(
const TouchEvent& event) {
int manhattanDistance = abs(event.x() - last_click_position_.x()) +
abs(event.y() - last_click_position_.y());
return manhattanDistance < kMaximumTouchMoveInPixelsForClick;
}
-bool GestureRecognizer::IsOverMinFlickSpeed() {
+bool GestureSequence::IsOverMinFlickSpeed() {
return (x_velocity_ * x_velocity_ + y_velocity_ * y_velocity_) >
kMinFlickSpeedSquared;
}
-void GestureRecognizer::AppendTapDownGestureEvent(const TouchEvent& event,
- Gestures* gestures) {
+void GestureSequence::AppendTapDownGestureEvent(const TouchEvent& event,
+ Gestures* gestures) {
gestures->push_back(linked_ptr<GestureEvent>(new GestureEvent(
ui::ET_GESTURE_TAP_DOWN,
first_touch_position_.x(),
@@ -174,8 +173,8 @@ void GestureRecognizer::AppendTapDownGestureEvent(const TouchEvent& event,
0.f, 0.f)));
}
-void GestureRecognizer::AppendClickGestureEvent(const TouchEvent& event,
- Gestures* gestures) {
+void GestureSequence::AppendClickGestureEvent(const TouchEvent& event,
+ Gestures* gestures) {
gestures->push_back(linked_ptr<GestureEvent>(new GestureEvent(
ui::ET_GESTURE_TAP,
first_touch_position_.x(),
@@ -185,8 +184,8 @@ void GestureRecognizer::AppendClickGestureEvent(const TouchEvent& event,
0.f, 0.f)));
}
-void GestureRecognizer::AppendDoubleClickGestureEvent(const TouchEvent& event,
- Gestures* gestures) {
+void GestureSequence::AppendDoubleClickGestureEvent(const TouchEvent& event,
+ Gestures* gestures) {
gestures->push_back(linked_ptr<GestureEvent>(new GestureEvent(
ui::ET_GESTURE_DOUBLE_TAP,
first_touch_position_.x(),
@@ -196,8 +195,8 @@ void GestureRecognizer::AppendDoubleClickGestureEvent(const TouchEvent& event,
0.f, 0.f)));
}
-void GestureRecognizer::AppendScrollGestureBegin(const TouchEvent& event,
- Gestures* gestures) {
+void GestureSequence::AppendScrollGestureBegin(const TouchEvent& event,
+ Gestures* gestures) {
gestures->push_back(linked_ptr<GestureEvent>(new GestureEvent(
ui::ET_GESTURE_SCROLL_BEGIN,
event.x(),
@@ -207,10 +206,10 @@ void GestureRecognizer::AppendScrollGestureBegin(const TouchEvent& event,
0.f, 0.f)));
}
-void GestureRecognizer::AppendScrollGestureEnd(const TouchEvent& event,
- Gestures* gestures,
- float x_velocity,
- float y_velocity) {
+void GestureSequence::AppendScrollGestureEnd(const TouchEvent& event,
+ Gestures* gestures,
+ float x_velocity,
+ float y_velocity) {
gestures->push_back(linked_ptr<GestureEvent>(new GestureEvent(
ui::ET_GESTURE_SCROLL_END,
event.x(),
@@ -220,8 +219,8 @@ void GestureRecognizer::AppendScrollGestureEnd(const TouchEvent& event,
x_velocity, y_velocity)));
}
-void GestureRecognizer:: AppendScrollGestureUpdate(const TouchEvent& event,
- Gestures* gestures) {
+void GestureSequence:: AppendScrollGestureUpdate(const TouchEvent& event,
+ Gestures* gestures) {
float delta_x(event.x() - first_touch_position_.x());
float delta_y(event.y() - first_touch_position_.y());
@@ -236,7 +235,7 @@ void GestureRecognizer:: AppendScrollGestureUpdate(const TouchEvent& event,
first_touch_position_ = event.location();
}
-void GestureRecognizer::UpdateValues(const TouchEvent& event) {
+void GestureSequence::UpdateValues(const TouchEvent& event) {
if (state_ != GS_NO_GESTURE && event.type() == ui::ET_TOUCH_MOVED) {
double interval(event.time_stamp().InSecondsF() - last_touch_time_);
x_velocity_ = (event.x() - last_touch_position_.x()) / interval;
@@ -252,7 +251,7 @@ void GestureRecognizer::UpdateValues(const TouchEvent& event) {
}
}
-bool GestureRecognizer::Click(const TouchEvent& event, Gestures* gestures) {
+bool GestureSequence::Click(const TouchEvent& event, Gestures* gestures) {
bool gesture_added = false;
if (IsInClickTimeWindow() && IsInsideManhattanSquare(event)) {
gesture_added = true;
@@ -267,8 +266,8 @@ bool GestureRecognizer::Click(const TouchEvent& event, Gestures* gestures) {
return gesture_added;
}
-bool GestureRecognizer::InClickOrScroll(const TouchEvent& event,
- Gestures* gestures) {
+bool GestureSequence::InClickOrScroll(const TouchEvent& event,
+ Gestures* gestures) {
if (IsInClickTimeWindow() && IsInsideManhattanSquare(event)) {
SetState(GS_PENDING_SYNTHETIC_CLICK);
return false;
@@ -282,23 +281,23 @@ bool GestureRecognizer::InClickOrScroll(const TouchEvent& event,
return false;
}
-bool GestureRecognizer::InScroll(const TouchEvent& event, Gestures* gestures) {
+bool GestureSequence::InScroll(const TouchEvent& event, Gestures* gestures) {
AppendScrollGestureUpdate(event, gestures);
return true;
}
-bool GestureRecognizer::NoGesture(const TouchEvent&, Gestures*) {
+bool GestureSequence::NoGesture(const TouchEvent&, Gestures*) {
Reset();
return false;
}
-bool GestureRecognizer::TouchDown(const TouchEvent& event, Gestures* gestures) {
+bool GestureSequence::TouchDown(const TouchEvent& event, Gestures* gestures) {
AppendTapDownGestureEvent(event, gestures);
SetState(GS_PENDING_SYNTHETIC_CLICK);
return false;
}
-bool GestureRecognizer::ScrollEnd(const TouchEvent& event, Gestures* gestures) {
+bool GestureSequence::ScrollEnd(const TouchEvent& event, Gestures* gestures) {
if (IsOverMinFlickSpeed() && event.type() != ui::ET_TOUCH_CANCELLED)
AppendScrollGestureEnd(event, gestures, x_velocity_, y_velocity_);
else
@@ -308,4 +307,65 @@ bool GestureRecognizer::ScrollEnd(const TouchEvent& event, Gestures* gestures) {
return false;
}
+////////////////////////////////////////////////////////////////////////////////
+// GestureRecognizerAura, public:
+
+GestureRecognizerAura::GestureRecognizerAura()
+ : default_sequence_(new GestureSequence()) {
+}
+
+GestureRecognizerAura::~GestureRecognizerAura() {
+}
+
+GestureSequence::Gestures* GestureRecognizerAura::ProcessTouchEventForGesture(
+ const TouchEvent& event,
+ ui::TouchStatus status) {
+ return default_sequence_->ProcessTouchEventForGesture(event, status);
+}
+
+void GestureRecognizerAura::QueueTouchEventForGesture(Window* window,
+ const TouchEvent& event) {
+ if (!event_queue_[window])
+ event_queue_[window] = new std::queue<TouchEvent*>();
+ event_queue_[window]->push(event.Copy());
+}
+
+GestureSequence::Gestures* GestureRecognizerAura::AdvanceTouchQueue(
+ Window* window,
+ bool processed) {
+ if (!event_queue_[window]) {
+ LOG(ERROR) << "Trying to advance an empty gesture queue for " << window;
+ return NULL;
+ }
+
+ ScopedPop pop(event_queue_[window]);
+ TouchEvent* event = event_queue_[window]->front();
+
+ GestureSequence* sequence = window_sequence_[window];
+ if (!sequence) {
+ sequence = new GestureSequence();
+ window_sequence_[window] = sequence;
+ }
+
+ return sequence->ProcessTouchEventForGesture(*event,
+ processed ? ui::TOUCH_STATUS_CONTINUE : ui::TOUCH_STATUS_UNKNOWN);
+}
+
+void GestureRecognizerAura::FlushTouchQueue(Window* window) {
+ if (window_sequence_[window]) {
+ delete window_sequence_[window];
+ window_sequence_.erase(window);
+ }
+
+ if (event_queue_[window]) {
+ delete event_queue_[window];
+ event_queue_.erase(window);
+ }
+}
+
+// GestureRecognizer, static
+GestureRecognizer* GestureRecognizer::Create() {
+ return new GestureRecognizerAura();
+}
+
} // namespace aura
diff --git a/ui/aura/gestures/gesture_recognizer_aura.h b/ui/aura/gestures/gesture_recognizer_aura.h
new file mode 100644
index 0000000..59fcf25
--- /dev/null
+++ b/ui/aura/gestures/gesture_recognizer_aura.h
@@ -0,0 +1,190 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_AURA_GESTURES_GESTURE_RECOGNIZER_AURA_H_
+#define UI_AURA_GESTURES_GESTURE_RECOGNIZER_AURA_H_
+#pragma once
+
+#include <map>
+#include <queue>
+#include <vector>
+
+#include "base/memory/linked_ptr.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/aura/aura_export.h"
+#include "ui/aura/gestures/gesture_recognizer.h"
+#include "ui/base/events.h"
+#include "ui/gfx/point.h"
+
+namespace aura {
+class TouchEvent;
+class GestureEvent;
+
+// A GestureSequence recognizes gestures from touch sequences.
+class AURA_EXPORT GestureSequence {
+ public:
+ // Gesture state.
+ enum GestureState {
+ GS_NO_GESTURE,
+ GS_PENDING_SYNTHETIC_CLICK,
+ GS_SCROLL,
+ };
+
+ // ui::EventType is mapped to TouchState so it can fit into 3 bits of
+ // Signature.
+ enum TouchState {
+ TS_RELEASED,
+ TS_PRESSED,
+ TS_MOVED,
+ TS_STATIONARY,
+ TS_CANCELLED,
+ TS_UNKNOWN,
+ };
+
+ GestureSequence();
+ virtual ~GestureSequence();
+
+ typedef GestureRecognizer::Gestures Gestures;
+
+ // Invoked for each touch event that could contribute to the current gesture.
+ // Returns list of zero or more GestureEvents identified after processing
+ // TouchEvent.
+ // Caller would be responsible for freeing up Gestures.
+ virtual Gestures* ProcessTouchEventForGesture(const TouchEvent& event,
+ ui::TouchStatus status);
+
+ private:
+ // Gesture signature types for different values of combination (GestureState,
+ // touch_id, ui::EventType, touch_handled), see GestureSequence::Signature()
+ // for more info.
+ //
+ // Note: New addition of types should be placed as per their Signature value.
+ enum GestureSignatureType {
+ // For input combination (GS_NO_GESTURE, 0, ui::ET_TOUCH_PRESSED, false).
+ GST_NO_GESTURE_FIRST_PRESSED = 0x00000003,
+
+ // (GS_PENDING_SYNTHETIC_CLICK, 0, ui::ET_TOUCH_RELEASED, false).
+ GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED = 0x00020001,
+
+ // (GS_PENDING_SYNTHETIC_CLICK, 0, ui::ET_TOUCH_MOVED, false).
+ GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED = 0x00020005,
+
+ // (GS_PENDING_SYNTHETIC_CLICK, 0, ui::ET_TOUCH_STATIONARY, false).
+ GST_PENDING_SYNTHETIC_CLICK_FIRST_STATIONARY = 0x00020007,
+
+ // (GS_PENDING_SYNTHETIC_CLICK, 0, ui::ET_TOUCH_CANCELLED, false).
+ GST_PENDING_SYNTHETIC_CLICK_FIRST_CANCELLED = 0x00020009,
+
+ // (GS_SCROLL, 0, ui::ET_TOUCH_RELEASED, false).
+ GST_SCROLL_FIRST_RELEASED = 0x00040001,
+
+ // (GS_SCROLL, 0, ui::ET_TOUCH_MOVED, false).
+ GST_SCROLL_FIRST_MOVED = 0x00040005,
+
+ // (GS_SCROLL, 0, ui::ET_TOUCH_CANCELLED, false).
+ GST_SCROLL_FIRST_CANCELLED = 0x00040009,
+ };
+
+ // Builds a signature. Signatures are assembled by joining together
+ // multiple bits.
+ // 1 LSB bit so that the computed signature is always greater than 0
+ // 3 bits for the |type|.
+ // 1 bit for |touch_handled|
+ // 12 bits for |touch_id|
+ // 15 bits for the |gesture_state|.
+ static unsigned int Signature(GestureState state,
+ unsigned int touch_id, ui::EventType type,
+ bool touch_handled);
+
+ void Reset();
+
+ // Various statistical functions to manipulate gestures.
+ bool IsInClickTimeWindow();
+ bool IsInSecondClickTimeWindow();
+ bool IsInsideManhattanSquare(const TouchEvent& event);
+ bool IsSecondClickInsideManhattanSquare(const TouchEvent& event);
+ bool IsOverMinFlickSpeed();
+
+ // Functions to be called to add GestureEvents, after succesful recognition.
+ void AppendTapDownGestureEvent(const TouchEvent& event, Gestures* gestures);
+ void AppendClickGestureEvent(const TouchEvent& event, Gestures* gestures);
+ void AppendDoubleClickGestureEvent(const TouchEvent& event,
+ Gestures* gestures);
+ void AppendScrollGestureBegin(const TouchEvent& event, Gestures* gestures);
+ void AppendScrollGestureEnd(const TouchEvent& event,
+ Gestures* gestures,
+ float x_velocity, float y_velocity);
+ void AppendScrollGestureUpdate(const TouchEvent& event, Gestures* gestures);
+
+ void UpdateValues(const TouchEvent& event);
+ void SetState(const GestureState state ) { state_ = state; }
+
+ // Various GestureTransitionFunctions for a signature.
+ // There is, 1:many mapping from GestureTransitionFunction to Signature
+ // But a Signature have only one GestureTransitionFunction.
+ bool Click(const TouchEvent& event, Gestures* gestures);
+ bool InClickOrScroll(const TouchEvent& event, Gestures* gestures);
+ bool InScroll(const TouchEvent& event, Gestures* gestures);
+ bool NoGesture(const TouchEvent& event, Gestures* gestures);
+ bool TouchDown(const TouchEvent& event, Gestures* gestures);
+ bool ScrollEnd(const TouchEvent& event, Gestures* gestures);
+
+ // Location of first touch event in a touch sequence.
+ gfx::Point first_touch_position_;
+
+ // Time of first touch event in a touch sequence.
+ double first_touch_time_;
+
+ // Current state of gesture recognizer.
+ GestureState state_;
+
+ // Time of current touch event in a touch sequence.
+ double last_touch_time_;
+
+ // Time of click gesture.
+ double last_click_time_;
+
+ // Location of click gesture.
+ gfx::Point last_click_position_;
+
+ // Location of current touch event in a touch sequence.
+ gfx::Point last_touch_position_;
+
+ // Velocity in x and y direction.
+ float x_velocity_;
+ float y_velocity_;
+
+ // ui::EventFlags.
+ int flags_;
+
+ DISALLOW_COPY_AND_ASSIGN(GestureSequence);
+};
+
+class AURA_EXPORT GestureRecognizerAura : public GestureRecognizer {
+ public:
+ GestureRecognizerAura();
+ virtual ~GestureRecognizerAura();
+
+ private:
+ // Overridden from GestureRecognizer
+ virtual Gestures* ProcessTouchEventForGesture(
+ const TouchEvent& event,
+ ui::TouchStatus status) OVERRIDE;
+ virtual void QueueTouchEventForGesture(Window* window,
+ const TouchEvent& event) OVERRIDE;
+ virtual Gestures* AdvanceTouchQueue(Window* window, bool processed) OVERRIDE;
+ virtual void FlushTouchQueue(Window* window) OVERRIDE;
+
+ scoped_ptr<GestureSequence> default_sequence_;
+
+ typedef std::queue<TouchEvent*> TouchEventQueue;
+ std::map<Window*, TouchEventQueue*> event_queue_;
+ std::map<Window*, GestureSequence*> window_sequence_;
+
+ DISALLOW_COPY_AND_ASSIGN(GestureRecognizerAura);
+};
+
+} // namespace aura
+
+#endif // UI_AURA_GESTURES_GESTURE_RECOGNIZER_AURA_H_
diff --git a/ui/aura/gestures/gesture_recognizer_unittest.cc b/ui/aura/gestures/gesture_recognizer_unittest.cc
index bc65b71..b436ac4 100644
--- a/ui/aura/gestures/gesture_recognizer_unittest.cc
+++ b/ui/aura/gestures/gesture_recognizer_unittest.cc
@@ -97,6 +97,27 @@ class GestureEventConsumeDelegate : public TestWindowDelegate {
DISALLOW_COPY_AND_ASSIGN(GestureEventConsumeDelegate);
};
+class QueueTouchEventDelegate : public GestureEventConsumeDelegate {
+ public:
+ QueueTouchEventDelegate() : window_(NULL) {}
+ virtual ~QueueTouchEventDelegate() {}
+
+ virtual ui::TouchStatus OnTouchEvent(TouchEvent* event) OVERRIDE {
+ return ui::TOUCH_STATUS_QUEUED;
+ }
+
+ void ReceivedAck() {
+ RootWindow::GetInstance()->AdvanceQueuedTouchEvent(window_, false);
+ }
+
+ void set_window(Window* w) { window_ = w; }
+
+ private:
+ Window* window_;
+
+ DISALLOW_COPY_AND_ASSIGN(QueueTouchEventDelegate);
+};
+
// A delegate that ignores gesture events but keeps track of [synthetic] mouse
// events.
class GestureEventSynthDelegate : public TestWindowDelegate {
@@ -262,5 +283,79 @@ TEST_F(GestureRecognizerTest, GestureTapSyntheticMouse) {
EXPECT_TRUE(delegate->mouse_exit());
}
+TEST_F(GestureRecognizerTest, AsynchronousGestureRecognition) {
+ scoped_ptr<QueueTouchEventDelegate> queued_delegate(
+ new QueueTouchEventDelegate());
+ const int kWindowWidth = 123;
+ const int kWindowHeight = 45;
+ gfx::Rect bounds(100, 200, kWindowWidth, kWindowHeight);
+ scoped_ptr<aura::Window> queue(CreateTestWindowWithDelegate(
+ queued_delegate.get(), -1234, bounds, NULL));
+
+ queued_delegate->set_window(queue.get());
+
+ // Touch down on the window. This should not generate any gesture event.
+ queued_delegate->Reset();
+ TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(101, 201), 0);
+ RootWindow::GetInstance()->DispatchTouchEvent(&press);
+ EXPECT_FALSE(queued_delegate->tap());
+ EXPECT_FALSE(queued_delegate->tap_down());
+ EXPECT_FALSE(queued_delegate->double_tap());
+ EXPECT_FALSE(queued_delegate->scroll_begin());
+ EXPECT_FALSE(queued_delegate->scroll_update());
+ EXPECT_FALSE(queued_delegate->scroll_end());
+
+ // Create another window, and place a touch-down on it. This should create a
+ // tap-down gesture.
+ scoped_ptr<GestureEventConsumeDelegate> delegate(
+ new GestureEventConsumeDelegate());
+ scoped_ptr<aura::Window> window(CreateTestWindowWithDelegate(
+ delegate.get(), -2345, gfx::Rect(0, 0, 50, 50), NULL));
+ delegate->Reset();
+ TouchEvent press2(ui::ET_TOUCH_PRESSED, gfx::Point(10, 20), 0);
+ RootWindow::GetInstance()->DispatchTouchEvent(&press2);
+ EXPECT_FALSE(delegate->tap());
+ EXPECT_TRUE(delegate->tap_down());
+ EXPECT_FALSE(delegate->double_tap());
+ EXPECT_FALSE(delegate->scroll_begin());
+ EXPECT_FALSE(delegate->scroll_update());
+ EXPECT_FALSE(delegate->scroll_end());
+
+ // Introduce some delay before the touch is released so that it is recognized
+ // as a tap. However, this still should not create any gesture events.
+ queued_delegate->Reset();
+ TouchEvent release(ui::ET_TOUCH_RELEASED, gfx::Point(101, 201), 0);
+ Event::TestApi test_release(&release);
+ test_release.set_time_stamp(press.time_stamp() +
+ base::TimeDelta::FromMilliseconds(50));
+ RootWindow::GetInstance()->DispatchTouchEvent(&release);
+ EXPECT_FALSE(queued_delegate->tap());
+ EXPECT_FALSE(queued_delegate->tap_down());
+ EXPECT_FALSE(queued_delegate->double_tap());
+ EXPECT_FALSE(queued_delegate->scroll_begin());
+ EXPECT_FALSE(queued_delegate->scroll_update());
+ EXPECT_FALSE(queued_delegate->scroll_end());
+
+ // Process the first queued event.
+ queued_delegate->Reset();
+ queued_delegate->ReceivedAck();
+ EXPECT_FALSE(queued_delegate->tap());
+ EXPECT_TRUE(queued_delegate->tap_down());
+ EXPECT_FALSE(queued_delegate->double_tap());
+ EXPECT_FALSE(queued_delegate->scroll_begin());
+ EXPECT_FALSE(queued_delegate->scroll_update());
+ EXPECT_FALSE(queued_delegate->scroll_end());
+
+ // Now, process the second queued event.
+ queued_delegate->Reset();
+ queued_delegate->ReceivedAck();
+ EXPECT_TRUE(queued_delegate->tap());
+ EXPECT_FALSE(queued_delegate->tap_down());
+ EXPECT_FALSE(queued_delegate->double_tap());
+ EXPECT_FALSE(queued_delegate->scroll_begin());
+ EXPECT_FALSE(queued_delegate->scroll_update());
+ EXPECT_FALSE(queued_delegate->scroll_end());
+}
+
} // namespace test
} // namespace aura
diff --git a/ui/aura/root_window.cc b/ui/aura/root_window.cc
index a6a568b..7720699 100644
--- a/ui/aura/root_window.cc
+++ b/ui/aura/root_window.cc
@@ -431,7 +431,7 @@ RootWindow::RootWindow()
focused_window_(NULL),
touch_event_handler_(NULL),
gesture_handler_(NULL),
- gesture_recognizer_(new GestureRecognizer()),
+ gesture_recognizer_(GestureRecognizer::Create()),
synthesize_mouse_move_(false) {
SetName("RootWindow");
gfx::Screen::SetInstance(screen_);