diff options
author | sadrul@chromium.org <sadrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-28 00:24:30 +0000 |
---|---|---|
committer | sadrul@chromium.org <sadrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-28 00:24:30 +0000 |
commit | 0889c17954b71b24b45a21032cbdada706730a7a (patch) | |
tree | 787d61ceaf9021414e9c1675d3c6d4249c11f8c8 /ui | |
parent | 2955a8e3b08a0b68058a0b268ccaec8729cbcbf7 (diff) | |
download | chromium_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.gyp | 3 | ||||
-rw-r--r-- | ui/aura/event.cc | 11 | ||||
-rw-r--r-- | ui/aura/event.h | 4 | ||||
-rw-r--r-- | ui/aura/gestures/gesture_recognizer.h | 150 | ||||
-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.h | 190 | ||||
-rw-r--r-- | ui/aura/gestures/gesture_recognizer_unittest.cc | 95 | ||||
-rw-r--r-- | ui/aura/root_window.cc | 2 |
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_); |