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) {
press_point = w1->bounds().CenterPoint();
aura::Window::ConvertPointToWindow(w1->parent(), root_window, &press_point);
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);
// Window2 should still be active and focused.
......@@ -547,13 +547,15 @@ TEST_F(RootWindowEventFilterTest, UpdateCursorVisibility) {
aura::MouseEvent mouse_moved(
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);
aura::TouchEvent touch_pressed2(
ui::ET_TOUCH_PRESSED, gfx::Point(0, 0), 1);
root_window_filter->set_update_cursor_visibility(true);
root_window->DispatchMouseEvent(&mouse_moved);
EXPECT_TRUE(root_window->cursor_shown());
root_window->DispatchTouchEvent(&touch_pressed);
root_window->DispatchTouchEvent(&touch_pressed1);
EXPECT_FALSE(root_window->cursor_shown());
root_window->DispatchMouseEvent(&mouse_moved);
EXPECT_TRUE(root_window->cursor_shown());
......@@ -563,7 +565,7 @@ TEST_F(RootWindowEventFilterTest, UpdateCursorVisibility) {
root_window->DispatchMouseEvent(&mouse_moved);
EXPECT_FALSE(root_window->cursor_shown());
root_window->ShowCursor(true);
root_window->DispatchTouchEvent(&touch_pressed);
root_window->DispatchTouchEvent(&touch_pressed2);
EXPECT_TRUE(root_window->cursor_shown());
}
......
......@@ -27,7 +27,8 @@ GesturePoint::GesturePoint()
: first_touch_time_(0.0),
last_touch_time_(0.0),
last_tap_time_(0.0),
velocity_calculator_(kBufferedPoints) {
velocity_calculator_(kBufferedPoints),
point_id_(-1) {
}
GesturePoint::~GesturePoint() {}
......@@ -35,6 +36,7 @@ GesturePoint::~GesturePoint() {}
void GesturePoint::Reset() {
first_touch_time_ = last_touch_time_ = 0.0;
velocity_calculator_.ClearHistory();
point_id_ = -1;
}
void GesturePoint::UpdateValues(const TouchEvent& event) {
......
......@@ -53,8 +53,16 @@ class GesturePoint {
double last_touch_time() const { return last_touch_time_; }
const gfx::Point& last_touch_position() const { return last_touch_position_; }
void set_touch_id(unsigned int touch_id) { touch_id_ = touch_id; }
unsigned int touch_id() const { return touch_id_; }
// point_id_ is used to drive GestureSequence::ProcessTouchEventForGesture.
// 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 {
return last_touch_position_.x() - first_touch_position_.x();
......@@ -87,7 +95,7 @@ class GesturePoint {
VelocityCalculator velocity_calculator_;
unsigned int touch_id_;
int point_id_;
DISALLOW_COPY_AND_ASSIGN(GesturePoint);
};
......
......@@ -12,11 +12,6 @@
#include "ui/aura/gestures/gesture_configuration.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 {
// TODO(girard): Make these configurable in sync with this CL
......@@ -99,18 +94,6 @@ enum EdgeStateSignatureType {
GST_SCROLL_FIRST_CANCELLED =
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 =
G(GS_SCROLL, 1, TS_PRESSED, false),
......@@ -164,9 +147,6 @@ GestureSequence::GestureSequence(RootWindow* root_window)
long_press_timer_(CreateTimer()),
point_count_(0),
root_window_(root_window) {
for (int i = 0; i < kMaxGesturePoints; ++i) {
points_[i].set_touch_id(i);
}
}
GestureSequence::~GestureSequence() {
......@@ -185,7 +165,14 @@ GestureSequence::Gestures* GestureSequence::ProcessTouchEventForGesture(
if (event.type() == ui::ET_TOUCH_PRESSED) {
if (point_count_ == kMaxGesturePoints)
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_;
......@@ -195,7 +182,10 @@ GestureSequence::Gestures* GestureSequence::ProcessTouchEventForGesture(
GesturePoint& point = GesturePointForEvent(event);
point.UpdateValues(event);
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:
TouchDown(event, point, gestures.get());
set_state(GS_PENDING_SYNTHETIC_CLICK);
......@@ -217,7 +207,6 @@ GestureSequence::Gestures* GestureSequence::ProcessTouchEventForGesture(
NoGesture(event, point, gestures.get());
break;
case GST_SCROLL_FIRST_MOVED:
case GST_SCROLL_SECOND_MOVED:
if (scroll_type_ == ST_VERTICAL ||
scroll_type_ == ST_HORIZONTAL)
BreakRailScroll(event, point, gestures.get());
......@@ -226,12 +215,9 @@ GestureSequence::Gestures* GestureSequence::ProcessTouchEventForGesture(
break;
case GST_SCROLL_FIRST_RELEASED:
case GST_SCROLL_FIRST_CANCELLED:
case GST_SCROLL_SECOND_RELEASED:
case GST_SCROLL_SECOND_CANCELLED:
ScrollEnd(event, point, gestures.get());
set_state(GS_NO_GESTURE);
break;
case GST_SCROLL_FIRST_PRESSED:
case GST_SCROLL_SECOND_PRESSED:
case GST_PENDING_SYNTHETIC_CLICK_SECOND_PRESSED:
PinchStart(event, point, gestures.get());
......@@ -240,8 +226,8 @@ GestureSequence::Gestures* GestureSequence::ProcessTouchEventForGesture(
case GST_PINCH_FIRST_MOVED:
case GST_PINCH_SECOND_MOVED:
if (PinchUpdate(event, point, gestures.get())) {
points_[0].UpdateForScroll();
points_[1].UpdateForScroll();
GetPointByPointId(0)->UpdateForScroll();
GetPointByPointId(1)->UpdateForScroll();
}
break;
case GST_PINCH_FIRST_RELEASED:
......@@ -265,15 +251,27 @@ GestureSequence::Gestures* GestureSequence::ProcessTouchEventForGesture(
if (last_state == GS_PENDING_SYNTHETIC_CLICK && state_ != last_state)
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_;
}
return gestures.release();
}
void GestureSequence::Reset() {
set_state(GS_NO_GESTURE);
for (int i = 0; i < point_count_; ++i)
for (int i = 0; i < kMaxGesturePoints; ++i)
points_[i].Reset();
}
......@@ -292,6 +290,17 @@ GesturePoint& GestureSequence::GesturePointForEvent(
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,
Gestures* gestures) {
gestures->push_back(linked_ptr<GestureEvent>(new GestureEvent(
......@@ -482,16 +491,14 @@ bool GestureSequence::TouchDown(const TouchEvent& event,
}
void GestureSequence::AppendLongPressGestureEvent() {
// TODO(tdresser) - this may not always be the first point
const GesturePoint& point = points_[0];
const GesturePoint* point = GetPointByPointId(0);
GestureEvent* gesture = new GestureEvent(
ui::ET_GESTURE_LONG_PRESS,
point.first_touch_position().x(),
point.first_touch_position().y(),
point->first_touch_position().x(),
point->first_touch_position().y(),
flags_,
base::Time::FromDoubleT(point.last_touch_time()),
point.touch_id(), 0.f);
base::Time::FromDoubleT(point->last_touch_time()),
point->point_id(), 0.f);
root_window_->DispatchGestureEvent(gesture);
}
......@@ -514,13 +521,16 @@ bool GestureSequence::PinchStart(const TouchEvent& event,
state_ == GS_PENDING_SYNTHETIC_CLICK);
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_;
AppendPinchGestureBegin(points_[0], points_[1], gestures);
AppendPinchGestureBegin(*point1, *point2, gestures);
if (state_ == GS_PENDING_SYNTHETIC_CLICK) {
gfx::Point center = points_[0].last_touch_position().Middle(
points_[1].last_touch_position());
gfx::Point center = point1->last_touch_position().Middle(
point2->last_touch_position());
AppendScrollGestureBegin(point, center, gestures);
}
......@@ -530,23 +540,27 @@ bool GestureSequence::PinchStart(const TouchEvent& event,
bool GestureSequence::PinchUpdate(const TouchEvent& event,
const GesturePoint& point, Gestures* gestures) {
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_) <
GestureConfiguration::minimum_pinch_update_distance_in_pixels()) {
// 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
// direction to do a two-finger scroll.
if (!points_[0].DidScroll(event,
if (!point1->DidScroll(event,
GestureConfiguration::minimum_distance_for_pinch_scroll_in_pixels()) ||
!points_[1].DidScroll(event,
!point2->DidScroll(event,
GestureConfiguration::minimum_distance_for_pinch_scroll_in_pixels()))
return false;
gfx::Point center = points_[0].last_touch_position().Middle(
points_[1].last_touch_position());
gfx::Point center = point1->last_touch_position().Middle(
point2->last_touch_position());
AppendScrollGestureUpdate(point, center, gestures);
} else {
AppendPinchGestureUpdate(points_[0], points_[1],
AppendPinchGestureUpdate(*point1, *point2,
distance / pinch_distance_current_, gestures);
pinch_distance_current_ = distance;
}
......@@ -556,8 +570,12 @@ bool GestureSequence::PinchUpdate(const TouchEvent& event,
bool GestureSequence::PinchEnd(const TouchEvent& event,
const GesturePoint& point, Gestures* gestures) {
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);
pinch_distance_start_ = 0;
......
......@@ -55,7 +55,11 @@ class AURA_EXPORT GestureSequence {
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.
void AppendTapDownGestureEvent(const GesturePoint& point, Gestures* gestures);
......
......@@ -518,7 +518,8 @@ TEST_F(WindowTest, CaptureTests) {
generator.PressLeftButton();
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());
// 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