summaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authorvollick@google.com <vollick@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-17 15:03:00 +0000
committervollick@google.com <vollick@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-17 15:03:00 +0000
commit6084b45328f06ac4eed830fe2eaaaf577a989d4e (patch)
treed394ced52c2131913f0fefba4d5677b5c8b94924 /ui
parent30105e54695a5f2ef5aada7379a51bb0f90c2c62 (diff)
downloadchromium_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.h20
-rw-r--r--ui/views/events/event.cc37
-rw-r--r--ui/views/events/event.h39
-rw-r--r--ui/views/touchui/gesture_recognizer.cc287
-rw-r--r--ui/views/touchui/gesture_recognizer.h177
-rw-r--r--ui/views/view.cc10
-rw-r--r--ui/views/view.h8
-rw-r--r--ui/views/view_unittest.cc334
-rw-r--r--ui/views/views.gyp2
-rw-r--r--ui/views/widget/native_widget_delegate.h4
-rw-r--r--ui/views/widget/root_view.cc75
-rw-r--r--ui/views/widget/root_view.h15
-rw-r--r--ui/views/widget/widget.cc7
-rw-r--r--ui/views/widget/widget.h1
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;