diff options
author | vollick@google.com <vollick@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-17 15:03:00 +0000 |
---|---|---|
committer | vollick@google.com <vollick@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-17 15:03:00 +0000 |
commit | 6084b45328f06ac4eed830fe2eaaaf577a989d4e (patch) | |
tree | d394ced52c2131913f0fefba4d5677b5c8b94924 /ui | |
parent | 30105e54695a5f2ef5aada7379a51bb0f90c2c62 (diff) | |
download | chromium_src-6084b45328f06ac4eed830fe2eaaaf577a989d4e.zip chromium_src-6084b45328f06ac4eed830fe2eaaaf577a989d4e.tar.gz chromium_src-6084b45328f06ac4eed830fe2eaaaf577a989d4e.tar.bz2 |
Initial views touchui Gesture Recognizer support
This is just a merge of Gajen's patch (http://codereview.chromium.org/8364039/) with ToT.
BUG=101645
TEST=views_unittest
Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=117869
Review URL: https://chromiumcodereview.appspot.com/9076002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@117894 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui')
-rw-r--r-- | ui/base/events.h | 20 | ||||
-rw-r--r-- | ui/views/events/event.cc | 37 | ||||
-rw-r--r-- | ui/views/events/event.h | 39 | ||||
-rw-r--r-- | ui/views/touchui/gesture_recognizer.cc | 287 | ||||
-rw-r--r-- | ui/views/touchui/gesture_recognizer.h | 177 | ||||
-rw-r--r-- | ui/views/view.cc | 10 | ||||
-rw-r--r-- | ui/views/view.h | 8 | ||||
-rw-r--r-- | ui/views/view_unittest.cc | 334 | ||||
-rw-r--r-- | ui/views/views.gyp | 2 | ||||
-rw-r--r-- | ui/views/widget/native_widget_delegate.h | 4 | ||||
-rw-r--r-- | ui/views/widget/root_view.cc | 75 | ||||
-rw-r--r-- | ui/views/widget/root_view.h | 15 | ||||
-rw-r--r-- | ui/views/widget/widget.cc | 7 | ||||
-rw-r--r-- | ui/views/widget/widget.h | 1 |
14 files changed, 1009 insertions, 7 deletions
diff --git a/ui/base/events.h b/ui/base/events.h index 62bebf4..2128df5 100644 --- a/ui/base/events.h +++ b/ui/base/events.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -38,6 +38,14 @@ enum EventType { ET_SCROLL, ET_TRANSLATED_KEY_PRESS, ET_TRANSLATED_KEY_RELEASE, + + // GestureEvent types + ET_GESTURE_SCROLL_BEGIN, + ET_GESTURE_SCROLL_END, + ET_GESTURE_SCROLL_UPDATE, + ET_GESTURE_TAP, + ET_GESTURE_TAP_DOWN, + ET_GESTURE_DOUBLE_TAP, }; // Event flags currently supported @@ -76,6 +84,16 @@ enum TouchStatus { // Updates the list of devices for cached properties. UI_EXPORT void UpdateDeviceList(); +enum GestureStatus { + GESTURE_STATUS_UNKNOWN = 0, // Unknown Gesture status. This is used to + // indicate that the Gesture event was not + // handled. + GESTURE_STATUS_CONSUMED, // The Gesture event got consumed. + GESTURE_STATUS_SYNTH_MOUSE // The Gesture event was not processed, but a + // synthetic mouse event generated from the + // unused Gesture event was handled. +}; + // Get the EventType from a native event. UI_EXPORT EventType EventTypeFromNative(const base::NativeEvent& native_event); diff --git a/ui/views/events/event.cc b/ui/views/events/event.cc index af97208..e6c7a0d 100644 --- a/ui/views/events/event.cc +++ b/ui/views/events/event.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -201,4 +201,39 @@ const int MouseWheelEvent::kWheelDelta = 120; // This value matches GTK+ wheel scroll amount. const int MouseWheelEvent::kWheelDelta = 53; #endif + +//////////////////////////////////////////////////////////////////////////////// +// GestureEvent, public: + +GestureEvent::GestureEvent(ui::EventType type, + int x, + int y, + int flags, + base::Time time_stamp, + float delta_x, + float delta_y) + : LocatedEvent(type, gfx::Point(x, y), flags), + delta_x_(delta_x), + delta_y_(delta_y) { + set_time_stamp(time_stamp); +} + +GestureEvent::GestureEvent(const GestureEvent& model, View* source, + View* target) + : LocatedEvent(model, source, target), + delta_x_(model.delta_x_), + delta_y_(model.delta_y_) { + set_time_stamp(model.time_stamp()); +} + +//////////////////////////////////////////////////////////////////////////////// +// GestureEvent, private: + +GestureEvent::GestureEvent(const GestureEvent& model, View* root) + : LocatedEvent(model, root), + delta_x_(model.delta_x_), + delta_y_(model.delta_y_) { + set_time_stamp(model.time_stamp()); +} + } // namespace views diff --git a/ui/views/events/event.h b/ui/views/events/event.h index 640e991..fcdb7d5 100644 --- a/ui/views/events/event.h +++ b/ui/views/events/event.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -62,6 +62,10 @@ class VIEWS_EXPORT Event { #endif ui::EventType type() const { return type_; } const base::Time& time_stamp() const { return time_stamp_; } + + // Required for Gesture testing purposes. + void set_time_stamp(base::Time time_stamp) { time_stamp_ = time_stamp; } + int flags() const { return flags_; } void set_flags(int flags) { flags_ = flags; } @@ -419,6 +423,39 @@ class VIEWS_EXPORT ScrollEvent : public MouseEvent { DISALLOW_COPY_AND_ASSIGN(ScrollEvent); }; +//////////////////////////////////////////////////////////////////////////////// +// GestureEvent class +// +//////////////////////////////////////////////////////////////////////////////// +class VIEWS_EXPORT GestureEvent : public LocatedEvent { + public: + GestureEvent(ui::EventType type, + int x, + int y, + int flags, + base::Time time_stamp, + float delta_x, + float delta_y); + + // Create a new GestureEvent which is identical to the provided model. + // If source / target views are provided, the model location will be converted + // from |source| coordinate system to |target| coordinate system. + GestureEvent(const GestureEvent& model, View* source, View* target); + + float delta_x() const { return delta_x_; } + float delta_y() const { return delta_y_; } + + private: + friend class internal::RootView; + + GestureEvent(const GestureEvent& model, View* root); + + float delta_x_; + float delta_y_; + + DISALLOW_COPY_AND_ASSIGN(GestureEvent); +}; + } // namespace views #endif // UI_VIEWS_EVENTS_EVENT_H_ diff --git a/ui/views/touchui/gesture_recognizer.cc b/ui/views/touchui/gesture_recognizer.cc new file mode 100644 index 0000000..112b222 --- /dev/null +++ b/ui/views/touchui/gesture_recognizer.cc @@ -0,0 +1,287 @@ +// 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. + +#include "ui/views/touchui/gesture_recognizer.h" + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "ui/views/events/event.h" + +namespace { +// TODO(Gajen): Make these configurable in sync with this CL http://code.google. +// com/p/chromium/issues/detail?id=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; + +} // namespace + +namespace views { + +//////////////////////////////////////////////////////////////////////////////// +// GestureRecognizer Public: + +GestureRecognizer::GestureRecognizer() + : first_touch_time_(0.0), + state_(GestureRecognizer::GS_NO_GESTURE), + last_touch_time_(0.0), + last_click_time_(0.0), + x_velocity_(0.0), + y_velocity_(0.0), + flags_(0) { +} + +GestureRecognizer::~GestureRecognizer() { +} + +GestureRecognizer* GestureRecognizer::GetInstance() { + return Singleton<GestureRecognizer>::get(); +} + +GestureRecognizer::Gestures* GestureRecognizer::ProcessTouchEventForGesture( + const TouchEvent& event, + ui::TouchStatus status) { + if (status != ui::TOUCH_STATUS_UNKNOWN) + return NULL; // The event was consumed by a touch sequence. + + scoped_ptr<Gestures> gestures(new Gestures()); + UpdateValues(event); + switch (Signature(state_, event.identity(), event.type(), false)) { + case GST_NO_GESTURE_FIRST_PRESSED: + TouchDown(event, gestures.get()); + break; + case GST_PENDING_SYNTHETIC_CLICK_FIRST_RELEASED: + Click(event, gestures.get()); + break; + case GST_PENDING_SYNTHETIC_CLICK_FIRST_MOVED: + case GST_PENDING_SYNTHETIC_CLICK_FIRST_STATIONARY: + InClickOrScroll(event, gestures.get()); + break; + case GST_PENDING_SYNTHETIC_CLICK_FIRST_CANCELLED: + NoGesture(event, gestures.get()); + break; + case GST_SCROLL_FIRST_MOVED: + InScroll(event, gestures.get()); + break; + case GST_SCROLL_FIRST_RELEASED: + case GST_SCROLL_FIRST_CANCELLED: + ScrollEnd(event, gestures.get()); + break; + } + return gestures.release(); +} + +void GestureRecognizer::Reset() { + first_touch_time_ = 0.0; + state_ = GestureRecognizer::GS_NO_GESTURE; + last_touch_time_ = 0.0; + last_touch_position_.SetPoint(0, 0); + x_velocity_ = 0.0; + y_velocity_ = 0.0; +} + +//////////////////////////////////////////////////////////////////////////////// +// GestureRecognizer Private: + +// static +GestureRecognizer::TouchState GestureRecognizer::TouchEventTypeToTouchState( + ui::EventType type) { + switch (type) { + case ui::ET_TOUCH_RELEASED: return TS_RELEASED; + case ui::ET_TOUCH_PRESSED: return TS_PRESSED; + case ui::ET_TOUCH_MOVED: return TS_MOVED; + case ui::ET_TOUCH_STATIONARY: return TS_STATIONARY; + case ui::ET_TOUCH_CANCELLED: return TS_CANCELLED; + default: + VLOG(1) << "Unknown Touch Event type"; + } + return TS_UNKNOWN; +} + +unsigned int GestureRecognizer::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() { + double duration(last_touch_time_ - first_touch_time_); + return duration >= kMinimumTouchDownDurationInSecondsForClick && + duration < kMaximumTouchDownDurationInSecondsForClick; +} + +bool GestureRecognizer::IsInSecondClickTimeWindow() { + double duration(last_touch_time_ - last_click_time_); + return duration < kMaximumSecondsBetweenDoubleClick; +} + +bool GestureRecognizer::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( + 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() { + return (x_velocity_ * x_velocity_ + y_velocity_ * y_velocity_) > + kMinFlickSpeedSquared; +} + +void GestureRecognizer::AppendTapDownGestureEvent(const TouchEvent& event, + Gestures* gestures) { + gestures->push_back(linked_ptr<GestureEvent>(new GestureEvent( + ui::ET_GESTURE_TAP_DOWN, + first_touch_position_.x(), + first_touch_position_.y(), + event.flags(), + base::Time::FromDoubleT(last_touch_time_), + 0.f, 0.f))); +} + +void GestureRecognizer::AppendClickGestureEvent(const TouchEvent& event, + Gestures* gestures) { + gestures->push_back(linked_ptr<GestureEvent>(new GestureEvent( + ui::ET_GESTURE_TAP, + first_touch_position_.x(), + first_touch_position_.y(), + event.flags(), + base::Time::FromDoubleT(last_touch_time_), + 0.f, 0.f))); +} + +void GestureRecognizer::AppendDoubleClickGestureEvent(const TouchEvent& event, + Gestures* gestures) { + gestures->push_back(linked_ptr<GestureEvent>(new GestureEvent( + ui::ET_GESTURE_DOUBLE_TAP, + first_touch_position_.x(), + first_touch_position_.y(), + event.flags(), + base::Time::FromDoubleT(last_touch_time_), + 0.f, 0.f))); +} + +void GestureRecognizer::AppendScrollGestureBegin(const TouchEvent& event, + Gestures* gestures) { + gestures->push_back(linked_ptr<GestureEvent>(new GestureEvent( + ui::ET_GESTURE_SCROLL_BEGIN, + event.x(), + event.y(), + event.flags(), + base::Time::FromDoubleT(last_touch_time_), + 0.f, 0.f))); +} + +void GestureRecognizer::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(), + event.y(), + event.flags(), + base::Time::FromDoubleT(last_touch_time_), + x_velocity, y_velocity))); +} + +void GestureRecognizer:: AppendScrollGestureUpdate(const TouchEvent& event, + Gestures* gestures) { + float delta_x(event.x() - first_touch_position_.x()); + float delta_y(event.y() - first_touch_position_.y()); + + gestures->push_back(linked_ptr<GestureEvent>(new GestureEvent( + ui::ET_GESTURE_SCROLL_UPDATE, + event.x(), + event.y(), + event.flags(), + base::Time::FromDoubleT(last_touch_time_), + delta_x, delta_y))); + + first_touch_position_ = event.location(); +} + +void GestureRecognizer::UpdateValues(const TouchEvent& event) { + if (state_ != GS_NO_GESTURE && event.type() == ui::ET_TOUCH_MOVED) { + double interval(event.time_stamp().ToDoubleT() - last_touch_time_); + x_velocity_ = (event.x() - last_touch_position_.x()) / interval; + y_velocity_ = (event.y() - last_touch_position_.y()) / interval; + } + last_touch_time_ = event.time_stamp().ToDoubleT(); + last_touch_position_ = event.location(); + if (state_ == GS_NO_GESTURE) { + first_touch_time_ = last_touch_time_; + first_touch_position_ = event.location(); + x_velocity_ = 0.0; + y_velocity_ = 0.0; + } +} + +bool GestureRecognizer::Click(const TouchEvent& event, Gestures* gestures) { + bool gesture_added = false; + if (IsInClickTimeWindow() && IsInsideManhattanSquare(event)) { + gesture_added = true; + AppendClickGestureEvent(event, gestures); + if (IsInSecondClickTimeWindow() && + IsSecondClickInsideManhattanSquare(event)) + AppendDoubleClickGestureEvent(event, gestures); + last_click_time_ = last_touch_time_; + last_click_position_ = last_touch_position_; + } + Reset(); + return gesture_added; +} + +bool GestureRecognizer::InClickOrScroll(const TouchEvent& event, + Gestures* gestures) { + if (IsInClickTimeWindow() && IsInsideManhattanSquare(event)) { + SetState(GS_PENDING_SYNTHETIC_CLICK); + return false; + } + if (event.type() == ui::ET_TOUCH_MOVED && !IsInsideManhattanSquare(event)) { + AppendScrollGestureBegin(event, gestures); + AppendScrollGestureUpdate(event, gestures); + SetState(GS_SCROLL); + return true; + } + return false; +} + +bool GestureRecognizer::InScroll(const TouchEvent& event, Gestures* gestures) { + AppendScrollGestureUpdate(event, gestures); + return true; +} + +bool GestureRecognizer::NoGesture(const TouchEvent&, Gestures*) { + Reset(); + return false; +} + +bool GestureRecognizer::TouchDown(const TouchEvent& event, Gestures* gestures) { + AppendTapDownGestureEvent(event, gestures); + SetState(GS_PENDING_SYNTHETIC_CLICK); + return false; +} + +bool GestureRecognizer::ScrollEnd(const TouchEvent& event, Gestures* gestures) { + if (IsOverMinFlickSpeed() && event.type() != ui::ET_TOUCH_CANCELLED) + AppendScrollGestureEnd(event, gestures, x_velocity_, y_velocity_); + else + AppendScrollGestureEnd(event, gestures, 0.f, 0.f); + SetState(GS_NO_GESTURE); + Reset(); + return false; +} + +} // namespace views diff --git a/ui/views/touchui/gesture_recognizer.h b/ui/views/touchui/gesture_recognizer.h new file mode 100644 index 0000000..2eeca89 --- /dev/null +++ b/ui/views/touchui/gesture_recognizer.h @@ -0,0 +1,177 @@ +// 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_VIEWS_TOUCHUI_GESTURE_RECOGNIZER_H_ +#define UI_VIEWS_TOUCHUI_GESTURE_RECOGNIZER_H_ +#pragma once + +#include <map> +#include <vector> + +#include "base/memory/linked_ptr.h" +#include "base/memory/singleton.h" +#include "ui/base/events.h" +#include "ui/gfx/point.h" +#include "ui/views/views_export.h" + +namespace views { +class GestureManager; +class TouchEvent; +class GestureEvent; + +// A GestureRecognizer recognizes gestures from touch sequences. +class VIEWS_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, + }; + + // List of GestureEvent*. + typedef std::vector<linked_ptr<GestureEvent> > Gestures; + + GestureRecognizer(); + virtual ~GestureRecognizer(); + + static GestureRecognizer* GetInstance(); + + // 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); + + // Clears the GestureRecognizer to its initial state. + virtual void Reset(); + + // Accessor function. + GestureState GetState() const { return state_; } + + private: + friend struct DefaultSingletonTraits<GestureRecognizer>; + + // 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, + }; + + // Get equivalent TouchState from EventType |type|. + static TouchState TouchEventTypeToTouchState(ui::EventType type); + + // 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_; + + DISALLOW_COPY_AND_ASSIGN(GestureRecognizer); +}; + +} // namespace views + +#endif // UI_VIEWS_TOUCHUI_GESTURE_RECOGNIZER_H_ diff --git a/ui/views/view.cc b/ui/views/view.cc index 5e371af..b358ef3 100644 --- a/ui/views/view.cc +++ b/ui/views/view.cc @@ -793,6 +793,10 @@ ui::TouchStatus View::OnTouchEvent(const TouchEvent& event) { return ui::TOUCH_STATUS_UNKNOWN; } +ui::GestureStatus View::OnGestureEvent(const GestureEvent& event) { + return ui::GESTURE_STATUS_UNKNOWN; +} + void View::SetMouseHandler(View *new_mouse_handler) { // It is valid for new_mouse_handler to be NULL if (parent_) @@ -820,6 +824,12 @@ InputMethod* View::GetInputMethod() { return widget ? widget->GetInputMethod() : NULL; } +ui::GestureStatus View::ProcessGestureEvent(const GestureEvent& event) { + // TODO(Gajen): Implement a grab scheme similar to as as is found in + // MousePressed. + return OnGestureEvent(event); +} + // Accelerators ---------------------------------------------------------------- void View::AddAccelerator(const ui::Accelerator& accelerator) { diff --git a/ui/views/view.h b/ui/views/view.h index 5f0d55c..dfe1cc4 100644 --- a/ui/views/view.h +++ b/ui/views/view.h @@ -582,6 +582,10 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // does nothing. Override as needed. virtual ui::TouchStatus OnTouchEvent(const TouchEvent& event); + // This method is invoked for each GestureEvent recognized from GestureManager + // Default implementation does nothing. Override as needed. + virtual ui::GestureStatus OnGestureEvent(const GestureEvent& event); + // Set the MouseHandler for a drag session. // // A drag session is a stream of mouse events starting @@ -1257,6 +1261,10 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, // the result of OnTouchEvent. ui::TouchStatus ProcessTouchEvent(const TouchEvent& event); + // GestureManager will invoke this with incoming GestureEvents. Returns the + // the result of OnGestureEvent. + ui::GestureStatus ProcessGestureEvent(const GestureEvent& event); + // Accelerators -------------------------------------------------------------- // Registers this view's keyboard accelerators that are not registered to diff --git a/ui/views/view_unittest.cc b/ui/views/view_unittest.cc index 44d7456..8d0d956 100644 --- a/ui/views/view_unittest.cc +++ b/ui/views/view_unittest.cc @@ -30,6 +30,7 @@ #include "ui/views/focus/view_storage.h" #include "ui/views/test/views_test_base.h" #include "ui/views/touchui/gesture_manager.h" +#include "ui/views/touchui/gesture_recognizer.h" #include "ui/views/view.h" #include "ui/views/views_delegate.h" #include "ui/views/widget/native_widget.h" @@ -206,6 +207,8 @@ class TestView : public View { location_.SetPoint(0, 0); last_touch_event_type_ = 0; last_touch_event_was_handled_ = false; + last_gesture_event_type_ = 0; + last_gesture_event_was_handled_ = false; last_clip_.setEmpty(); accelerator_count_map_.clear(); } @@ -215,6 +218,8 @@ class TestView : public View { virtual bool OnMouseDragged(const MouseEvent& event) OVERRIDE; virtual void OnMouseReleased(const MouseEvent& event) OVERRIDE; virtual ui::TouchStatus OnTouchEvent(const TouchEvent& event) OVERRIDE; + // Ignores GestureEvent by default. + virtual ui::GestureStatus OnGestureEvent(const GestureEvent& event) OVERRIDE; virtual void Paint(gfx::Canvas* canvas) OVERRIDE; virtual void SchedulePaintInRect(const gfx::Rect& rect) OVERRIDE; virtual bool AcceleratorPressed(const ui::Accelerator& accelerator) OVERRIDE; @@ -230,6 +235,10 @@ class TestView : public View { // Painting. std::vector<gfx::Rect> scheduled_paint_rects_; + // GestureEvent + int last_gesture_event_type_; + bool last_gesture_event_was_handled_; + // TouchEvent. int last_touch_event_type_; bool last_touch_event_was_handled_; @@ -266,6 +275,27 @@ class MockGestureManager : public GestureManager { DISALLOW_COPY_AND_ASSIGN(MockGestureManager); }; +// GestureRecognizer for testing. +class TestGestureRecognizer : public GestureRecognizer { + public: + TestGestureRecognizer(); + + // Reset all test state. + void reset() { + last_touch_event_ = 0; + previously_handled_flag_ = false; + } + + virtual Gestures* ProcessTouchEventForGesture( + const TouchEvent& event, + ui::TouchStatus status) OVERRIDE; + + bool previously_handled_flag_; + int last_touch_event_; + + DISALLOW_COPY_AND_ASSIGN(TestGestureRecognizer); +}; + // A view subclass that ignores all touch events for testing purposes. class TestViewIgnoreTouch : public TestView { public: @@ -276,6 +306,32 @@ class TestViewIgnoreTouch : public TestView { virtual ui::TouchStatus OnTouchEvent(const TouchEvent& event) OVERRIDE; }; +// A view subclass that consumes all Gesture events for testing purposes. +class TestViewConsumeGesture : public TestView { + public: + TestViewConsumeGesture() : TestView() {} + virtual ~TestViewConsumeGesture() {} + + private: + virtual ui::TouchStatus OnTouchEvent(const TouchEvent& event) OVERRIDE; + virtual ui::GestureStatus OnGestureEvent(const GestureEvent& event) OVERRIDE; + + DISALLOW_COPY_AND_ASSIGN(TestViewConsumeGesture); +}; + +// A view subclass that ignores all Gesture and touch events. +class TestViewIgnoreTouchAndGesture: public TestView { + public: + TestViewIgnoreTouchAndGesture() : TestView() {} + virtual ~TestViewIgnoreTouchAndGesture() {} + + private: + virtual ui::TouchStatus OnTouchEvent(const TouchEvent& event) OVERRIDE; + virtual ui::GestureStatus OnGestureEvent(const GestureEvent& event) OVERRIDE; + + DISALLOW_COPY_AND_ASSIGN(TestViewIgnoreTouchAndGesture); +}; + //////////////////////////////////////////////////////////////////////////////// // OnBoundsChanged //////////////////////////////////////////////////////////////////////////////// @@ -540,6 +596,284 @@ TEST_F(ViewTest, TouchEvent) { } //////////////////////////////////////////////////////////////////////////////// +// GestureEvent +//////////////////////////////////////////////////////////////////////////////// + +GestureRecognizer::Gestures* TestGestureRecognizer::ProcessTouchEventForGesture( + const TouchEvent& event, + ui::TouchStatus status) { + if (status != ui::TOUCH_STATUS_UNKNOWN) { + return NULL; + } + last_touch_event_ = event.type(); + previously_handled_flag_ = status != ui::TOUCH_STATUS_UNKNOWN; + return GestureRecognizer::ProcessTouchEventForGesture(event, status); +} + +TestGestureRecognizer::TestGestureRecognizer() { +} + +ui::GestureStatus TestView::OnGestureEvent(const GestureEvent& event) { + return ui::GESTURE_STATUS_UNKNOWN; +} + +// GestureConsumer view should ignore TouchEvent for testing purposes. +ui::TouchStatus TestViewConsumeGesture::OnTouchEvent( + const TouchEvent& event) { + return ui::TOUCH_STATUS_UNKNOWN; +} + +ui::GestureStatus TestViewConsumeGesture::OnGestureEvent( + const GestureEvent& event) { + last_gesture_event_type_ = event.type(); + location_.SetPoint(event.x(), event.y()); + return ui::GESTURE_STATUS_CONSUMED; +} + +// IgnoreTouchAndGesture view should ignore touch and Gesture event for +// testing purposes. +ui::TouchStatus TestViewIgnoreTouchAndGesture::OnTouchEvent( + const TouchEvent& event) { + return ui::TOUCH_STATUS_UNKNOWN; +} + +ui::GestureStatus TestViewIgnoreTouchAndGesture::OnGestureEvent( + const GestureEvent& event) { + return ui::GESTURE_STATUS_UNKNOWN; +} + +#if defined(TOUCH_UI) +TEST_F(ViewTest, GestureEvent) { + MockGestureManager gm; + TestGestureRecognizer gr; + + // Views hierarchy for non delivery of GestureEvent. + TestView* v1 = new TestViewConsumeGesture(); + v1->SetBounds(0, 0, 300, 300); + + TestView* v2 = new TestView(); + v2->SetBounds(100, 100, 100, 100); + + TestView* v3 = new TestViewConsumeGesture(); + v3->SetBounds(0, 0, 100, 100); + + // Views hierarchy for delivery of GestureEvent. + TestView* v4 = new TestViewConsumeGesture(); + v4->SetBounds(200, 200, 100, 100); + + scoped_ptr<Widget> widget(new Widget()); + Widget::InitParams params(Widget::InitParams::TYPE_POPUP); + params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.bounds = gfx::Rect(50, 50, 650, 650); + widget->Init(params); + View* root = widget->GetRootView(); + + root->AddChildView(v1); + static_cast<internal::RootView*>(root)->SetGestureManagerForTesting(&gm); + static_cast<internal::RootView*>(root)->SetGestureRecognizerForTesting(&gr); + v1->AddChildView(v2); + v2->AddChildView(v3); + v1->AddChildView(v4); + + // |v3| completely obscures |v2|, but all the touch events on |v3| should + // reach |v2| because |v3| doesn't process any touch events, hence no gesture + // conversion take place. + + // Both |v4| and |v1| ignore touch events, hence |v4| should recieve gesture + // events. + + // Make sure if none of the views handle the touch event, the gesture manager + // does. + v1->Reset(); + v2->Reset(); + v3->Reset(); + v4->Reset(); + gr.reset(); + gm.Reset(); + + TouchEvent unhandled(ui::ET_TOUCH_MOVED, + 400, + 400, + 0, /* no flags */ + 0, /* first finger touch */ + 1.0, 0.0, 1.0, 0.0); + root->OnTouchEvent(unhandled); + + EXPECT_EQ(v1->last_touch_event_type_, 0); + EXPECT_EQ(v2->last_touch_event_type_, 0); + EXPECT_EQ(v4->last_touch_event_type_, 0); + EXPECT_EQ(v4->last_gesture_event_type_, 0); + + EXPECT_EQ(gr.previously_handled_flag_, false); + EXPECT_EQ(gr.last_touch_event_, ui::ET_TOUCH_MOVED); + + // Test press, drag, release touch sequence. + v1->Reset(); + v2->Reset(); + v3->Reset(); + v4->Reset(); + gr.reset(); + gm.Reset(); + + TouchEvent pressed(ui::ET_TOUCH_PRESSED, + 110, + 120, + 0, /* no flags */ + 0, /* first finger touch */ + 1.0, 0.0, 1.0, 0.0); + v2->last_touch_event_was_handled_ = true; + root->OnTouchEvent(pressed); + + EXPECT_EQ(v2->last_touch_event_type_, ui::ET_TOUCH_PRESSED); + EXPECT_EQ(v2->location_.x(), 10); + EXPECT_EQ(v2->location_.y(), 20); + // Make sure v1 did not receive the event + EXPECT_EQ(v1->last_touch_event_type_, 0); + + // Since v2 handled the touch-event, the gesture recognizer should not + // handle it. + EXPECT_EQ(gr.last_touch_event_, 0); + EXPECT_EQ(gr.previously_handled_flag_, false); + + // Drag event out of bounds. Should still go to v2 + v1->Reset(); + v2->Reset(); + TouchEvent dragged(ui::ET_TOUCH_MOVED, + 50, + 40, + 0, /* no flags */ + 0, /* first finger touch */ + 1.0, 0.0, 1.0, 0.0); + root->OnTouchEvent(dragged); + EXPECT_EQ(v2->last_touch_event_type_, ui::ET_TOUCH_MOVED); + EXPECT_EQ(v2->location_.x(), -50); + EXPECT_EQ(v2->location_.y(), -60); + // Make sure v1 did not receive the event + EXPECT_EQ(v1->last_touch_event_type_, 0); + + EXPECT_EQ(gr.last_touch_event_, 0); + EXPECT_EQ(gr.previously_handled_flag_, false); + + // Released event out of bounds. Should still go to v2 + v1->Reset(); + v2->Reset(); + TouchEvent released(ui::ET_TOUCH_RELEASED, 0, 0, 0, 0 /* first finger */, + 1.0, 0.0, 1.0, 0.0); + v2->last_touch_event_was_handled_ = true; + root->OnTouchEvent(released); + EXPECT_EQ(v2->last_touch_event_type_, ui::ET_TOUCH_RELEASED); + EXPECT_EQ(v2->location_.x(), -100); + EXPECT_EQ(v2->location_.y(), -100); + // Make sure v1 did not receive the event + EXPECT_EQ(v1->last_touch_event_type_, 0); + + EXPECT_EQ(gr.last_touch_event_, 0); + EXPECT_EQ(gr.previously_handled_flag_, false); + + // Gesture event handling test. + // 1) Test gesture type ui::ET_GESTURE_TAP_DOWN. + v1->Reset(); + v4->Reset(); + gr.reset(); + gm.Reset(); + + TouchEvent second_pressed(ui::ET_TOUCH_PRESSED, + 210, + 220, + 0, /* no flags */ + 0, /* first finger touch */ + 1.0, 0.0, 1.0, 0.0); + base::Time pressed_time = second_pressed.time_stamp(); + root->OnTouchEvent(second_pressed); + + // Since v1 and V4 didn't handled touch event, the gesture manager should + // handle it. + EXPECT_EQ(gr.last_touch_event_, ui::ET_TOUCH_PRESSED); + EXPECT_EQ(gr.previously_handled_flag_, false); + + // Check v4 should receive gesture event but not v1. + EXPECT_EQ(v1->last_touch_event_type_, 0); + EXPECT_EQ(v4->last_touch_event_type_, 0); + EXPECT_EQ(v4->last_gesture_event_type_, ui::ET_GESTURE_TAP_DOWN); + EXPECT_EQ(v4->location_.x(), 10); + EXPECT_EQ(v4->location_.y(), 20); + + // 2) Test gesture type ui::ET_GESTURE_TAP. + v1->Reset(); + v4->Reset(); + gr.reset(); + gm.Reset(); + + TouchEvent second_released(ui::ET_TOUCH_RELEASED, + 210, + 220, + 0, /* no flags */ + 0, /* first finger touch */ + 1.0, 0.0, 1.0, 0.0); + + // Set touch time with-in click window. + second_released.set_time_stamp(base::Time::FromDoubleT( + pressed_time.ToDoubleT() + 0.7)); + root->OnTouchEvent(second_released); + + // Since v1 and V4 didn't handled touch event, the gesture manager should + // handle it. + EXPECT_EQ(gr.last_touch_event_, ui::ET_TOUCH_RELEASED); + EXPECT_EQ(gr.previously_handled_flag_, false); + + // Check v4 should receive gesture event but not v1. + EXPECT_EQ(v1->last_touch_event_type_, 0); + EXPECT_EQ(v4->last_touch_event_type_, 0); + EXPECT_EQ(v4->last_gesture_event_type_, ui::ET_GESTURE_TAP); + EXPECT_EQ(v4->location_.x(), 10); + EXPECT_EQ(v4->location_.y(), 20); + + // 3) Test Gesture to mouse conversion. + // Views hierarchy for delivery of mouse event in absence of Touch and + // Gesture handlers. + TestView* v5 = new TestViewIgnoreTouchAndGesture(); + v5->SetBounds(0, 0, 300, 300); + + TestView* v6 = new TestViewIgnoreTouchAndGesture(); + v6->SetBounds(100, 100, 100, 100); + + root->AddChildView(v5); + v5->AddChildView(v6); + + v5->Reset(); + v6->Reset(); + gr.reset(); + gm.Reset(); + + TouchEvent third_pressed(ui::ET_TOUCH_PRESSED, + 110, + 120, + 0, /* no flags */ + 0, /* first finger touch */ + 1.0, 0.0, 1.0, 0.0); + root->OnTouchEvent(third_pressed); + + // Since v5 and V6 didn't handled touch and gesture event, gesture recognizer + // and manager should recieve touch event. + EXPECT_EQ(gr.last_touch_event_, ui::ET_TOUCH_PRESSED); + EXPECT_EQ(gr.previously_handled_flag_, false); + EXPECT_EQ(gm.previously_handled_flag_, false); + EXPECT_EQ(gm.last_touch_event_, ui::ET_TOUCH_PRESSED); + EXPECT_EQ(gm.last_view_, root); + EXPECT_EQ(gm.dispatched_synthetic_event_, true); + + // Check v6 shouldn't recieve touch and gesture event but mouse event. + EXPECT_EQ(v6->last_touch_event_type_, 0); + EXPECT_EQ(v6->last_gesture_event_type_, 0); + + // Check v5 shouldn't recieve touch, gesture and mouse event. + EXPECT_EQ(v5->last_touch_event_type_, 0); + EXPECT_EQ(v5->last_gesture_event_type_, 0); + widget->CloseNow(); +} +#endif + +//////////////////////////////////////////////////////////////////////////////// // Painting //////////////////////////////////////////////////////////////////////////////// diff --git a/ui/views/views.gyp b/ui/views/views.gyp index 082ac4c..1bee26a 100644 --- a/ui/views/views.gyp +++ b/ui/views/views.gyp @@ -314,6 +314,8 @@ 'repeat_controller.h', 'touchui/gesture_manager.cc', 'touchui/gesture_manager.h', + 'touchui/gesture_recognizer.cc', + 'touchui/gesture_recognizer.h', 'touchui/touch_selection_controller.cc', 'touchui/touch_selection_controller.h', 'view.cc', diff --git a/ui/views/widget/native_widget_delegate.h b/ui/views/widget/native_widget_delegate.h index 317374a..6bbef31 100644 --- a/ui/views/widget/native_widget_delegate.h +++ b/ui/views/widget/native_widget_delegate.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -17,6 +17,7 @@ class Size; namespace views { class InputMethod; +class GestureEvent; class KeyEvent; class MouseEvent; class TouchEvent; @@ -96,6 +97,7 @@ class VIEWS_EXPORT NativeWidgetDelegate { virtual bool OnMouseEvent(const MouseEvent& event) = 0; virtual void OnMouseCaptureLost() = 0; virtual ui::TouchStatus OnTouchEvent(const TouchEvent& event) = 0; + virtual ui::GestureStatus OnGestureEvent(const GestureEvent& event) = 0; // Runs the specified native command. Returns true if the command is handled. virtual bool ExecuteCommand(int command_id) = 0; diff --git a/ui/views/widget/root_view.cc b/ui/views/widget/root_view.cc index c4978ca..a8f4ef6 100644 --- a/ui/views/widget/root_view.cc +++ b/ui/views/widget/root_view.cc @@ -16,6 +16,7 @@ #include "ui/views/focus/view_storage.h" #include "ui/views/layout/fill_layout.h" #include "ui/views/touchui/gesture_manager.h" +#include "ui/views/touchui/gesture_recognizer.h" #include "ui/views/widget/widget.h" namespace views { @@ -40,6 +41,8 @@ RootView::RootView(Widget* widget) last_mouse_event_y_(-1), gesture_manager_(GestureManager::GetInstance()), touch_pressed_handler_(NULL), + gesture_recognizer_(GestureRecognizer::GetInstance()), + gesture_handling_view_(NULL), ALLOW_THIS_IN_INITIALIZER_LIST(focus_search_(this, false, false)), focus_traversable_parent_(NULL), focus_traversable_parent_view_(NULL) { @@ -337,7 +340,7 @@ ui::TouchStatus RootView::OnTouchEvent(const TouchEvent& event) { if (touch_pressed_handler_) { TouchEvent touch_event(e, this, touch_pressed_handler_); status = touch_pressed_handler_->ProcessTouchEvent(touch_event); - if (gesture_manager_->ProcessTouchEventForGesture(e, this, status)) + if (DoGestureProcessing(e, status)) status = ui::TOUCH_STATUS_SYNTH_MOUSE; if (status == ui::TOUCH_STATUS_END) touch_pressed_handler_ = NULL; @@ -376,7 +379,7 @@ ui::TouchStatus RootView::OnTouchEvent(const TouchEvent& event) { if (status != ui::TOUCH_STATUS_START) touch_pressed_handler_ = NULL; - if (gesture_manager_->ProcessTouchEventForGesture(e, this, status)) + if (DoGestureProcessing(e, status)) status = ui::TOUCH_STATUS_SYNTH_MOUSE; return status; } @@ -390,6 +393,42 @@ ui::TouchStatus RootView::OnTouchEvent(const TouchEvent& event) { return status; } +ui::GestureStatus RootView::OnGestureEvent(const GestureEvent& event) { + GestureEvent e(event, this); + ui::GestureStatus status = ui::GESTURE_STATUS_UNKNOWN; + + // Walk up the tree until we find a view that wants the gesture event. + for (gesture_handling_view_ = GetEventHandlerForPoint(e.location()); + gesture_handling_view_ && (gesture_handling_view_ != this); + gesture_handling_view_ = gesture_handling_view_->parent()) { + if (!gesture_handling_view_->enabled()) { + // Disabled views eat events but are treated as not handled by the + // the GestureManager. + return ui::GESTURE_STATUS_UNKNOWN; + } + + // See if this view wants to handle the Gesture. + GestureEvent gesture_event(e, this, gesture_handling_view_); + status = gesture_handling_view_->ProcessGestureEvent(gesture_event); + + // The view could have removed itself from the tree when handling + // OnGestureEvent(). So handle as per OnMousePressed. NB: we + // assume that the RootView itself cannot be so removed. + if (!gesture_handling_view_) return ui::GESTURE_STATUS_UNKNOWN; + + // The gesture event wasn't processed. Go up the view hierarchy and + // dispatch the gesture event. + if (status == ui::GESTURE_STATUS_UNKNOWN) { + continue; + } else if (status == ui::GESTURE_STATUS_CONSUMED) { + return status; + } else { + return ui::GESTURE_STATUS_UNKNOWN; + } + } + return status; +} + void RootView::SetMouseHandler(View *new_mh) { // If we're clearing the mouse handler, clear explicit_mouse_handler_ as well. explicit_mouse_handler_ = (new_mh != NULL); @@ -418,6 +457,8 @@ void RootView::ViewHierarchyChanged(bool is_add, View* parent, View* child) { mouse_move_handler_ = NULL; if (touch_pressed_handler_ == child) touch_pressed_handler_ = NULL; + if (gesture_handling_view_ == child) + gesture_handling_view_ = NULL; } } @@ -457,5 +498,35 @@ void RootView::SetMouseLocationAndFlags(const MouseEvent& event) { last_mouse_event_y_ = event.y(); } +bool RootView::DoGestureProcessing(const TouchEvent& event, + ui::TouchStatus status) { + if (status != ui::TOUCH_STATUS_UNKNOWN) + return false; // The event was consumed by a touch sequence. + + // Get the GestureEvent list processed from GestureRecognizer. + scoped_ptr<GestureRecognizer::Gestures> gestures; + gestures.reset(gesture_recognizer_->ProcessTouchEventForGesture(event, + status)); + bool synthetic = true; + for (unsigned int i = 0; i < gestures->size(); i++) { + GestureEvent* event = gestures->at(i).get(); + GestureEvent e(*event, this); + if (OnGestureEvent(e) == ui::GESTURE_STATUS_CONSUMED) { + // All gesture events should be consumed. + synthetic = false; + } else { + synthetic = true; + break; + } + } + if (synthetic) { + // TODO(Gajen): This should be removed in future once all views are capable + // of handling OnGestureEvent. + return gesture_manager_->ProcessTouchEventForGesture(event, this, + status); + } + return synthetic; +} + } // namespace internal } // namespace views diff --git a/ui/views/widget/root_view.h b/ui/views/widget/root_view.h index 3f372e6..2539676 100644 --- a/ui/views/widget/root_view.h +++ b/ui/views/widget/root_view.h @@ -21,6 +21,7 @@ namespace views { class Widget; class GestureManager; +class GestureRecognizer; // This is a views-internal API and should not be used externally. // Widget exposes this object as a View*. @@ -71,6 +72,9 @@ class VIEWS_EXPORT RootView : public View, public FocusTraversable { // Provided only for testing: void SetGestureManagerForTesting(GestureManager* g) { gesture_manager_ = g; } + void SetGestureRecognizerForTesting(GestureRecognizer* gr) { + gesture_recognizer_ = gr; + } // Focus --------------------------------------------------------------------- @@ -110,6 +114,7 @@ class VIEWS_EXPORT RootView : public View, public FocusTraversable { virtual void OnMouseExited(const MouseEvent& event) OVERRIDE; virtual bool OnMouseWheel(const MouseWheelEvent& event) OVERRIDE; virtual ui::TouchStatus OnTouchEvent(const TouchEvent& event) OVERRIDE; + virtual ui::GestureStatus OnGestureEvent(const GestureEvent& event) OVERRIDE; virtual void SetMouseHandler(View* new_mouse_handler) OVERRIDE; virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE; virtual void ReorderChildLayers(ui::Layer* parent_layer) OVERRIDE; @@ -145,6 +150,10 @@ class VIEWS_EXPORT RootView : public View, public FocusTraversable { // be applied to the point prior to calling this). void SetMouseLocationAndFlags(const MouseEvent& event); + // Feeds touch event to GestureRecognizer. + // Returns true if the event resulted in firing a synthetic event. + bool DoGestureProcessing(const TouchEvent& event, ui::TouchStatus status); + ////////////////////////////////////////////////////////////////////////////// // Tree operations ----------------------------------------------------------- @@ -179,6 +188,12 @@ class VIEWS_EXPORT RootView : public View, public FocusTraversable { // The view currently handling touch events. View* touch_pressed_handler_; + // The gesture_recognizer_ for this. + GestureRecognizer* gesture_recognizer_; + + // The view currently handling gesture events. + View* gesture_handling_view_; + // Focus --------------------------------------------------------------------- // The focus search algorithm. diff --git a/ui/views/widget/widget.cc b/ui/views/widget/widget.cc index 73c2bd0..b9be13b4 100644 --- a/ui/views/widget/widget.cc +++ b/ui/views/widget/widget.cc @@ -1074,7 +1074,12 @@ void Widget::OnMouseCaptureLost() { ui::TouchStatus Widget::OnTouchEvent(const TouchEvent& event) { ScopedEvent scoped(this, event); - return static_cast<internal::RootView*>(GetRootView())->OnTouchEvent(event); + return GetRootView()->OnTouchEvent(event); +} + +ui::GestureStatus Widget::OnGestureEvent(const GestureEvent& event) { + ScopedEvent scoped(this, event); + return GetRootView()->OnGestureEvent(event); } bool Widget::ExecuteCommand(int command_id) { diff --git a/ui/views/widget/widget.h b/ui/views/widget/widget.h index f00d6b7..f1b2d41 100644 --- a/ui/views/widget/widget.h +++ b/ui/views/widget/widget.h @@ -619,6 +619,7 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, virtual bool OnMouseEvent(const MouseEvent& event) OVERRIDE; virtual void OnMouseCaptureLost() OVERRIDE; virtual ui::TouchStatus OnTouchEvent(const TouchEvent& event) OVERRIDE; + virtual ui::GestureStatus OnGestureEvent(const GestureEvent& event) OVERRIDE; virtual bool ExecuteCommand(int command_id) OVERRIDE; virtual InputMethod* GetInputMethodDirect() OVERRIDE; virtual Widget* AsWidget() OVERRIDE; |