Commit 9877c372 authored by tdresser@chromium.org's avatar tdresser@chromium.org

Gestures are now possible using touch events with any ids

Each gesture point stores a "point_id," which is used for state transitions in the GestureRecognizer.
The point_ids are maintained such that the set of point_ids is always contiguous, from 0 to the number of current touches.
A lower point_id indicates that a touch occurred first.

BUG=113144
TEST=GestureRecognizerTest.*


Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=123989

Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=124071

Review URL: http://codereview.chromium.org/9452024

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@124668 0039d316-1c4b-4281-b951-d872f2087c98
parent 01d0ea20
...@@ -325,7 +325,7 @@ TEST_F(RootWindowEventFilterTest, ActivateOnTouch) { ...@@ -325,7 +325,7 @@ TEST_F(RootWindowEventFilterTest, ActivateOnTouch) {
press_point = w1->bounds().CenterPoint(); press_point = w1->bounds().CenterPoint();
aura::Window::ConvertPointToWindow(w1->parent(), root_window, &press_point); aura::Window::ConvertPointToWindow(w1->parent(), root_window, &press_point);
d1.set_activate(false); d1.set_activate(false);
aura::TouchEvent touchev2(ui::ET_TOUCH_PRESSED, press_point, 0); aura::TouchEvent touchev2(ui::ET_TOUCH_PRESSED, press_point, 1);
root_window->DispatchTouchEvent(&touchev2); root_window->DispatchTouchEvent(&touchev2);
// Window2 should still be active and focused. // Window2 should still be active and focused.
...@@ -547,13 +547,15 @@ TEST_F(RootWindowEventFilterTest, UpdateCursorVisibility) { ...@@ -547,13 +547,15 @@ TEST_F(RootWindowEventFilterTest, UpdateCursorVisibility) {
aura::MouseEvent mouse_moved( aura::MouseEvent mouse_moved(
ui::ET_MOUSE_MOVED, gfx::Point(0, 0), gfx::Point(0, 0), 0x0); ui::ET_MOUSE_MOVED, gfx::Point(0, 0), gfx::Point(0, 0), 0x0);
aura::TouchEvent touch_pressed( aura::TouchEvent touch_pressed1(
ui::ET_TOUCH_PRESSED, gfx::Point(0, 0), 0); ui::ET_TOUCH_PRESSED, gfx::Point(0, 0), 0);
aura::TouchEvent touch_pressed2(
ui::ET_TOUCH_PRESSED, gfx::Point(0, 0), 1);
root_window_filter->set_update_cursor_visibility(true); root_window_filter->set_update_cursor_visibility(true);
root_window->DispatchMouseEvent(&mouse_moved); root_window->DispatchMouseEvent(&mouse_moved);
EXPECT_TRUE(root_window->cursor_shown()); EXPECT_TRUE(root_window->cursor_shown());
root_window->DispatchTouchEvent(&touch_pressed); root_window->DispatchTouchEvent(&touch_pressed1);
EXPECT_FALSE(root_window->cursor_shown()); EXPECT_FALSE(root_window->cursor_shown());
root_window->DispatchMouseEvent(&mouse_moved); root_window->DispatchMouseEvent(&mouse_moved);
EXPECT_TRUE(root_window->cursor_shown()); EXPECT_TRUE(root_window->cursor_shown());
...@@ -563,7 +565,7 @@ TEST_F(RootWindowEventFilterTest, UpdateCursorVisibility) { ...@@ -563,7 +565,7 @@ TEST_F(RootWindowEventFilterTest, UpdateCursorVisibility) {
root_window->DispatchMouseEvent(&mouse_moved); root_window->DispatchMouseEvent(&mouse_moved);
EXPECT_FALSE(root_window->cursor_shown()); EXPECT_FALSE(root_window->cursor_shown());
root_window->ShowCursor(true); root_window->ShowCursor(true);
root_window->DispatchTouchEvent(&touch_pressed); root_window->DispatchTouchEvent(&touch_pressed2);
EXPECT_TRUE(root_window->cursor_shown()); EXPECT_TRUE(root_window->cursor_shown());
} }
......
...@@ -27,7 +27,8 @@ GesturePoint::GesturePoint() ...@@ -27,7 +27,8 @@ GesturePoint::GesturePoint()
: first_touch_time_(0.0), : first_touch_time_(0.0),
last_touch_time_(0.0), last_touch_time_(0.0),
last_tap_time_(0.0), last_tap_time_(0.0),
velocity_calculator_(kBufferedPoints) { velocity_calculator_(kBufferedPoints),
point_id_(-1) {
} }
GesturePoint::~GesturePoint() {} GesturePoint::~GesturePoint() {}
...@@ -35,6 +36,7 @@ GesturePoint::~GesturePoint() {} ...@@ -35,6 +36,7 @@ GesturePoint::~GesturePoint() {}
void GesturePoint::Reset() { void GesturePoint::Reset() {
first_touch_time_ = last_touch_time_ = 0.0; first_touch_time_ = last_touch_time_ = 0.0;
velocity_calculator_.ClearHistory(); velocity_calculator_.ClearHistory();
point_id_ = -1;
} }
void GesturePoint::UpdateValues(const TouchEvent& event) { void GesturePoint::UpdateValues(const TouchEvent& event) {
......
...@@ -53,8 +53,16 @@ class GesturePoint { ...@@ -53,8 +53,16 @@ class GesturePoint {
double last_touch_time() const { return last_touch_time_; } double last_touch_time() const { return last_touch_time_; }
const gfx::Point& last_touch_position() const { return last_touch_position_; } const gfx::Point& last_touch_position() const { return last_touch_position_; }
void set_touch_id(unsigned int touch_id) { touch_id_ = touch_id; } // point_id_ is used to drive GestureSequence::ProcessTouchEventForGesture.
unsigned int touch_id() const { return touch_id_; } // point_ids are maintained such that the set of point_ids is always
// contiguous, from 0 to the number of current touches.
// A lower point_id indicates that a touch occurred first.
// A negative point_id indicates that the GesturePoint is not currently
// associated with a touch.
void set_point_id(int point_id) { point_id_ = point_id; }
const int point_id() const { return point_id_; }
const bool in_use() const { return point_id_ >= 0; }
double x_delta() const { double x_delta() const {
return last_touch_position_.x() - first_touch_position_.x(); return last_touch_position_.x() - first_touch_position_.x();
...@@ -87,7 +95,7 @@ class GesturePoint { ...@@ -87,7 +95,7 @@ class GesturePoint {
VelocityCalculator velocity_calculator_; VelocityCalculator velocity_calculator_;
unsigned int touch_id_; int point_id_;
DISALLOW_COPY_AND_ASSIGN(GesturePoint); DISALLOW_COPY_AND_ASSIGN(GesturePoint);
}; };
......
...@@ -12,11 +12,6 @@ ...@@ -12,11 +12,6 @@
#include "ui/aura/gestures/gesture_configuration.h" #include "ui/aura/gestures/gesture_configuration.h"
#include "ui/base/events.h" #include "ui/base/events.h"
// TODO(sad): Pinch gestures currently always assume that the first two
// touch-points (i.e. at indices 0 and 1) are involved. This may not
// always be the case. This needs to be fixed eventually.
// http://crbug.com/113144
namespace { namespace {
// TODO(girard): Make these configurable in sync with this CL // TODO(girard): Make these configurable in sync with this CL
...@@ -99,18 +94,6 @@ enum EdgeStateSignatureType { ...@@ -99,18 +94,6 @@ enum EdgeStateSignatureType {
GST_SCROLL_FIRST_CANCELLED = GST_SCROLL_FIRST_CANCELLED =
G(GS_SCROLL, 0, TS_CANCELLED, false), G(GS_SCROLL, 0, TS_CANCELLED, false),
GST_SCROLL_FIRST_PRESSED =
G(GS_SCROLL, 0, TS_PRESSED, false),
GST_SCROLL_SECOND_RELEASED =
G(GS_SCROLL, 1, TS_RELEASED, false),
GST_SCROLL_SECOND_MOVED =
G(GS_SCROLL, 1, TS_MOVED, false),
GST_SCROLL_SECOND_CANCELLED =
G(GS_SCROLL, 1, TS_CANCELLED, false),
GST_SCROLL_SECOND_PRESSED = GST_SCROLL_SECOND_PRESSED =
G(GS_SCROLL, 1, TS_PRESSED, false), G(GS_SCROLL, 1, TS_PRESSED, false),
...@@ -164,9 +147,6 @@ GestureSequence::GestureSequence(RootWindow* root_window) ...@@ -164,9 +147,6 @@ GestureSequence::GestureSequence(RootWindow* root_window)
long_press_timer_(CreateTimer()), long_press_timer_(CreateTimer()),
point_count_(0), point_count_(0),
root_window_(root_window) { root_window_(root_window) {
for (int i = 0; i < kMaxGesturePoints; ++i) {
points_[i].set_touch_id(i);
}
} }
GestureSequence::~GestureSequence() { GestureSequence::~GestureSequence() {
...@@ -185,7 +165,14 @@ GestureSequence::Gestures* GestureSequence::ProcessTouchEventForGesture( ...@@ -185,7 +165,14 @@ GestureSequence::Gestures* GestureSequence::ProcessTouchEventForGesture(
if (event.type() == ui::ET_TOUCH_PRESSED) { if (event.type() == ui::ET_TOUCH_PRESSED) {
if (point_count_ == kMaxGesturePoints) if (point_count_ == kMaxGesturePoints)
return NULL; return NULL;
++point_count_; GesturePoint* new_point = &points_[event.touch_id()];
// Eventually, we shouldn't be able to get two PRESSED events without either
// a RELEASE or CANCEL. Currently if a RELEASE is preventDefaulted,
// this could occur: crbug.com/116537
// TODO(tdresser): Enable this DCHECK, and remove the following condition
// DCHECK(!points_[event.touch_id()].in_use());
if (!points_[event.touch_id()].in_use())
new_point->set_point_id(point_count_++);
} }
GestureState last_state = state_; GestureState last_state = state_;
...@@ -195,7 +182,10 @@ GestureSequence::Gestures* GestureSequence::ProcessTouchEventForGesture( ...@@ -195,7 +182,10 @@ GestureSequence::Gestures* GestureSequence::ProcessTouchEventForGesture(
GesturePoint& point = GesturePointForEvent(event); GesturePoint& point = GesturePointForEvent(event);
point.UpdateValues(event); point.UpdateValues(event);
flags_ = event.flags(); flags_ = event.flags();
switch (Signature(state_, event.touch_id(), event.type(), false)) { const int point_id = points_[event.touch_id()].point_id();
if (point_id < 0)
return NULL;
switch (Signature(state_, point_id, event.type(), false)) {
case GST_NO_GESTURE_FIRST_PRESSED: case GST_NO_GESTURE_FIRST_PRESSED:
TouchDown(event, point, gestures.get()); TouchDown(event, point, gestures.get());
set_state(GS_PENDING_SYNTHETIC_CLICK); set_state(GS_PENDING_SYNTHETIC_CLICK);
...@@ -217,7 +207,6 @@ GestureSequence::Gestures* GestureSequence::ProcessTouchEventForGesture( ...@@ -217,7 +207,6 @@ GestureSequence::Gestures* GestureSequence::ProcessTouchEventForGesture(
NoGesture(event, point, gestures.get()); NoGesture(event, point, gestures.get());
break; break;
case GST_SCROLL_FIRST_MOVED: case GST_SCROLL_FIRST_MOVED:
case GST_SCROLL_SECOND_MOVED:
if (scroll_type_ == ST_VERTICAL || if (scroll_type_ == ST_VERTICAL ||
scroll_type_ == ST_HORIZONTAL) scroll_type_ == ST_HORIZONTAL)
BreakRailScroll(event, point, gestures.get()); BreakRailScroll(event, point, gestures.get());
...@@ -226,12 +215,9 @@ GestureSequence::Gestures* GestureSequence::ProcessTouchEventForGesture( ...@@ -226,12 +215,9 @@ GestureSequence::Gestures* GestureSequence::ProcessTouchEventForGesture(
break; break;
case GST_SCROLL_FIRST_RELEASED: case GST_SCROLL_FIRST_RELEASED:
case GST_SCROLL_FIRST_CANCELLED: case GST_SCROLL_FIRST_CANCELLED:
case GST_SCROLL_SECOND_RELEASED:
case GST_SCROLL_SECOND_CANCELLED:
ScrollEnd(event, point, gestures.get()); ScrollEnd(event, point, gestures.get());
set_state(GS_NO_GESTURE); set_state(GS_NO_GESTURE);
break; break;
case GST_SCROLL_FIRST_PRESSED:
case GST_SCROLL_SECOND_PRESSED: case GST_SCROLL_SECOND_PRESSED:
case GST_PENDING_SYNTHETIC_CLICK_SECOND_PRESSED: case GST_PENDING_SYNTHETIC_CLICK_SECOND_PRESSED:
PinchStart(event, point, gestures.get()); PinchStart(event, point, gestures.get());
...@@ -240,8 +226,8 @@ GestureSequence::Gestures* GestureSequence::ProcessTouchEventForGesture( ...@@ -240,8 +226,8 @@ GestureSequence::Gestures* GestureSequence::ProcessTouchEventForGesture(
case GST_PINCH_FIRST_MOVED: case GST_PINCH_FIRST_MOVED:
case GST_PINCH_SECOND_MOVED: case GST_PINCH_SECOND_MOVED:
if (PinchUpdate(event, point, gestures.get())) { if (PinchUpdate(event, point, gestures.get())) {
points_[0].UpdateForScroll(); GetPointByPointId(0)->UpdateForScroll();
points_[1].UpdateForScroll(); GetPointByPointId(1)->UpdateForScroll();
} }
break; break;
case GST_PINCH_FIRST_RELEASED: case GST_PINCH_FIRST_RELEASED:
...@@ -265,15 +251,27 @@ GestureSequence::Gestures* GestureSequence::ProcessTouchEventForGesture( ...@@ -265,15 +251,27 @@ GestureSequence::Gestures* GestureSequence::ProcessTouchEventForGesture(
if (last_state == GS_PENDING_SYNTHETIC_CLICK && state_ != last_state) if (last_state == GS_PENDING_SYNTHETIC_CLICK && state_ != last_state)
long_press_timer_->Stop(); long_press_timer_->Stop();
if (event.type() == ui::ET_TOUCH_RELEASED) // The set of point_ids must be contiguous and include 0.
// When a touch point is released, all points with ids greater than the
// released point must have their ids decremented, or the set of point_ids
// could end up with gaps.
if (event.type() == ui::ET_TOUCH_RELEASED) {
GesturePoint& old_point = points_[event.touch_id()];
for (int i = 0; i < kMaxGesturePoints; ++i) {
GesturePoint& point = points_[i];
if (point.point_id() > old_point.point_id())
point.set_point_id(point.point_id() - 1);
}
old_point.Reset();
--point_count_; --point_count_;
}
return gestures.release(); return gestures.release();
} }
void GestureSequence::Reset() { void GestureSequence::Reset() {
set_state(GS_NO_GESTURE); set_state(GS_NO_GESTURE);
for (int i = 0; i < point_count_; ++i) for (int i = 0; i < kMaxGesturePoints; ++i)
points_[i].Reset(); points_[i].Reset();
} }
...@@ -292,6 +290,17 @@ GesturePoint& GestureSequence::GesturePointForEvent( ...@@ -292,6 +290,17 @@ GesturePoint& GestureSequence::GesturePointForEvent(
return points_[event.touch_id()]; return points_[event.touch_id()];
} }
GesturePoint* GestureSequence::GetPointByPointId(int point_id) {
DCHECK(0 <= point_id && point_id < kMaxGesturePoints);
for (int i = 0; i < kMaxGesturePoints; ++i) {
GesturePoint& point = points_[i];
if (point.in_use() && point.point_id() == point_id)
return &point;
}
NOTREACHED();
return NULL;
}
void GestureSequence::AppendTapDownGestureEvent(const GesturePoint& point, void GestureSequence::AppendTapDownGestureEvent(const GesturePoint& point,
Gestures* gestures) { Gestures* gestures) {
gestures->push_back(linked_ptr<GestureEvent>(new GestureEvent( gestures->push_back(linked_ptr<GestureEvent>(new GestureEvent(
...@@ -482,16 +491,14 @@ bool GestureSequence::TouchDown(const TouchEvent& event, ...@@ -482,16 +491,14 @@ bool GestureSequence::TouchDown(const TouchEvent& event,
} }
void GestureSequence::AppendLongPressGestureEvent() { void GestureSequence::AppendLongPressGestureEvent() {
// TODO(tdresser) - this may not always be the first point const GesturePoint* point = GetPointByPointId(0);
const GesturePoint& point = points_[0];
GestureEvent* gesture = new GestureEvent( GestureEvent* gesture = new GestureEvent(
ui::ET_GESTURE_LONG_PRESS, ui::ET_GESTURE_LONG_PRESS,
point.first_touch_position().x(), point->first_touch_position().x(),
point.first_touch_position().y(), point->first_touch_position().y(),
flags_, flags_,
base::Time::FromDoubleT(point.last_touch_time()), base::Time::FromDoubleT(point->last_touch_time()),
point.touch_id(), 0.f); point->point_id(), 0.f);
root_window_->DispatchGestureEvent(gesture); root_window_->DispatchGestureEvent(gesture);
} }
...@@ -514,13 +521,16 @@ bool GestureSequence::PinchStart(const TouchEvent& event, ...@@ -514,13 +521,16 @@ bool GestureSequence::PinchStart(const TouchEvent& event,
state_ == GS_PENDING_SYNTHETIC_CLICK); state_ == GS_PENDING_SYNTHETIC_CLICK);
AppendTapDownGestureEvent(point, gestures); AppendTapDownGestureEvent(point, gestures);
pinch_distance_current_ = points_[0].Distance(points_[1]); const GesturePoint* point1 = GetPointByPointId(0);
const GesturePoint* point2 = GetPointByPointId(1);
pinch_distance_current_ = point1->Distance(*point2);
pinch_distance_start_ = pinch_distance_current_; pinch_distance_start_ = pinch_distance_current_;
AppendPinchGestureBegin(points_[0], points_[1], gestures); AppendPinchGestureBegin(*point1, *point2, gestures);
if (state_ == GS_PENDING_SYNTHETIC_CLICK) { if (state_ == GS_PENDING_SYNTHETIC_CLICK) {
gfx::Point center = points_[0].last_touch_position().Middle( gfx::Point center = point1->last_touch_position().Middle(
points_[1].last_touch_position()); point2->last_touch_position());
AppendScrollGestureBegin(point, center, gestures); AppendScrollGestureBegin(point, center, gestures);
} }
...@@ -530,23 +540,27 @@ bool GestureSequence::PinchStart(const TouchEvent& event, ...@@ -530,23 +540,27 @@ bool GestureSequence::PinchStart(const TouchEvent& event,
bool GestureSequence::PinchUpdate(const TouchEvent& event, bool GestureSequence::PinchUpdate(const TouchEvent& event,
const GesturePoint& point, Gestures* gestures) { const GesturePoint& point, Gestures* gestures) {
DCHECK(state_ == GS_PINCH); DCHECK(state_ == GS_PINCH);
float distance = points_[0].Distance(points_[1]);
const GesturePoint* point1 = GetPointByPointId(0);
const GesturePoint* point2 = GetPointByPointId(1);
float distance = point1->Distance(*point2);
if (abs(distance - pinch_distance_current_) < if (abs(distance - pinch_distance_current_) <
GestureConfiguration::minimum_pinch_update_distance_in_pixels()) { GestureConfiguration::minimum_pinch_update_distance_in_pixels()) {
// The fingers didn't move towards each other, or away from each other, // The fingers didn't move towards each other, or away from each other,
// enough to constitute a pinch. But perhaps they moved enough in the same // enough to constitute a pinch. But perhaps they moved enough in the same
// direction to do a two-finger scroll. // direction to do a two-finger scroll.
if (!points_[0].DidScroll(event, if (!point1->DidScroll(event,
GestureConfiguration::minimum_distance_for_pinch_scroll_in_pixels()) || GestureConfiguration::minimum_distance_for_pinch_scroll_in_pixels()) ||
!points_[1].DidScroll(event, !point2->DidScroll(event,
GestureConfiguration::minimum_distance_for_pinch_scroll_in_pixels())) GestureConfiguration::minimum_distance_for_pinch_scroll_in_pixels()))
return false; return false;
gfx::Point center = points_[0].last_touch_position().Middle( gfx::Point center = point1->last_touch_position().Middle(
points_[1].last_touch_position()); point2->last_touch_position());
AppendScrollGestureUpdate(point, center, gestures); AppendScrollGestureUpdate(point, center, gestures);
} else { } else {
AppendPinchGestureUpdate(points_[0], points_[1], AppendPinchGestureUpdate(*point1, *point2,
distance / pinch_distance_current_, gestures); distance / pinch_distance_current_, gestures);
pinch_distance_current_ = distance; pinch_distance_current_ = distance;
} }
...@@ -556,8 +570,12 @@ bool GestureSequence::PinchUpdate(const TouchEvent& event, ...@@ -556,8 +570,12 @@ bool GestureSequence::PinchUpdate(const TouchEvent& event,
bool GestureSequence::PinchEnd(const TouchEvent& event, bool GestureSequence::PinchEnd(const TouchEvent& event,
const GesturePoint& point, Gestures* gestures) { const GesturePoint& point, Gestures* gestures) {
DCHECK(state_ == GS_PINCH); DCHECK(state_ == GS_PINCH);
float distance = points_[0].Distance(points_[1]);
AppendPinchGestureEnd(points_[0], points_[1], const GesturePoint* point1 = GetPointByPointId(0);
const GesturePoint* point2 = GetPointByPointId(1);
float distance = point1->Distance(*point2);
AppendPinchGestureEnd(*point1, *point2,
distance / pinch_distance_start_, gestures); distance / pinch_distance_start_, gestures);
pinch_distance_start_ = 0; pinch_distance_start_ = 0;
......
...@@ -55,7 +55,11 @@ class AURA_EXPORT GestureSequence { ...@@ -55,7 +55,11 @@ class AURA_EXPORT GestureSequence {
GesturePoint& GesturePointForEvent(const TouchEvent& event); GesturePoint& GesturePointForEvent(const TouchEvent& event);
// Functions to be called to add GestureEvents, after succesful recognition. // Do a linear scan through points_ to find the GesturePoint
// with id |point_id|.
GesturePoint* GetPointByPointId(int point_id);
// Functions to be called to add GestureEvents, after successful recognition.
// Tap gestures. // Tap gestures.
void AppendTapDownGestureEvent(const GesturePoint& point, Gestures* gestures); void AppendTapDownGestureEvent(const GesturePoint& point, Gestures* gestures);
......
...@@ -518,7 +518,8 @@ TEST_F(WindowTest, CaptureTests) { ...@@ -518,7 +518,8 @@ TEST_F(WindowTest, CaptureTests) {
generator.PressLeftButton(); generator.PressLeftButton();
EXPECT_EQ(1, delegate.mouse_event_count()); EXPECT_EQ(1, delegate.mouse_event_count());
root_window()->DispatchTouchEvent(&touchev); TouchEvent touchev2(ui::ET_TOUCH_PRESSED, gfx::Point(50, 50), 1);
root_window()->DispatchTouchEvent(&touchev2);
EXPECT_EQ(0, delegate.touch_event_count()); EXPECT_EQ(0, delegate.touch_event_count());
// Removing the capture window from parent should reset the capture window // Removing the capture window from parent should reset the capture window
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment