diff options
author | lisayin@chromium.org <lisayin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-08 19:00:36 +0000 |
---|---|---|
committer | lisayin@chromium.org <lisayin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-08 19:00:36 +0000 |
commit | 217ba203fcec6a907e0afed7ba2c189af550db0b (patch) | |
tree | b5465f6bab234ab95c3e676641218d94fb2503b2 /ui/chromeos | |
parent | bd04638669a2434042003c36b0194a0dcbd3ad90 (diff) | |
download | chromium_src-217ba203fcec6a907e0afed7ba2c189af550db0b.zip chromium_src-217ba203fcec6a907e0afed7ba2c189af550db0b.tar.gz chromium_src-217ba203fcec6a907e0afed7ba2c189af550db0b.tar.bz2 |
Swipe Gestures for Accessibility
Register that gestures have been received by the touch_exploration_controller in Accessibility Mode.
Swipe gestures are registered when the user begins a swipe and completes it before the time out period. If a swipe is successfully completed, the keyboard shortcut <shift>+<search>+direction will called.
If the grace period has elapsed, then the mode changes to touch exploration. If an additional finger has been added before the grace period has elapsed, then the mode changes to passthrough.
BUG= 387304
Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=281614
Review URL: https://codereview.chromium.org/330763007
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@281780 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui/chromeos')
-rw-r--r-- | ui/chromeos/touch_exploration_controller.cc | 202 | ||||
-rw-r--r-- | ui/chromeos/touch_exploration_controller.h | 52 | ||||
-rw-r--r-- | ui/chromeos/touch_exploration_controller_unittest.cc | 310 |
3 files changed, 491 insertions, 73 deletions
diff --git a/ui/chromeos/touch_exploration_controller.cc b/ui/chromeos/touch_exploration_controller.cc index 1a77b97..173da5b 100644 --- a/ui/chromeos/touch_exploration_controller.cc +++ b/ui/chromeos/touch_exploration_controller.cc @@ -18,11 +18,17 @@ namespace ui { +namespace { +// In ChromeOS, VKEY_LWIN is synonymous for the search key. +const ui::KeyboardCode kChromeOSSearchKey = ui::VKEY_LWIN; +} // namespace + TouchExplorationController::TouchExplorationController( aura::Window* root_window) : root_window_(root_window), state_(NO_FINGERS_DOWN), event_handler_for_testing_(NULL), + gesture_provider_(this), prev_state_(NO_FINGERS_DOWN) { CHECK(root_window); root_window->GetHost()->GetEventSource()->AddEventRewriter(this); @@ -48,6 +54,10 @@ bool TouchExplorationController::IsInNoFingersDownStateForTesting() const { return state_ == NO_FINGERS_DOWN; } +bool TouchExplorationController::IsInGestureInProgressStateForTesting() const { + return state_ == GESTURE_IN_PROGRESS; +} + ui::EventRewriteStatus TouchExplorationController::RewriteEvent( const ui::Event& event, scoped_ptr<ui::Event>* rewritten_event) { @@ -59,9 +69,6 @@ ui::EventRewriteStatus TouchExplorationController::RewriteEvent( << ", Flags: " << key_event.flags() << ", Is char: " << key_event.is_char(); } - if(event.IsGestureEvent()){ - VLOG(0) << "\n Gesture event " << event.name(); - } return ui::EVENT_REWRITE_CONTINUE; } const ui::TouchEvent& touch_event = static_cast<const ui::TouchEvent&>(event); @@ -123,6 +130,8 @@ ui::EventRewriteStatus TouchExplorationController::RewriteEvent( return InDoubleTapPressed(touch_event, rewritten_event); case TOUCH_EXPLORATION: return InTouchExploration(touch_event, rewritten_event); + case GESTURE_IN_PROGRESS: + return InGestureInProgress(touch_event, rewritten_event); case TOUCH_EXPLORE_SECOND_PRESS: return InTouchExploreSecondPress(touch_event, rewritten_event); case TWO_TO_ONE_FINGER: @@ -152,6 +161,9 @@ ui::EventRewriteStatus TouchExplorationController::InNoFingersDown( gesture_detector_config_.double_tap_timeout, this, &TouchExplorationController::OnTapTimerFired); + gesture_provider_.OnTouchEvent(event); + gesture_provider_.OnTouchEventAck(false); + ProcessGestureEvents(); state_ = SINGLE_TAP_PRESSED; VLOG_STATE(); return ui::EVENT_REWRITE_DISCARD; @@ -182,19 +194,33 @@ ui::EventRewriteStatus TouchExplorationController::InSingleTapPressed( VLOG_STATE(); return EVENT_REWRITE_DISCARD; } else if (type == ui::ET_TOUCH_MOVED) { - // If the user moves far enough from the initial touch location (outside - // the "slop" region, jump to the touch exploration mode early. - // TODO(evy, lisayin): Add gesture recognition here instead - - // we should probably jump to gesture mode here if the velocity is - // high enough, and touch exploration if the velocity is lower. - float delta = (event.location() - initial_press_->location()).Length(); - if (delta > gesture_detector_config_.touch_slop) { - EnterTouchToMouseMode(); - state_ = TOUCH_EXPLORATION; + float distance = (event.location() - initial_press_->location()).Length(); + // If the user does not move far enough from the original position, then the + // resulting movement should not be considered to be a deliberate gesture or + // touch exploration. + if (distance <= gesture_detector_config_.touch_slop) + return EVENT_REWRITE_DISCARD; + + float delta_time = + (event.time_stamp() - initial_press_->time_stamp()).InSecondsF(); + float velocity = distance / delta_time; + VLOG(0) << "\n Delta time: " << delta_time + << "\n Distance: " << distance + << "\n Velocity of click: " << velocity + << "\n Minimum swipe velocity: " + << gesture_detector_config_.minimum_swipe_velocity; + + // If the user moves fast enough from the initial touch location, start + // gesture detection. Otherwise, jump to the touch exploration mode early. + if (velocity > gesture_detector_config_.minimum_swipe_velocity) { + state_ = GESTURE_IN_PROGRESS; VLOG_STATE(); - return InTouchExploration(event, rewritten_event); + return InGestureInProgress(event, rewritten_event); } - return EVENT_REWRITE_DISCARD; + EnterTouchToMouseMode(); + state_ = TOUCH_EXPLORATION; + VLOG_STATE(); + return InTouchExploration(event, rewritten_event); } NOTREACHED() << "Unexpected event type received."; return ui::EVENT_REWRITE_CONTINUE; @@ -205,13 +231,23 @@ TouchExplorationController::InSingleTapOrTouchExploreReleased( const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event) { const ui::EventType type = event.type(); + // If there is more than one finger down, then discard to wait until only one + // finger is or no fingers are down. + if (current_touch_ids_.size() > 1) { + state_ = WAIT_FOR_RELEASE; + return ui::EVENT_REWRITE_DISCARD; + } + // If there is no touch exploration yet, discard. + if (!last_touch_exploration_ || type == ui::ET_TOUCH_RELEASED) { + if (current_touch_ids_.size() == 0) { + ResetToNoFingersDown(); + } + return ui::EVENT_REWRITE_DISCARD; + } + if (type == ui::ET_TOUCH_PRESSED) { // This is the second tap in a double-tap (or double tap-hold). // Rewrite at location of last touch exploration. - // If there is no touch exploration yet, discard instead. - if (!last_touch_exploration_) { - return ui::EVENT_REWRITE_DISCARD; - } rewritten_event->reset( new ui::TouchEvent(ui::ET_TOUCH_PRESSED, last_touch_exploration_->location(), @@ -221,13 +257,6 @@ TouchExplorationController::InSingleTapOrTouchExploreReleased( state_ = DOUBLE_TAP_PRESSED; VLOG_STATE(); return ui::EVENT_REWRITE_REWRITTEN; - } else if (type == ui::ET_TOUCH_RELEASED && !last_touch_exploration_) { - // If the previous press was discarded, we need to also handle its - // release. - if (current_touch_ids_.size() == 0) { - ResetToNoFingersDown(); - } - return ui::EVENT_REWRITE_DISCARD; } NOTREACHED() << "Unexpected event type received."; return ui::EVENT_REWRITE_CONTINUE; @@ -243,7 +272,7 @@ ui::EventRewriteStatus TouchExplorationController::InDoubleTapPressed( return EVENT_REWRITE_DISCARD; // Rewrite release at location of last touch exploration with the same - // id as the prevoius press. + // id as the previous press. rewritten_event->reset( new ui::TouchEvent(ui::ET_TOUCH_RELEASED, last_touch_exploration_->location(), @@ -296,6 +325,44 @@ ui::EventRewriteStatus TouchExplorationController::InTouchExploration( return ui::EVENT_REWRITE_REWRITTEN; } +ui::EventRewriteStatus TouchExplorationController::InGestureInProgress( + const ui::TouchEvent& event, + scoped_ptr<ui::Event>* rewritten_event) { + ui::EventType type = event.type(); + // If additional fingers are added before a swipe gesture has been registered, + // then the state will no longer be GESTURE_IN_PROGRESS. + if (type == ui::ET_TOUCH_PRESSED || + event.touch_id() != initial_press_->touch_id()) { + if (tap_timer_.IsRunning()) + tap_timer_.Stop(); + // Discard any pending gestures. + ignore_result(gesture_provider_.GetAndResetPendingGestures()); + state_ = TWO_TO_ONE_FINGER; + last_two_to_one_.reset(new TouchEvent(event)); + rewritten_event->reset(new ui::TouchEvent(ui::ET_TOUCH_PRESSED, + event.location(), + event.touch_id(), + event.time_stamp())); + (*rewritten_event)->set_flags(event.flags()); + return EVENT_REWRITE_REWRITTEN; + } + + // There should not be more than one finger down. + DCHECK(current_touch_ids_.size() <= 1); + if (type == ui::ET_TOUCH_MOVED) { + gesture_provider_.OnTouchEvent(event); + gesture_provider_.OnTouchEventAck(false); + } + if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) { + gesture_provider_.OnTouchEvent(event); + gesture_provider_.OnTouchEventAck(false); + if (current_touch_ids_.size() == 0) + ResetToNoFingersDown(); + } + + ProcessGestureEvents(); + return ui::EVENT_REWRITE_DISCARD; +} ui::EventRewriteStatus TouchExplorationController::InTwoToOneFinger( const ui::TouchEvent& event, @@ -414,6 +481,7 @@ ui::EventRewriteStatus TouchExplorationController::InTouchExploreSecondPress( initial_press_->touch_id(), event.time_stamp())); (*rewritten_event)->set_flags(event.flags()); + EnterTouchToMouseMode(); state_ = TOUCH_EXPLORATION; VLOG_STATE(); return ui::EVENT_REWRITE_REWRITTEN; @@ -433,6 +501,7 @@ ui::EventRewriteStatus TouchExplorationController::InWaitForRelease( } if (current_touch_ids_.size() == 0) { state_ = NO_FINGERS_DOWN; + VLOG_STATE(); ResetToNoFingersDown(); } return EVENT_REWRITE_DISCARD; @@ -448,6 +517,9 @@ void TouchExplorationController::OnTapTimerFired() { last_touch_exploration_.reset(new TouchEvent(*initial_press_)); return; case SINGLE_TAP_PRESSED: + case GESTURE_IN_PROGRESS: + // Discard any pending gestures. + ignore_result(gesture_provider_.GetAndResetPendingGestures()); EnterTouchToMouseMode(); state_ = TOUCH_EXPLORATION; VLOG_STATE(); @@ -455,12 +527,11 @@ void TouchExplorationController::OnTapTimerFired() { default: return; } - - scoped_ptr<ui::Event> mouse_move = CreateMouseMoveEvent( - initial_press_->location(), initial_press_->flags()); + scoped_ptr<ui::Event> mouse_move = + CreateMouseMoveEvent(initial_press_->location(), initial_press_->flags()); DispatchEvent(mouse_move.get()); last_touch_exploration_.reset(new TouchEvent(*initial_press_)); -} + } void TouchExplorationController::DispatchEvent(ui::Event* event) { if (event_handler_for_testing_) { @@ -471,6 +542,75 @@ void TouchExplorationController::DispatchEvent(ui::Event* event) { root_window_->GetHost()->dispatcher()->OnEventFromSource(event); } +void TouchExplorationController::OnGestureEvent(ui::GestureEvent* gesture) { + CHECK(gesture->IsGestureEvent()); + VLOG(0) << " \n Gesture Triggered: " << gesture->name(); + if (gesture->type() == ui::ET_GESTURE_SWIPE) { + if (tap_timer_.IsRunning()) + tap_timer_.Stop(); + OnSwipeEvent(gesture); + return; + } +} + +void TouchExplorationController::ProcessGestureEvents() { + scoped_ptr<ScopedVector<ui::GestureEvent> > gestures( + gesture_provider_.GetAndResetPendingGestures()); + if (gestures) { + for (ScopedVector<GestureEvent>::iterator i = gestures->begin(); + i != gestures->end(); + ++i) { + OnGestureEvent(*i); + } + } +} + +void TouchExplorationController::OnSwipeEvent(ui::GestureEvent* swipe_gesture) { + // A swipe gesture contains details for the direction in which the swipe + // occurred. + GestureEventDetails event_details = swipe_gesture->details(); + if (event_details.swipe_left()) { + DispatchShiftSearchKeyEvent(ui::VKEY_LEFT); + return; + } else if (event_details.swipe_right()) { + DispatchShiftSearchKeyEvent(ui::VKEY_RIGHT); + return; + } else if (event_details.swipe_up()) { + DispatchShiftSearchKeyEvent(ui::VKEY_UP); + return; + } else if (event_details.swipe_down()) { + DispatchShiftSearchKeyEvent(ui::VKEY_DOWN); + return; + } +} + +void TouchExplorationController::DispatchShiftSearchKeyEvent( + const ui::KeyboardCode direction) { + // In order to activate the shortcut shift+search+<arrow key> + // three KeyPressed events must be dispatched in succession along + // with three KeyReleased events. + ui::KeyEvent shift_down = ui::KeyEvent( + ui::ET_KEY_PRESSED, ui::VKEY_SHIFT, ui::EF_SHIFT_DOWN, false); + ui::KeyEvent search_down = ui::KeyEvent( + ui::ET_KEY_PRESSED, kChromeOSSearchKey, ui::EF_SHIFT_DOWN, false); + ui::KeyEvent direction_down = + ui::KeyEvent(ui::ET_KEY_PRESSED, direction, ui::EF_SHIFT_DOWN, false); + + ui::KeyEvent direction_up = + ui::KeyEvent(ui::ET_KEY_RELEASED, direction, ui::EF_SHIFT_DOWN, false); + ui::KeyEvent search_up = ui::KeyEvent( + ui::ET_KEY_RELEASED, kChromeOSSearchKey, ui::EF_SHIFT_DOWN, false); + ui::KeyEvent shift_up = + ui::KeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_SHIFT, ui::EF_NONE, false); + + DispatchEvent(&shift_down); + DispatchEvent(&search_down); + DispatchEvent(&direction_down); + DispatchEvent(&direction_up); + DispatchEvent(&search_up); + DispatchEvent(&shift_up); +} + scoped_ptr<ui::Event> TouchExplorationController::CreateMouseMoveEvent( const gfx::PointF& location, int flags) { @@ -549,6 +689,8 @@ const char* TouchExplorationController::EnumStateToString(State state) { return "DOUBLE_TAP_PRESSED"; case TOUCH_EXPLORATION: return "TOUCH_EXPLORATION"; + case GESTURE_IN_PROGRESS: + return "GESTURE_IN_PROGRESS"; case TOUCH_EXPLORE_SECOND_PRESS: return "TOUCH_EXPLORE_SECOND_PRESS"; case TWO_TO_ONE_FINGER: diff --git a/ui/chromeos/touch_exploration_controller.h b/ui/chromeos/touch_exploration_controller.h index 100a47a..6ade40a 100644 --- a/ui/chromeos/touch_exploration_controller.h +++ b/ui/chromeos/touch_exploration_controller.h @@ -11,6 +11,7 @@ #include "ui/events/event.h" #include "ui/events/event_rewriter.h" #include "ui/events/gesture_detection/gesture_detector.h" +#include "ui/events/gestures/gesture_provider_aura.h" #include "ui/gfx/geometry/point.h" namespace aura { @@ -21,10 +22,13 @@ namespace ui { class Event; class EventHandler; +class GestureEvent; +class GestureProviderAura; class TouchEvent; // TouchExplorationController is used in tandem with "Spoken Feedback" to -// make the touch UI accessible. +// make the touch UI accessible. Gestures are mapped to accessiblity key +// shortcuts. // // ** Short version ** // @@ -32,7 +36,8 @@ class TouchEvent; // exploring the screen gets turned into mouse moves (which can then be // spoken by an accessibility service running), a single tap while the user // is in touch exploration or a double-tap simulates a click, and gestures -// can be used to send high-level accessibility commands. +// can be used to send high-level accessibility commands. For example, a swipe +// right would correspond to the keyboard short cut shift+search+right. // When two or more fingers are pressed initially, from then on the events // are passed through, but with the initial finger removed - so if you swipe // down with two fingers, the running app will see a one-finger swipe. @@ -61,6 +66,14 @@ class TouchEvent; // location. This allows the user to perform a single tap // anywhere to activate it. // +// The user can perform swipe gestures in one of the four cardinal directions +// which will be interpreted and used to control the UI. The gesture will only +// be registered if the finger moves outside the slop and completed within the +// grace period. If additional fingers are added during the grace period, the +// state changes to passthrough. If the gesture fails to be completed within the +// grace period, the state changes to touch exploration mode. Once the state has +// changed, any gestures made during the grace period are discarded. +// // If the user double-taps, the second tap is passed through, allowing the // user to click - however, the double-tap location is changed to the location // of the last successful touch exploration - that allows the user to explore @@ -84,7 +97,8 @@ class TouchEvent; // The caller is expected to retain ownership of instances of this class and // destroy them before |root_window| is destroyed. class UI_CHROMEOS_EXPORT TouchExplorationController - : public ui::EventRewriter { + : public ui::EventRewriter, + public ui::GestureProviderAuraClient { public: explicit TouchExplorationController(aura::Window* root_window); virtual ~TouchExplorationController(); @@ -92,6 +106,7 @@ class UI_CHROMEOS_EXPORT TouchExplorationController void CallTapTimerNowForTesting(); void SetEventHandlerForTesting(ui::EventHandler* event_handler_for_testing); bool IsInNoFingersDownStateForTesting() const; + bool IsInGestureInProgressStateForTesting() const; private: // Overridden from ui::EventRewriter @@ -116,6 +131,8 @@ class UI_CHROMEOS_EXPORT TouchExplorationController const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event); ui::EventRewriteStatus InPassthrough( const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event); + ui::EventRewriteStatus InGestureInProgress( + const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event); ui::EventRewriteStatus InTouchExploreSecondPress( const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event); ui::EventRewriteStatus InWaitForRelease( @@ -130,6 +147,23 @@ class UI_CHROMEOS_EXPORT TouchExplorationController // Dispatch a new event outside of the event rewriting flow. void DispatchEvent(ui::Event* event); + // Overridden from GestureProviderAuraClient. + // + // The gesture provider keeps track of all the touch events after + // the user moves fast enough to trigger a gesture. After the user + // completes their gesture, this method will decide what keyboard + // input their gesture corresponded to. + virtual void OnGestureEvent(ui::GestureEvent* gesture) OVERRIDE; + + // Process the gesture events that have been created. + void ProcessGestureEvents(); + + void OnSwipeEvent(ui::GestureEvent* swipe_gesture); + + // Dispatches the keyboard short cut Shift+Search+<arrow key> + // outside the event rewritting flow. + void DispatchShiftSearchKeyEvent(const ui::KeyboardCode direction); + scoped_ptr<ui::Event> CreateMouseMoveEvent(const gfx::PointF& location, int flags); @@ -172,6 +206,15 @@ class UI_CHROMEOS_EXPORT TouchExplorationController // mode until all fingers are lifted. TOUCH_EXPLORATION, + // If the user moves their finger faster than the threshold velocity after a + // single tap, the touch events that follow will be translated into gesture + // events. If the user successfully completes a gesture within the grace + // period, the gesture will be interpreted and used to control the UI via + // discrete actions - currently by synthesizing key events corresponding to + // each gesture Otherwise, the collected gestures are discarded and the + // state changes to touch_exploration. + GESTURE_IN_PROGRESS, + // The user was in touch exploration, but has placed down another finger. // If the user releases the second finger, a touch press and release // will go through at the last touch explore location. If the user @@ -244,6 +287,9 @@ class UI_CHROMEOS_EXPORT TouchExplorationController // timeout and pixel slop constants. ui::GestureDetector::Config gesture_detector_config_; + // Gesture Handler to interpret the touch events. + ui::GestureProviderAura gesture_provider_; + // The previous state entered. State prev_state_; diff --git a/ui/chromeos/touch_exploration_controller_unittest.cc b/ui/chromeos/touch_exploration_controller_unittest.cc index 10da4b1..3d0fbb6 100644 --- a/ui/chromeos/touch_exploration_controller_unittest.cc +++ b/ui/chromeos/touch_exploration_controller_unittest.cc @@ -13,6 +13,7 @@ #include "ui/aura/window.h" #include "ui/events/event.h" #include "ui/events/event_utils.h" +#include "ui/events/gestures/gesture_provider_aura.h" #include "ui/gfx/geometry/point.h" #include "ui/gl/gl_implementation.h" #include "ui/gl/gl_surface.h" @@ -20,7 +21,7 @@ namespace ui { namespace { -// Records all mouse and touch events. +// Records all mouse, touch, gesture, and key events. class EventCapturer : public ui::EventHandler { public: EventCapturer() {} @@ -37,6 +38,11 @@ class EventCapturer : public ui::EventHandler { } else if (event->IsTouchEvent()) { events_.push_back( new ui::TouchEvent(static_cast<ui::TouchEvent&>(*event))); + } else if (event->IsGestureEvent()) { + events_.push_back( + new ui::GestureEvent(static_cast<ui::GestureEvent&>(*event))); + } else if (event->IsKeyEvent()) { + events_.push_back(new ui::KeyEvent(static_cast<ui::KeyEvent&>(*event))); } else { return; } @@ -47,12 +53,11 @@ class EventCapturer : public ui::EventHandler { // exit early with a sensible error rather than letting the test time out. ASSERT_LT(events_.size(), 100u); } - const ScopedVector<ui::LocatedEvent>& captured_events() const { - return events_; - } + + const ScopedVector<ui::Event>& captured_events() const { return events_; } private: - ScopedVector<ui::LocatedEvent> events_; + ScopedVector<ui::Event> events_; DISALLOW_COPY_AND_ASSIGN(EventCapturer); }; @@ -61,8 +66,10 @@ class EventCapturer : public ui::EventHandler { class TouchExplorationTest : public aura::test::AuraTestBase { public: - TouchExplorationTest() - : simulated_clock_(new base::SimpleTestTickClock()) {} + TouchExplorationTest() : simulated_clock_(new base::SimpleTestTickClock()) { + // Tests fail if time is ever 0. + simulated_clock_->Advance(base::TimeDelta::FromMilliseconds(10)); + } virtual ~TouchExplorationTest() {} virtual void SetUp() OVERRIDE { @@ -88,13 +95,26 @@ class TouchExplorationTest : public aura::test::AuraTestBase { protected: aura::client::CursorClient* cursor_client() { return cursor_client_.get(); } - const ScopedVector<ui::LocatedEvent>& GetCapturedEvents() { + const ScopedVector<ui::Event>& GetCapturedEvents() { return event_capturer_.captured_events(); } - std::vector<ui::LocatedEvent*> GetCapturedEventsOfType(int type) { - const ScopedVector<ui::LocatedEvent>& all_events = GetCapturedEvents(); - std::vector<ui::LocatedEvent*> events; + std::vector<ui::LocatedEvent*> GetCapturedLocatedEvents() { + const ScopedVector<ui::Event>& all_events = GetCapturedEvents(); + std::vector<ui::LocatedEvent*> located_events; + for (size_t i = 0; i < all_events.size(); ++i) { + if (all_events[i]->IsMouseEvent() || + all_events[i]->IsTouchEvent() || + all_events[i]->IsGestureEvent()) { + located_events.push_back(static_cast<ui::LocatedEvent*>(all_events[i])); + } + } + return located_events; + } + + std::vector<ui::Event*> GetCapturedEventsOfType(int type) { + const ScopedVector<ui::Event>& all_events = GetCapturedEvents(); + std::vector<ui::Event*> events; for (size_t i = 0; i < all_events.size(); ++i) { if (type == all_events[i]->type()) events.push_back(all_events[i]); @@ -102,6 +122,16 @@ class TouchExplorationTest : public aura::test::AuraTestBase { return events; } + std::vector<ui::LocatedEvent*> GetCapturedLocatedEventsOfType(int type) { + std::vector<ui::LocatedEvent*> located_events = GetCapturedLocatedEvents(); + std::vector<ui::LocatedEvent*> events; + for (size_t i = 0; i < located_events.size(); ++i) { + if (type == located_events[i]->type()) + events.push_back(located_events[i]); + } + return events; + } + void ClearCapturedEvents() { event_capturer_.Reset(); } @@ -154,6 +184,11 @@ class TouchExplorationTest : public aura::test::AuraTestBase { return touch_exploration_controller_->IsInNoFingersDownStateForTesting(); } + bool IsInGestureInProgressState() { + return touch_exploration_controller_ + ->IsInGestureInProgressStateForTesting(); + } + base::TimeDelta Now() { // This is the same as what EventTimeForNow() does, but here we do it // with our simulated clock. @@ -201,16 +236,57 @@ void ConfirmEventsAreMouseAndEqual(ui::Event* e1, ui::Event* e2) { EXPECT_EQ(mouse_event1->flags(), mouse_event2->flags()); } +// Executes a number of assertions to confirm that |e1| and |e2| are key events +// and are equal to each other. +void ConfirmEventsAreKeyAndEqual(ui::Event* e1, ui::Event* e2) { + ASSERT_TRUE(e1->IsKeyEvent()); + ASSERT_TRUE(e2->IsKeyEvent()); + ui::KeyEvent* key_event1 = static_cast<ui::KeyEvent*>(e1); + ui::KeyEvent* key_event2 = static_cast<ui::KeyEvent*>(e2); + EXPECT_EQ(key_event1->type(), key_event2->type()); + EXPECT_EQ(key_event1->key_code(), key_event2->key_code()); + EXPECT_EQ(key_event1->code(), key_event2->code()); + EXPECT_EQ(key_event1->flags(), key_event2->flags()); +} + #define CONFIRM_EVENTS_ARE_TOUCH_AND_EQUAL(e1, e2) \ ASSERT_NO_FATAL_FAILURE(ConfirmEventsAreTouchAndEqual(e1, e2)) #define CONFIRM_EVENTS_ARE_MOUSE_AND_EQUAL(e1, e2) \ ASSERT_NO_FATAL_FAILURE(ConfirmEventsAreMouseAndEqual(e1, e2)) +#define CONFIRM_EVENTS_ARE_KEY_AND_EQUAL(e1, e2) \ + ASSERT_NO_FATAL_FAILURE(ConfirmEventsAreKeyAndEqual(e1, e2)) + // TODO(mfomitchev): Need to investigate why we don't get mouse enter/exit // events when running these tests as part of ui_unittests. We do get them when // the tests are run as part of ash unit tests. +// If a swipe has been successfully completed, then six key events will be +// dispatched that correspond to shift+search+direction +void AssertDirectionalNavigationEvents(const ScopedVector<ui::Event>& events, + ui::KeyboardCode direction) { + ASSERT_EQ(6U, events.size()); + ui::KeyEvent shift_pressed( + ui::ET_KEY_PRESSED, ui::VKEY_SHIFT, ui::EF_SHIFT_DOWN, false); + ui::KeyEvent search_pressed( + ui::ET_KEY_PRESSED, ui::VKEY_LWIN, ui::EF_SHIFT_DOWN, false); + ui::KeyEvent direction_pressed( + ui::ET_KEY_PRESSED, direction, ui::EF_SHIFT_DOWN, false); + ui::KeyEvent direction_released( + ui::ET_KEY_RELEASED, direction, ui::EF_SHIFT_DOWN, false); + ui::KeyEvent search_released( + ui::ET_KEY_RELEASED, VKEY_LWIN, ui::EF_SHIFT_DOWN, false); + ui::KeyEvent shift_released( + ui::ET_KEY_RELEASED, ui::VKEY_SHIFT, ui::EF_NONE, false); + CONFIRM_EVENTS_ARE_KEY_AND_EQUAL(&shift_pressed, events[0]); + CONFIRM_EVENTS_ARE_KEY_AND_EQUAL(&search_pressed, events[1]); + CONFIRM_EVENTS_ARE_KEY_AND_EQUAL(&direction_pressed, events[2]); + CONFIRM_EVENTS_ARE_KEY_AND_EQUAL(&direction_released, events[3]); + CONFIRM_EVENTS_ARE_KEY_AND_EQUAL(&search_released, events[4]); + CONFIRM_EVENTS_ARE_KEY_AND_EQUAL(&shift_released, events[5]); +} + TEST_F(TouchExplorationTest, EntersTouchToMouseModeAfterPressAndDelay) { SwitchTouchExplorationMode(true); EXPECT_FALSE(IsInTouchToMouseMode()); @@ -231,6 +307,7 @@ TEST_F(TouchExplorationTest, EntersTouchToMouseModeAfterMoveOutsideSlop) { EXPECT_FALSE(IsInTouchToMouseMode()); generator_->MoveTouch(gfx::Point(11, 12 + half_slop)); EXPECT_FALSE(IsInTouchToMouseMode()); + AdvanceSimulatedTimePastTapDelay(); generator_->MoveTouch(gfx::Point(11 + slop + 1, 12)); EXPECT_TRUE(IsInTouchToMouseMode()); } @@ -244,7 +321,7 @@ TEST_F(TouchExplorationTest, OneFingerTap) { AdvanceSimulatedTimePastTapDelay(); std::vector<ui::LocatedEvent*> events = - GetCapturedEventsOfType(ui::ET_MOUSE_MOVED); + GetCapturedLocatedEventsOfType(ui::ET_MOUSE_MOVED); ASSERT_EQ(1U, events.size()); EXPECT_EQ(location, events[0]->location()); @@ -274,7 +351,7 @@ TEST_F(TouchExplorationTest, ActualMouseMovesUnaffected) { AdvanceSimulatedTimePastTapDelay(); std::vector<ui::LocatedEvent*> events = - GetCapturedEventsOfType(ui::ET_MOUSE_MOVED); + GetCapturedLocatedEventsOfType(ui::ET_MOUSE_MOVED); ASSERT_EQ(4U, events.size()); EXPECT_EQ(location_start, events[0]->location()); @@ -319,7 +396,7 @@ TEST_F(TouchExplorationTest, TurnOnMidTouch) { generator_->Dispatch(&touch_move); EXPECT_TRUE(cursor_client()->IsCursorVisible()); EXPECT_FALSE(cursor_client()->IsMouseEventsEnabled()); - const ScopedVector<ui::LocatedEvent>& captured_events = GetCapturedEvents(); + std::vector<ui::LocatedEvent*> captured_events = GetCapturedLocatedEvents(); ASSERT_EQ(1u, captured_events.size()); CONFIRM_EVENTS_ARE_TOUCH_AND_EQUAL(captured_events[0], &touch_move); ClearCapturedEvents(); @@ -328,7 +405,8 @@ TEST_F(TouchExplorationTest, TurnOnMidTouch) { generator_->PressTouchId(2); AdvanceSimulatedTimePastTapDelay(); EXPECT_TRUE(IsInTouchToMouseMode()); - ScopedVector<ui::LocatedEvent>::const_iterator it; + captured_events = GetCapturedLocatedEvents(); + std::vector<ui::LocatedEvent*>::const_iterator it; for (it = captured_events.begin(); it != captured_events.end(); ++it) { if ((*it)->type() == ui::ET_MOUSE_MOVED) break; @@ -342,6 +420,7 @@ TEST_F(TouchExplorationTest, TurnOnMidTouch) { 1, Now()); generator_->Dispatch(&touch_release); + captured_events = GetCapturedLocatedEvents(); ASSERT_EQ(1u, captured_events.size()); CONFIRM_EVENTS_ARE_TOUCH_AND_EQUAL(captured_events[0], &touch_release); ClearCapturedEvents(); @@ -350,7 +429,7 @@ TEST_F(TouchExplorationTest, TurnOnMidTouch) { generator_->MoveTouchId(gfx::Point(13, 14), 2); generator_->ReleaseTouchId(2); AdvanceSimulatedTimePastTapDelay(); - + captured_events = GetCapturedLocatedEvents(); ASSERT_EQ(2u, captured_events.size()); EXPECT_EQ(ui::ET_MOUSE_MOVED, captured_events[0]->type()); EXPECT_EQ(ui::ET_MOUSE_MOVED, captured_events[1]->type()); @@ -367,7 +446,7 @@ TEST_F(TouchExplorationTest, TimerFiresLateDuringTouchExploration) { simulated_clock_->Advance(base::TimeDelta::FromMilliseconds(1000)); generator_->PressTouchId(2); std::vector<ui::LocatedEvent*> events = - GetCapturedEventsOfType(ui::ET_MOUSE_MOVED); + GetCapturedLocatedEventsOfType(ui::ET_MOUSE_MOVED); ASSERT_EQ(1U, events.size()); EXPECT_TRUE(events[0]->flags() & ui::EF_IS_SYNTHESIZED); EXPECT_TRUE(events[0]->flags() & ui::EF_TOUCH_ACCESSIBILITY); @@ -400,7 +479,7 @@ TEST_F(TouchExplorationTest, TimerFiresLateAfterTap) { AdvanceSimulatedTimePastTapDelay(); std::vector<ui::LocatedEvent*> events = - GetCapturedEventsOfType(ui::ET_MOUSE_MOVED); + GetCapturedLocatedEventsOfType(ui::ET_MOUSE_MOVED); ASSERT_EQ(2U, events.size()); EXPECT_EQ(location0, events[0]->location()); EXPECT_TRUE(events[0]->flags() & ui::EF_IS_SYNTHESIZED); @@ -424,7 +503,7 @@ TEST_F(TouchExplorationTest, DoubleTap) { AdvanceSimulatedTimePastTapDelay(); std::vector<ui::LocatedEvent*> events = - GetCapturedEventsOfType(ui::ET_MOUSE_MOVED); + GetCapturedLocatedEventsOfType(ui::ET_MOUSE_MOVED); ASSERT_EQ(1U, events.size()); EXPECT_EQ(tap_location, events[0]->location()); @@ -442,7 +521,7 @@ TEST_F(TouchExplorationTest, DoubleTap) { generator_->PressTouch(); generator_->ReleaseTouch(); - const ScopedVector<ui::LocatedEvent>& captured_events = GetCapturedEvents(); + std::vector<ui::LocatedEvent*> captured_events = GetCapturedLocatedEvents(); ASSERT_EQ(2U, captured_events.size()); EXPECT_EQ(ui::ET_TOUCH_PRESSED, captured_events[0]->type()); EXPECT_EQ(tap_location, captured_events[0]->location()); @@ -465,7 +544,7 @@ TEST_F(TouchExplorationTest, DoubleTapLongPress) { AdvanceSimulatedTimePastTapDelay(); std::vector<ui::LocatedEvent*> events = - GetCapturedEventsOfType(ui::ET_MOUSE_MOVED); + GetCapturedLocatedEventsOfType(ui::ET_MOUSE_MOVED); ASSERT_EQ(1U, events.size()); EXPECT_EQ(tap_location, events[0]->location()); @@ -487,7 +566,7 @@ TEST_F(TouchExplorationTest, DoubleTapLongPress) { simulated_clock_->Advance(gesture_detector_config_.longpress_timeout); generator_->ReleaseTouch(); - const ScopedVector<ui::LocatedEvent>& captured_events = GetCapturedEvents(); + std::vector<ui::LocatedEvent*> captured_events = GetCapturedLocatedEvents(); ASSERT_EQ(2U, captured_events.size()); EXPECT_EQ(ui::ET_TOUCH_PRESSED, captured_events[0]->type()); EXPECT_EQ(tap_location, captured_events[0]->location()); @@ -509,6 +588,8 @@ TEST_F(TouchExplorationTest, SingleTap) { gfx::Point initial_location(11, 12); generator_->set_current_location(initial_location); generator_->PressTouch(); + AdvanceSimulatedTimePastTapDelay(); + ClearCapturedEvents(); // Move to another location for single tap gfx::Point tap_location(22, 23); @@ -523,7 +604,7 @@ TEST_F(TouchExplorationTest, SingleTap) { generator_->PressTouch(); generator_->ReleaseTouch(); - const ScopedVector<ui::LocatedEvent>& captured_events = GetCapturedEvents(); + std::vector<ui::LocatedEvent*> captured_events = GetCapturedLocatedEvents(); ASSERT_EQ(4U, captured_events.size()); EXPECT_EQ(ui::ET_MOUSE_MOVED, captured_events[0]->type()); EXPECT_EQ(ui::ET_MOUSE_MOVED, captured_events[1]->type()); @@ -549,7 +630,7 @@ TEST_F(TouchExplorationTest, DoubleTapNoTouchExplore) { generator_->PressTouch(); generator_->ReleaseTouch(); - const ScopedVector<ui::LocatedEvent>& captured_events = GetCapturedEvents(); + std::vector<ui::LocatedEvent*> captured_events = GetCapturedLocatedEvents(); ASSERT_EQ(0U, captured_events.size()); } @@ -564,7 +645,7 @@ TEST_F(TouchExplorationTest, SplitTap) { // Tap and hold at one location, and get a mouse move event in touch explore. EnterTouchExplorationModeAtLocation(initial_touch_location); std::vector<ui::LocatedEvent*> events = - GetCapturedEventsOfType(ui::ET_MOUSE_MOVED); + GetCapturedLocatedEventsOfType(ui::ET_MOUSE_MOVED); ASSERT_EQ(1U, events.size()); EXPECT_EQ(initial_touch_location, events[0]->location()); @@ -579,12 +660,13 @@ TEST_F(TouchExplorationTest, SplitTap) { ui::TouchEvent split_tap_press( ui::ET_TOUCH_PRESSED, second_touch_location, 1, Now()); generator_->Dispatch(&split_tap_press); + EXPECT_FALSE(IsInGestureInProgressState()); ui::TouchEvent split_tap_release( ui::ET_TOUCH_RELEASED, second_touch_location, 1, Now()); generator_->Dispatch(&split_tap_release); EXPECT_FALSE(IsInNoFingersDownState()); - const ScopedVector<ui::LocatedEvent>& captured_events = GetCapturedEvents(); + std::vector<ui::LocatedEvent*> captured_events = GetCapturedLocatedEvents(); ASSERT_EQ(2U, captured_events.size()); EXPECT_EQ(ui::ET_TOUCH_PRESSED, captured_events[0]->type()); EXPECT_EQ(initial_touch_location, captured_events[0]->location()); @@ -606,7 +688,7 @@ TEST_F(TouchExplorationTest, SplitTapRelease) { EnterTouchExplorationModeAtLocation(initial_touch_location); std::vector<ui::LocatedEvent*> events = - GetCapturedEventsOfType(ui::ET_MOUSE_MOVED); + GetCapturedLocatedEventsOfType(ui::ET_MOUSE_MOVED); ASSERT_EQ(1U, events.size()); ClearCapturedEvents(); @@ -626,7 +708,7 @@ TEST_F(TouchExplorationTest, SplitTapRelease) { generator_->Dispatch(&split_tap_release); EXPECT_TRUE(IsInNoFingersDownState()); - const ScopedVector<ui::LocatedEvent>& captured_events = GetCapturedEvents(); + std::vector<ui::LocatedEvent*> captured_events = GetCapturedLocatedEvents(); ASSERT_EQ(2U, captured_events.size()); EXPECT_EQ(ui::ET_TOUCH_PRESSED, captured_events[0]->type()); EXPECT_EQ(initial_touch_location, captured_events[0]->location()); @@ -646,7 +728,7 @@ TEST_F(TouchExplorationTest, SplitTapLongPress) { // Tap and hold at one location, and get a mouse move event in touch explore. EnterTouchExplorationModeAtLocation(initial_touch_location); std::vector<ui::LocatedEvent*> events = - GetCapturedEventsOfType(ui::ET_MOUSE_MOVED); + GetCapturedLocatedEventsOfType(ui::ET_MOUSE_MOVED); ASSERT_EQ(1U, events.size()); ClearCapturedEvents(); @@ -664,7 +746,7 @@ TEST_F(TouchExplorationTest, SplitTapLongPress) { generator_->Dispatch(&split_tap_release); EXPECT_FALSE(IsInNoFingersDownState()); - const ScopedVector<ui::LocatedEvent>& captured_events = GetCapturedEvents(); + std::vector<ui::LocatedEvent*> captured_events = GetCapturedLocatedEvents(); ASSERT_EQ(2U, captured_events.size()); EXPECT_EQ(ui::ET_TOUCH_PRESSED, captured_events[0]->type()); EXPECT_EQ(initial_touch_location, captured_events[0]->location()); @@ -690,7 +772,7 @@ TEST_F(TouchExplorationTest, SplitTapReleaseLongPress) { // Tap and hold at one location, and get a mouse move event in touch explore. EnterTouchExplorationModeAtLocation(initial_touch_location); std::vector<ui::LocatedEvent*> events = - GetCapturedEventsOfType(ui::ET_MOUSE_MOVED); + GetCapturedLocatedEventsOfType(ui::ET_MOUSE_MOVED); ASSERT_EQ(1U, events.size()); ClearCapturedEvents(); @@ -711,7 +793,7 @@ TEST_F(TouchExplorationTest, SplitTapReleaseLongPress) { generator_->Dispatch(&split_tap_release); EXPECT_TRUE(IsInTouchToMouseMode()); - const ScopedVector<ui::LocatedEvent>& captured_events = GetCapturedEvents(); + std::vector<ui::LocatedEvent*> captured_events = GetCapturedLocatedEvents(); ASSERT_EQ(2U, captured_events.size()); EXPECT_EQ(ui::ET_TOUCH_PRESSED, captured_events[0]->type()); EXPECT_EQ(initial_touch_location, captured_events[0]->location()); @@ -733,7 +815,7 @@ TEST_F(TouchExplorationTest, SplitTapLongPressMultiFinger) { EnterTouchExplorationModeAtLocation(initial_touch_location); std::vector<ui::LocatedEvent*> events = - GetCapturedEventsOfType(ui::ET_MOUSE_MOVED); + GetCapturedLocatedEventsOfType(ui::ET_MOUSE_MOVED); ASSERT_EQ(1U, events.size()); EXPECT_EQ(initial_touch_location, events[0]->location()); @@ -765,7 +847,7 @@ TEST_F(TouchExplorationTest, SplitTapLongPressMultiFinger) { ui::ET_TOUCH_RELEASED, third_touch_location, 2, Now()); generator_->Dispatch(&third_tap_release); - const ScopedVector<ui::LocatedEvent>& captured_events = GetCapturedEvents(); + std::vector<ui::LocatedEvent*> captured_events = GetCapturedLocatedEvents(); ASSERT_EQ(2U, captured_events.size()); EXPECT_EQ(ui::ET_TOUCH_PRESSED, captured_events[0]->type()); EXPECT_EQ(initial_touch_location, captured_events[0]->location()); @@ -785,7 +867,7 @@ TEST_F(TouchExplorationTest, TwoToOneFingerReleaseSecond) { gfx::Point first_touch_location = gfx::Point(7, 7); gfx::Point second_touch_location = gfx::Point(10, 11); EnterTwoToOne(first_touch_location, second_touch_location); - const ScopedVector<ui::LocatedEvent>& captured_events = GetCapturedEvents(); + std::vector<ui::LocatedEvent*> captured_events = GetCapturedLocatedEvents(); ASSERT_EQ(captured_events.size(), 1u); ClearCapturedEvents(); @@ -797,6 +879,7 @@ TEST_F(TouchExplorationTest, TwoToOneFingerReleaseSecond) { 1, Now()); generator_->Dispatch(&second_touch_move); + captured_events = GetCapturedLocatedEvents(); ASSERT_EQ(1u, captured_events.size()); ClearCapturedEvents(); @@ -804,7 +887,8 @@ TEST_F(TouchExplorationTest, TwoToOneFingerReleaseSecond) { // finger is touching. gfx::Point first_touch_move_location = gfx::Point(15, 16); generator_->MoveTouchId(first_touch_move_location, 0); - EXPECT_EQ(0u, GetCapturedEvents().size()); + captured_events = GetCapturedLocatedEvents(); + EXPECT_EQ(0u, captured_events.size()); EXPECT_TRUE(cursor_client()->IsCursorVisible()); EXPECT_FALSE(cursor_client()->IsMouseEventsEnabled()); @@ -814,6 +898,7 @@ TEST_F(TouchExplorationTest, TwoToOneFingerReleaseSecond) { ui::ET_TOUCH_RELEASED, second_touch_move_location, 1, Now()); generator_->Dispatch(&second_touch_release); EXPECT_FALSE(IsInTouchToMouseMode()); + captured_events = GetCapturedLocatedEvents(); ASSERT_EQ(captured_events.size(), 1u); ClearCapturedEvents(); @@ -825,7 +910,7 @@ TEST_F(TouchExplorationTest, TwoToOneFingerReleaseSecond) { ui::TouchEvent first_touch_release( ui::ET_TOUCH_RELEASED, first_touch_move_location, 0, Now()); generator_->Dispatch(&first_touch_release); - + captured_events = GetCapturedLocatedEvents(); ASSERT_EQ(captured_events.size(), 0u); EXPECT_TRUE(IsInNoFingersDownState()); } @@ -836,7 +921,7 @@ TEST_F(TouchExplorationTest, TwoToOneFingerRelaseFirst) { gfx::Point first_touch_location = gfx::Point(11,12); gfx::Point second_touch_location = gfx::Point(21, 22); EnterTwoToOne(first_touch_location, second_touch_location); - const ScopedVector<ui::LocatedEvent>& captured_events = GetCapturedEvents(); + std::vector<ui::LocatedEvent*> captured_events = GetCapturedLocatedEvents(); ASSERT_EQ(captured_events.size(), 1u); ClearCapturedEvents(); @@ -847,6 +932,7 @@ TEST_F(TouchExplorationTest, TwoToOneFingerRelaseFirst) { ui::TouchEvent first_touch_release( ui::ET_TOUCH_RELEASED, first_touch_location, 0, Now()); generator_->Dispatch(&first_touch_release); + captured_events = GetCapturedLocatedEvents(); ASSERT_EQ(captured_events.size(), 1u); ClearCapturedEvents(); @@ -858,7 +944,7 @@ TEST_F(TouchExplorationTest, TwoToOneFingerRelaseFirst) { ui::TouchEvent second_touch_release( ui::ET_TOUCH_RELEASED, second_touch_location, 1, Now()); generator_->Dispatch(&second_touch_release); - + captured_events = GetCapturedLocatedEvents(); ASSERT_EQ(captured_events.size(), 0u); EXPECT_TRUE(IsInNoFingersDownState()); } @@ -866,17 +952,19 @@ TEST_F(TouchExplorationTest, TwoToOneFingerRelaseFirst) { // Placing three fingers should start passthrough, and all fingers should // continue to be passed through until the last one is released. TEST_F(TouchExplorationTest, Passthrough) { - const ScopedVector<ui::LocatedEvent>& captured_events = GetCapturedEvents(); + std::vector<ui::LocatedEvent*> captured_events = GetCapturedLocatedEvents(); gfx::Point first_touch_location = gfx::Point(11,12); gfx::Point second_touch_location = gfx::Point(21, 22); EnterTwoToOne(first_touch_location, second_touch_location); + captured_events = GetCapturedLocatedEvents(); ASSERT_EQ(captured_events.size(), 1u); gfx::Point third_touch_location = gfx::Point(31, 32); ui::TouchEvent third_touch_press( ui::ET_TOUCH_PRESSED, third_touch_location, 2, Now()); generator_->Dispatch(&third_touch_press); + captured_events = GetCapturedLocatedEvents(); // Now all fingers are registered as pressed. ASSERT_EQ(captured_events.size(), 3u); ClearCapturedEvents(); @@ -894,6 +982,7 @@ TEST_F(TouchExplorationTest, Passthrough) { generator_->Dispatch(&first_touch_first_move); generator_->Dispatch(&second_touch_first_move); generator_->Dispatch(&third_touch_first_move); + captured_events = GetCapturedLocatedEvents(); ASSERT_EQ(captured_events.size(), 3u); EXPECT_EQ(ui::ET_TOUCH_MOVED, captured_events[0]->type()); EXPECT_EQ(first_touch_location, captured_events[0]->location()); @@ -908,6 +997,7 @@ TEST_F(TouchExplorationTest, Passthrough) { ui::TouchEvent third_touch_release( ui::ET_TOUCH_RELEASED, third_touch_location, 2, Now()); generator_->Dispatch(&third_touch_release); + captured_events = GetCapturedLocatedEvents(); ASSERT_EQ(captured_events.size(), 1u); ClearCapturedEvents(); first_touch_location = gfx::Point(15, 16); @@ -918,6 +1008,7 @@ TEST_F(TouchExplorationTest, Passthrough) { ui::ET_TOUCH_MOVED, second_touch_location, 1, Now()); generator_->Dispatch(&first_touch_second_move); generator_->Dispatch(&second_touch_second_move); + captured_events = GetCapturedLocatedEvents(); ASSERT_EQ(captured_events.size(), 2u); EXPECT_EQ(ui::ET_TOUCH_MOVED, captured_events[0]->type()); EXPECT_EQ(first_touch_location, captured_events[0]->location()); @@ -930,12 +1021,14 @@ TEST_F(TouchExplorationTest, Passthrough) { ui::TouchEvent second_touch_release( ui::ET_TOUCH_RELEASED, second_touch_location, 1, Now()); generator_->Dispatch(&second_touch_release); + captured_events = GetCapturedLocatedEvents(); ASSERT_EQ(captured_events.size(), 1u); ClearCapturedEvents(); first_touch_location = gfx::Point(17, 18); ui::TouchEvent first_touch_third_move( ui::ET_TOUCH_MOVED, first_touch_location, 0, Now()); generator_->Dispatch(&first_touch_third_move); + captured_events = GetCapturedLocatedEvents(); ASSERT_EQ(captured_events.size(), 1u); EXPECT_EQ(ui::ET_TOUCH_MOVED, captured_events[0]->type()); EXPECT_EQ(first_touch_location, captured_events[0]->location()); @@ -944,8 +1037,145 @@ TEST_F(TouchExplorationTest, Passthrough) { ui::TouchEvent first_touch_release( ui::ET_TOUCH_RELEASED, first_touch_location, 0, Now()); generator_->Dispatch(&first_touch_release); + captured_events = GetCapturedLocatedEvents(); ASSERT_EQ(captured_events.size(), 1u); EXPECT_TRUE(IsInNoFingersDownState()); } +// Finger must have moved more than slop, faster than the minimum swipe +// velocity, and before the tap timer fires in order to enter +// GestureInProgress state. Otherwise, if the tap timer fires before the a +// gesture is completed, enter touch exploration. +TEST_F(TouchExplorationTest, EnterGestureInProgressState) { + SwitchTouchExplorationMode(true); + EXPECT_FALSE(IsInTouchToMouseMode()); + EXPECT_FALSE(IsInGestureInProgressState()); + + float distance = gesture_detector_config_.touch_slop + 1; + ui::TouchEvent first_press(ui::ET_TOUCH_PRESSED, gfx::Point(0, 1), 0, Now()); + gfx::Point second_location(distance / 2, 1); + gfx::Point third_location(distance, 1); + + generator_->Dispatch(&first_press); + simulated_clock_->Advance(base::TimeDelta::FromMilliseconds(10)); + // Since we are not out of the touch slop yet, we should not be in gesture in + // progress. + generator_->MoveTouch(second_location); + EXPECT_FALSE(IsInTouchToMouseMode()); + EXPECT_FALSE(IsInGestureInProgressState()); + simulated_clock_->Advance(base::TimeDelta::FromMilliseconds(10)); + + // Once we are out of slop, we should be in GestureInProgress. + generator_->MoveTouch(third_location); + EXPECT_TRUE(IsInGestureInProgressState()); + EXPECT_FALSE(IsInTouchToMouseMode()); + const ScopedVector<ui::Event>& captured_events = GetCapturedEvents(); + ASSERT_EQ(0U, captured_events.size()); + + // Exit out of gesture mode once grace period is over and enter touch + // exploration. + AdvanceSimulatedTimePastTapDelay(); + ASSERT_EQ(1U, captured_events.size()); + EXPECT_EQ(ui::ET_MOUSE_MOVED, captured_events[0]->type()); + EXPECT_TRUE(IsInTouchToMouseMode()); + EXPECT_FALSE(IsInGestureInProgressState()); +} + +// A swipe+direction gesture should trigger a Shift+Search+Direction +// keyboard event. +TEST_F(TouchExplorationTest, GestureSwipe) { + SwitchTouchExplorationMode(true); + std::vector<ui::KeyboardCode> directions; + directions.push_back(ui::VKEY_RIGHT); + directions.push_back(ui::VKEY_LEFT); + directions.push_back(ui::VKEY_UP); + directions.push_back(ui::VKEY_DOWN); + + for (std::vector<ui::KeyboardCode>::const_iterator it = directions.begin(); + it != directions.end(); + ++it) { + int x = 30; + int y = 31; + ui::TouchEvent origin(ui::ET_TOUCH_PRESSED, gfx::Point(x, y), 0, Now()); + generator_->Dispatch(&origin); + + ui::KeyboardCode direction = *it; + float distance = gesture_detector_config_.touch_slop + 1; + scoped_ptr<gfx::Point> swipe; + switch (direction) { + case ui::VKEY_RIGHT: + swipe.reset(new gfx::Point(x + distance, y)); + break; + case ui::VKEY_LEFT: + swipe.reset(new gfx::Point(x - distance, y)); + break; + case ui::VKEY_UP: + swipe.reset(new gfx::Point(x, y - distance)); + break; + case ui::VKEY_DOWN: + swipe.reset(new gfx::Point(x, y + distance)); + break; + default: + return; + } + + // A swipe is made when a fling starts + float delta_time = + distance / gesture_detector_config_.maximum_fling_velocity; + simulated_clock_->Advance(base::TimeDelta::FromSecondsD(delta_time)); + generator_->MoveTouch(*swipe); + EXPECT_TRUE(IsInGestureInProgressState()); + EXPECT_FALSE(IsInTouchToMouseMode()); + const ScopedVector<ui::Event>& captured_events = GetCapturedEvents(); + ASSERT_EQ(0U, captured_events.size()); + generator_->ReleaseTouch(); + + // The swipe registered and sent the appropriate key events. + AssertDirectionalNavigationEvents(captured_events, direction); + EXPECT_TRUE(IsInNoFingersDownState()); + EXPECT_FALSE(IsInTouchToMouseMode()); + EXPECT_FALSE(IsInGestureInProgressState()); + ClearCapturedEvents(); + } +} + +// With the simple swipe gestures, if additional fingers are added, then the +// state should change to passthrough. +TEST_F(TouchExplorationTest, FromGestureToPassthrough) { + SwitchTouchExplorationMode(true); + EXPECT_FALSE(IsInTouchToMouseMode()); + EXPECT_FALSE(IsInGestureInProgressState()); + + float distance = gesture_detector_config_.touch_slop + 1; + ui::TouchEvent first_press(ui::ET_TOUCH_PRESSED, gfx::Point(0, 1), 0, Now()); + generator_->Dispatch(&first_press); + simulated_clock_->Advance(base::TimeDelta::FromMilliseconds(10)); + gfx::Point second_location(distance, 1); + generator_->MoveTouch(second_location); + EXPECT_TRUE(IsInGestureInProgressState()); + EXPECT_FALSE(IsInTouchToMouseMode()); + const ScopedVector<ui::Event>& captured_events = GetCapturedEvents(); + ASSERT_EQ(0U, captured_events.size()); + + // Generate a second press that should go through as is. + ui::TouchEvent second_press( + ui::ET_TOUCH_PRESSED, gfx::Point(20, 21), 1, Now()); + generator_->Dispatch(&second_press); + EXPECT_FALSE(IsInGestureInProgressState()); + EXPECT_FALSE(IsInTouchToMouseMode()); + std::vector<ui::LocatedEvent*> captured_located_events = + GetCapturedLocatedEvents(); + ASSERT_EQ(1U, captured_events.size()); + CONFIRM_EVENTS_ARE_TOUCH_AND_EQUAL(captured_located_events[0], &second_press); + ClearCapturedEvents(); + + // The rest of the events should occur in passthrough. + generator_->ReleaseTouchId(0); + ASSERT_EQ(1U, captured_events.size()); + EXPECT_EQ(ui::ET_TOUCH_RELEASED, captured_events[0]->type()); + ClearCapturedEvents(); + generator_->ReleaseTouchId(1); + ASSERT_EQ(0U, captured_events.size()); +} + } // namespace ui |