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 3670f1e948f213908d1884d9f844eacf35916a5c..4b31d4c56bc87843274c8aff8e7bd250a44f2311 100644 |
| --- a/ui/chromeos/touch_exploration_controller.cc |
| +++ b/ui/chromeos/touch_exploration_controller.cc |
| @@ -25,6 +25,10 @@ namespace { |
| // Delay between adjustment sounds. |
| const base::TimeDelta kSoundDelay = base::TimeDelta::FromMilliseconds(150); |
| +// Delay before corner passthrough activates. |
| +const base::TimeDelta kCornerPassthroughDelay = |
| + base::TimeDelta::FromMilliseconds(700); |
| + |
| // In ChromeOS, VKEY_LWIN is synonymous for the search key. |
| const ui::KeyboardCode kChromeOSSearchKey = ui::VKEY_LWIN; |
| } // namespace |
| @@ -68,6 +72,13 @@ ui::EventRewriteStatus TouchExplorationController::RewriteEvent( |
| // this event under this new state. |
| } |
| + if (passthrough_timer_.IsRunning() && |
| + event.time_stamp() - initial_press_->time_stamp() > |
| + gesture_detector_config_.longpress_timeout) { |
| + passthrough_timer_.Stop(); |
| + OnPassthroughTimerFired(); |
| + } |
| + |
| const ui::EventType type = touch_event.type(); |
| const gfx::PointF& location = touch_event.location_f(); |
| const int touch_id = touch_event.touch_id(); |
| @@ -81,11 +92,14 @@ ui::EventRewriteStatus TouchExplorationController::RewriteEvent( |
| // In order to avoid accidentally double tapping when moving off the edge of |
| // the screen, the state will be rewritten to NoFingersDown. |
| TouchEvent touch_event = static_cast<const TouchEvent&>(event); |
| - if (FindEdgesWithinBounds(touch_event.location(), kLeavingScreenEdge) != |
| - NO_EDGE) { |
| - if (current_touch_ids_.size() == 0) { |
| - ResetToNoFingersDown(); |
| - } |
| + gfx::Point edges = touch_event.location(); |
| + if (FindEdgesWithinBounds(edges, kLeavingScreenEdge) != NO_EDGE) { |
| + // Indicates to the user that they are leaving the screen. |
| + if (VLOG_on_) |
| + VLOG(0) << "Leaving the Screen"; |
| + delegate_->PlayExitScreenEarcon(); |
| + if (current_touch_ids_.size() == 0) |
| + ResetToNoFingersDown(); |
| } |
| std::vector<int>::iterator it = std::find( |
| @@ -128,6 +142,8 @@ ui::EventRewriteStatus TouchExplorationController::RewriteEvent( |
| return InGestureInProgress(touch_event, rewritten_event); |
| case TOUCH_EXPLORE_SECOND_PRESS: |
| return InTouchExploreSecondPress(touch_event, rewritten_event); |
| + case CORNER_PASSTHROUGH: |
| + return InCornerPassthrough(touch_event, rewritten_event); |
| case SLIDE_GESTURE: |
| return InSlideGesture(touch_event, rewritten_event); |
| case ONE_FINGER_PASSTHROUGH: |
| @@ -148,25 +164,65 @@ ui::EventRewriteStatus TouchExplorationController::NextDispatchEvent( |
| ui::EventRewriteStatus TouchExplorationController::InNoFingersDown( |
| const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event) { |
| const ui::EventType type = event.type(); |
| - if (type == ui::ET_TOUCH_PRESSED) { |
| - initial_press_.reset(new TouchEvent(event)); |
| - last_unused_finger_event_.reset(new TouchEvent(event)); |
| - StartTapTimer(); |
| - gesture_provider_.OnTouchEvent(event); |
| - gesture_provider_.OnTouchEventAck(false); |
| - ProcessGestureEvents(); |
| - state_ = SINGLE_TAP_PRESSED; |
| - VLOG_STATE(); |
| - return ui::EVENT_REWRITE_DISCARD; |
| + if (type != ui::ET_TOUCH_PRESSED) { |
| + NOTREACHED() << "Unexpected event type received: " << event.name(); |
| + return ui::EVENT_REWRITE_CONTINUE; |
| } |
| - NOTREACHED() << "Unexpected event type received: " << event.name(); |
| - return ui::EVENT_REWRITE_CONTINUE; |
| + |
| + // If the user enters the screen from the edge then send an earcon. |
| + int edge = FindEdgesWithinBounds(event.location(), kLeavingScreenEdge); |
| + if (edge != NO_EDGE) |
| + delegate_->PlayEnterScreenEarcon(); |
| + |
| + int location = FindEdgesWithinBounds(event.location(), kSlopDistanceFromEdge); |
| + // If the press was at a corner, the user might go into corner passthrough |
| + // instead. |
| + bool in_a_bottom_corner = |
| + (BOTTOM_LEFT_CORNER == location) || (BOTTOM_RIGHT_CORNER == location); |
| + if (in_a_bottom_corner) { |
| + passthrough_timer_.Start( |
| + FROM_HERE, |
| + gesture_detector_config_.longpress_timeout, |
| + this, |
| + &TouchExplorationController::OnPassthroughTimerFired); |
| + } |
| + |
| + tap_timer_.Start(FROM_HERE, |
| + gesture_detector_config_.double_tap_timeout, |
| + this, |
| + &TouchExplorationController::OnTapTimerFired); |
| + initial_press_.reset(new TouchEvent(event)); |
| + last_unused_finger_event_.reset(new TouchEvent(event)); |
| + gesture_provider_.OnTouchEvent(event); |
| + gesture_provider_.OnTouchEventAck(false); |
| + ProcessGestureEvents(); |
| + state_ = SINGLE_TAP_PRESSED; |
| + VLOG_STATE(); |
| + return ui::EVENT_REWRITE_DISCARD; |
| } |
| ui::EventRewriteStatus TouchExplorationController::InSingleTapPressed( |
| const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event) { |
| const ui::EventType type = event.type(); |
| + int location = FindEdgesWithinBounds(event.location(), kMaxDistanceFromEdge); |
| + bool in_a_bottom_corner = |
| + (location == BOTTOM_LEFT_CORNER) || (location == BOTTOM_RIGHT_CORNER); |
| + // If the event is from the initial press and the location is no longer in the |
| + // corner, then we are not waiting for a corner passthrough anymore. |
| + if (event.touch_id() == initial_press_->touch_id() && !in_a_bottom_corner) { |
| + if (passthrough_timer_.IsRunning()) { |
| + passthrough_timer_.Stop(); |
| + // Since the long press timer has been running, it is possible that the |
| + // tap timer has timed out before the long press timer has. If the tap |
| + // timer timeout has elapsed, then fire the tap timer. |
| + if (event.time_stamp() - initial_press_->time_stamp() > |
| + gesture_detector_config_.double_tap_timeout) { |
| + OnTapTimerFired(); |
| + } |
| + } |
| + } |
| + |
| if (type == ui::ET_TOUCH_PRESSED) { |
| // TODO (evy, lisayin) : add support for multifinger swipes. |
| // For now, we wait for there to be only one finger down again. |
| @@ -200,7 +256,7 @@ ui::EventRewriteStatus TouchExplorationController::InSingleTapPressed( |
| } |
| // Change to slide gesture if the slide occurred at the right edge. |
| int edge = FindEdgesWithinBounds(event.location(), kMaxDistanceFromEdge); |
| - if (edge & RIGHT_EDGE) { |
| + if (edge & RIGHT_EDGE && edge != BOTTOM_RIGHT_CORNER) { |
| state_ = SLIDE_GESTURE; |
| VLOG_STATE(); |
| return InSlideGesture(event, rewritten_event); |
| @@ -393,6 +449,39 @@ ui::EventRewriteStatus TouchExplorationController::InGestureInProgress( |
| return ui::EVENT_REWRITE_DISCARD; |
| } |
| +ui::EventRewriteStatus TouchExplorationController::InCornerPassthrough( |
| + const ui::TouchEvent& event, |
| + scoped_ptr<ui::Event>* rewritten_event) { |
| + ui::EventType type = event.type(); |
| + |
| + // If the first finger has left the corner, then exit passthrough. |
| + if (event.touch_id() == initial_press_->touch_id()) { |
| + int edges = FindEdgesWithinBounds(event.location(), kSlopDistanceFromEdge); |
| + bool in_a_bottom_corner = (edges == BOTTOM_LEFT_CORNER) || |
| + (edges == BOTTOM_RIGHT_CORNER); |
| + if (type == ui::ET_TOUCH_MOVED && in_a_bottom_corner) |
| + return ui::EVENT_REWRITE_DISCARD; |
| + |
| + if (current_touch_ids_.size() == 0) { |
| + ResetToNoFingersDown(); |
| + return ui::EVENT_REWRITE_DISCARD; |
| + } |
| + |
| + state_ = WAIT_FOR_ONE_FINGER; |
| + VLOG_STATE(); |
| + return ui::EVENT_REWRITE_DISCARD; |
| + } |
| + |
| + rewritten_event->reset(new ui::TouchEvent( |
| + type, event.location(), event.touch_id(), event.time_stamp())); |
| + (*rewritten_event)->set_flags(event.flags()); |
| + |
| + if (current_touch_ids_.size() == 0) |
| + ResetToNoFingersDown(); |
| + |
| + return ui::EVENT_REWRITE_REWRITTEN; |
| +} |
| + |
| ui::EventRewriteStatus TouchExplorationController::InOneFingerPassthrough( |
| const ui::TouchEvent& event, |
| scoped_ptr<ui::Event>* rewritten_event) { |
| @@ -486,7 +575,7 @@ ui::EventRewriteStatus TouchExplorationController::InWaitForOneFinger( |
| } |
| void TouchExplorationController::PlaySoundForTimer() { |
| - delegate_->PlayVolumeAdjustSound(); |
| + delegate_->PlayVolumeAdjustEarcon(); |
| } |
| ui::EventRewriteStatus TouchExplorationController::InSlideGesture( |
| @@ -526,7 +615,7 @@ ui::EventRewriteStatus TouchExplorationController::InSlideGesture( |
| kSoundDelay, |
| this, |
| &ui::TouchExplorationController::PlaySoundForTimer); |
| - delegate_->PlayVolumeAdjustSound(); |
| + delegate_->PlayVolumeAdjustEarcon(); |
| } |
| // There should not be more than one finger down. |
| @@ -589,6 +678,8 @@ void TouchExplorationController::OnTapTimerFired() { |
| return; |
| } |
| case SINGLE_TAP_PRESSED: |
| + if (passthrough_timer_.IsRunning()) |
| + return; |
| case GESTURE_IN_PROGRESS: |
| // Discard any pending gestures. |
| delete gesture_provider_.GetAndResetPendingGestures(); |
| @@ -605,6 +696,32 @@ void TouchExplorationController::OnTapTimerFired() { |
| last_touch_exploration_.reset(new TouchEvent(*initial_press_)); |
| } |
| +void TouchExplorationController::OnPassthroughTimerFired() { |
| + // The long press timer should only be running if one finger is in the corner |
|
aboxhall
2014/08/06 21:03:12
I see why this comment was added, but I think it's
lisayin
2014/08/06 21:48:23
Done.
|
| + // ready for corner passthrough |
| + |
| + // Check that initial press isn't null and that the id of the initial press |
| + // still exists in touch_locations_. |
| + if (!initial_press_ || |
| + touch_locations_.end() == |
| + touch_locations_.find(initial_press_->touch_id())) |
| + return; |
|
aboxhall
2014/08/06 21:03:12
Is this an error/shouldn't happen condition? Also,
lisayin
2014/08/06 21:48:22
Done.
|
| + |
| + gfx::Point location = |
| + ToRoundedPoint(touch_locations_[initial_press_->touch_id()]); |
| + int corner = FindEdgesWithinBounds(location, kSlopDistanceFromEdge); |
| + if (corner != BOTTOM_LEFT_CORNER && corner != BOTTOM_RIGHT_CORNER) |
| + return; |
| + |
| + if (sound_timer_.IsRunning()) |
| + sound_timer_.Stop(); |
| + delegate_->PlayPassthroughEarcon(); |
| + delete gesture_provider_.GetAndResetPendingGestures(); |
| + state_ = CORNER_PASSTHROUGH; |
| + VLOG_STATE(); |
| + return; |
| +} |
| + |
| void TouchExplorationController::DispatchEvent(ui::Event* event) { |
| ui::EventDispatchDetails result ALLOW_UNUSED = |
| root_window_->GetHost()->dispatcher()->OnEventFromSource(event); |
| @@ -646,13 +763,13 @@ void TouchExplorationController::SideSlideControl(ui::GestureEvent* gesture) { |
| return; |
| if (type == ET_GESTURE_SCROLL_BEGIN) { |
| - delegate_->PlayVolumeAdjustSound(); |
| + delegate_->PlayVolumeAdjustEarcon(); |
| } |
| if (type == ET_GESTURE_SCROLL_END) { |
| if (sound_timer_.IsRunning()) |
| sound_timer_.Stop(); |
| - delegate_->PlayVolumeAdjustSound(); |
| + delegate_->PlayVolumeAdjustEarcon(); |
| } |
| // If the user is in the corner of the right side of the screen, the volume |
| @@ -799,13 +916,16 @@ void TouchExplorationController::EnterTouchToMouseMode() { |
| } |
| void TouchExplorationController::ResetToNoFingersDown() { |
| - ProcessGestureEvents(); |
| + if (passthrough_timer_.IsRunning()) |
|
aboxhall
2014/08/06 21:03:12
Note that in each of these cases, the IsRunning()
lisayin
2014/08/06 21:48:23
Done.
|
| + passthrough_timer_.Stop(); |
| if (sound_timer_.IsRunning()) |
| sound_timer_.Stop(); |
| - state_ = NO_FINGERS_DOWN; |
| - VLOG_STATE(); |
| if (tap_timer_.IsRunning()) |
| tap_timer_.Stop(); |
| + ProcessGestureEvents(); |
| + state_ = NO_FINGERS_DOWN; |
| + VLOG_STATE(); |
| + |
| } |
| void TouchExplorationController::VlogState(const char* function_name) { |
| @@ -869,6 +989,8 @@ const char* TouchExplorationController::EnumStateToString(State state) { |
| return "GESTURE_IN_PROGRESS"; |
| case TOUCH_EXPLORE_SECOND_PRESS: |
| return "TOUCH_EXPLORE_SECOND_PRESS"; |
| + case CORNER_PASSTHROUGH: |
| + return "CORNER_PASSTHROUGH"; |
| case SLIDE_GESTURE: |
| return "SLIDE_GESTURE"; |
| case ONE_FINGER_PASSTHROUGH: |