Chromium Code Reviews| Index: ui/chromeos/touch_exploration_controller.cc |
| diff --git a/ui/chromeos/touch_exploration_controller.cc b/ui/chromeos/touch_exploration_controller.cc |
| index 1a77b971aa9b0c7ad59f56d2004267541b4592dc..6670218404a30d7d0904ad0b6c0979855d5b4b68 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 { |
|
aboxhall
2014/07/02 16:45:57
suggestion: should we just have a GetStateForTesti
lisayin
2014/07/02 17:09:50
The state enum is private so I don't think that we
aboxhall
2014/07/02 18:00:13
Ahh, that makes sense, thanks.
|
| + 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,8 @@ ui::EventRewriteStatus TouchExplorationController::InNoFingersDown( |
| gesture_detector_config_.double_tap_timeout, |
| this, |
| &TouchExplorationController::OnTapTimerFired); |
| + gesture_provider_.OnTouchEvent(event); |
| + gesture_provider_.OnTouchEventAck(false); |
| state_ = SINGLE_TAP_PRESSED; |
| VLOG_STATE(); |
| return ui::EVENT_REWRITE_DISCARD; |
| @@ -182,19 +193,32 @@ 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 delta_time = |
| + (event.time_stamp() - initial_press_->time_stamp()).InSecondsF(); |
| + float delta_distance = |
|
aboxhall
2014/07/02 16:45:57
nit: I think this should just be 'distance'
lisayin
2014/07/02 17:19:43
Done.
|
| + (event.location() - initial_press_->location()).Length(); |
| + float velocity = delta_distance / delta_time; |
| + VLOG(0) << "\n Delta time: " << delta_time |
| + << "\n Delta distance: " << delta_distance |
| + << "\n Velocity of click: " << velocity |
| + << "\n Minimum swipe velocity: " |
| + << gesture_detector_config_.minimum_swipe_velocity; |
| + if (delta_distance <= gesture_detector_config_.touch_slop) |
|
aboxhall
2014/07/02 16:45:57
Presumably we don't need to compute delta_time and
lisayin
2014/07/02 17:19:43
Done.
|
| + return EVENT_REWRITE_DISCARD; |
| + |
| + // If the user moves fast enough and far enough |
|
aboxhall
2014/07/02 16:45:57
Re-flow this comment (I think tab should do it)
A
lisayin
2014/07/02 17:19:43
Done.
|
| + // from the initial touch location, start gesture detection. |
| + // Otherwise, if the user moves far enough from the initial touch location |
| + // outside the "slop" region, 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 +229,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 (should be replaced with wait_state). |
| + 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_ == NULL || 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 +255,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 +270,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 +323,43 @@ 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 (type == ui::ET_TOUCH_PRESSED || |
| + event.touch_id() != initial_press_->touch_id()) { |
| + // Should go to passthrough mode. |
| + if (tap_timer_.IsRunning()) |
| + tap_timer_.Stop(); |
| + // Discard any pending gestures. |
| + scoped_ptr<ScopedVector<ui::GestureEvent> > gestures( |
|
aboxhall
2014/07/02 16:45:58
Since you're not using |gestures|, you can use ign
lisayin
2014/07/02 17:09:50
Will I still need to cast the return from GetAndRe
aboxhall
2014/07/02 18:00:13
I see you figured this out :)
|
| + 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; |
| + } |
| + /* |
| + if (last_touch_exploration_ == NULL) { |
|
aboxhall
2014/07/02 16:45:57
Should this be uncommented or removed?
lisayin
2014/07/02 17:19:44
Done.
|
| + last_touch_exploration_.reset(initial_press_.get()); |
| + }*/ |
| + 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(); |
| + } |
| + return ui::EVENT_REWRITE_DISCARD; |
| +} |
| ui::EventRewriteStatus TouchExplorationController::InTwoToOneFinger( |
| const ui::TouchEvent& event, |
| @@ -414,6 +478,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 +498,7 @@ ui::EventRewriteStatus TouchExplorationController::InWaitForRelease( |
| } |
| if (current_touch_ids_.size() == 0) { |
| state_ = NO_FINGERS_DOWN; |
| + VLOG_STATE(); |
| ResetToNoFingersDown(); |
| } |
| return EVENT_REWRITE_DISCARD; |
| @@ -448,19 +514,22 @@ void TouchExplorationController::OnTapTimerFired() { |
| last_touch_exploration_.reset(new TouchEvent(*initial_press_)); |
| return; |
| case SINGLE_TAP_PRESSED: |
| + case GESTURE_IN_PROGRESS: { |
| + // Discard any pending gestures. |
| + scoped_ptr<ScopedVector<ui::GestureEvent> > gestures( |
|
aboxhall
2014/07/02 16:45:57
Similarly, you can use ignore_result() here.
lisayin
2014/07/02 17:19:43
Done.
|
| + gesture_provider_.GetAndResetPendingGestures()); |
| EnterTouchToMouseMode(); |
| state_ = TOUCH_EXPLORATION; |
| VLOG_STATE(); |
| - break; |
| + } break; |
| 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 +540,66 @@ void TouchExplorationController::DispatchEvent(ui::Event* event) { |
| root_window_->GetHost()->dispatcher()->OnEventFromSource(event); |
| } |
| +void TouchExplorationController::OnGestureEvent(ui::GestureEvent* gesture) { |
| + if (tap_timer_.IsRunning()) |
| + tap_timer_.Stop(); |
| + |
| + CHECK(gesture->IsGestureEvent()); |
| + VLOG(0) << " \n Gesture Triggered: " << gesture->name(); |
| + |
| + if (gesture->type() == ui::ET_GESTURE_SWIPE) { |
| + OnSwipeEvent(gesture); |
| + return; |
| + } |
| + // If the event processed was not a swipe gesture, then determine the next |
| + // state by the number of fingers currently down. |
| + if (current_touch_ids_.size() == 0) { |
| + ResetToNoFingersDown(); |
| + } else if (current_touch_ids_.size() == 1) { |
| + EnterTouchToMouseMode(); |
| + state_ = TOUCH_EXPLORATION; |
| + } |
| +} |
| + |
| +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. |
| + DispatchEvent(new ui::KeyEvent( |
| + ui::ET_KEY_PRESSED, ui::VKEY_SHIFT, ui::EF_SHIFT_DOWN, false)); |
| + DispatchEvent(new ui::KeyEvent( |
| + ui::ET_KEY_PRESSED, kChromeOSSearchKey, ui::EF_SHIFT_DOWN, false)); |
| + DispatchEvent(new ui::KeyEvent( |
| + ui::ET_KEY_PRESSED, direction, ui::EF_SHIFT_DOWN, false)); |
| + |
| + DispatchEvent(new ui::KeyEvent( |
| + ui::ET_KEY_RELEASED, direction, ui::EF_SHIFT_DOWN, false)); |
| + DispatchEvent(new ui::KeyEvent( |
| + ui::ET_KEY_RELEASED, kChromeOSSearchKey, ui::EF_SHIFT_DOWN, false)); |
| + DispatchEvent(new ui::KeyEvent( |
| + ui::ET_KEY_RELEASED, ui::VKEY_SHIFT, ui::EF_NONE, false)); |
| +} |
| + |
| scoped_ptr<ui::Event> TouchExplorationController::CreateMouseMoveEvent( |
| const gfx::PointF& location, |
| int flags) { |
| @@ -493,6 +622,16 @@ void TouchExplorationController::EnterTouchToMouseMode() { |
| } |
| void TouchExplorationController::ResetToNoFingersDown() { |
| + // Process the gestures. |
| + scoped_ptr<ScopedVector<ui::GestureEvent> > gestures; |
|
aboxhall
2014/07/02 16:45:57
You can initialise gestures with GetAndResetPendin
lisayin
2014/07/02 17:19:43
Done.
|
| + gestures.reset(gesture_provider_.GetAndResetPendingGestures()); |
| + if (gestures != NULL) { |
|
aboxhall
2014/07/02 16:45:57
if (gestures)
should work I think...
lisayin
2014/07/02 17:19:44
Done.
|
| + for (ScopedVector<GestureEvent>::iterator i = gestures->begin(); |
| + i != gestures->end(); |
| + ++i) { |
| + OnGestureEvent(*i); |
| + } |
| + } |
| state_ = NO_FINGERS_DOWN; |
| VLOG_STATE(); |
| if (tap_timer_.IsRunning()) |
| @@ -549,6 +688,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: |