| Index: ui/chromeos/touch_exploration_controller.cc
|
| diff --git a/ui/chromeos/touch_exploration_controller.cc b/ui/chromeos/touch_exploration_controller.cc
|
| index 01e3d5424eb46c0d82cf0307999af99242e7b598..7ecb54fc0398b9e1c60bd225c47e51b921129926 100644
|
| --- a/ui/chromeos/touch_exploration_controller.cc
|
| +++ b/ui/chromeos/touch_exploration_controller.cc
|
| @@ -23,6 +23,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
|
| @@ -35,7 +39,8 @@ TouchExplorationController::TouchExplorationController(
|
| state_(NO_FINGERS_DOWN),
|
| gesture_provider_(this),
|
| prev_state_(NO_FINGERS_DOWN),
|
| - VLOG_on_(true) {
|
| + VLOG_on_(true),
|
| + waiting_for_corner_passthrough_(false) {
|
| CHECK(root_window);
|
| root_window->GetHost()->GetEventSource()->AddEventRewriter(this);
|
| }
|
| @@ -72,6 +77,13 @@ ui::EventRewriteStatus TouchExplorationController::RewriteEvent(
|
| // this event under this new state.
|
| }
|
|
|
| + if (long_press_timer_.IsRunning() &&
|
| + event.time_stamp() - initial_press_->time_stamp() >
|
| + gesture_detector_config_.longpress_timeout) {
|
| + long_press_timer_.Stop();
|
| + OnLongPressTimerFired();
|
| + }
|
| +
|
| const ui::EventType type = touch_event.type();
|
| const gfx::PointF& location = touch_event.location_f();
|
| const int touch_id = touch_event.touch_id();
|
| @@ -79,14 +91,25 @@ ui::EventRewriteStatus TouchExplorationController::RewriteEvent(
|
| // Always update touch ids and touch locations, so we can use those
|
| // no matter what state we're in.
|
| if (type == ui::ET_TOUCH_PRESSED) {
|
| + // If the user enters the screen then send an earcon.
|
| + gfx::Point edges = touch_event.location();
|
| + if (FindEdgesWithinBounds(edges, kLeavingScreenEdge) != NO_EDGE) {
|
| + if (VLOG_on_)
|
| + VLOG(0) << "Entering the screen";
|
| + delegate_->PlayEnterScreenEarcon();
|
| + }
|
| current_touch_ids_.push_back(touch_id);
|
| touch_locations_.insert(std::pair<int, gfx::PointF>(touch_id, location));
|
| } else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
|
| // 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) {
|
| + gfx::Point edges = touch_event.location();
|
| + if (FindEdgesWithinBounds(edges, kLeavingScreenEdge) != NO_EDGE) {
|
| + if (VLOG_on_)
|
| + VLOG(0) << "Leaving screen";
|
| + // Indicates to the user that they are leaving the screen.
|
| + delegate_->PlayExitScreenEarcon();
|
| if (current_touch_ids_.size() == 0)
|
| ResetToNoFingersDown();
|
| }
|
| @@ -131,6 +154,8 @@ ui::EventRewriteStatus TouchExplorationController::RewriteEvent(
|
| return InTouchExploreSecondPress(touch_event, rewritten_event);
|
| case TWO_TO_ONE_FINGER:
|
| return InTwoToOneFinger(touch_event, rewritten_event);
|
| + case CORNER_PASSTHROUGH:
|
| + return InCornerPassthrough(touch_event, rewritten_event);
|
| case PASSTHROUGH:
|
| return InPassthrough(touch_event, rewritten_event);
|
| case WAIT_FOR_RELEASE:
|
| @@ -151,29 +176,64 @@ 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));
|
| - tap_timer_.Start(FROM_HERE,
|
| - 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;
|
| + 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;
|
| + int location = FindEdgesWithinBounds(event.location(), kSlopDistanceFromEdge);
|
| + base::TimeDelta timeout;
|
| +
|
| + // 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) {
|
| + if (VLOG_on_)
|
| + VLOG(0) << "Location: " << location;
|
| + long_press_timer_.Start(FROM_HERE,
|
| + gesture_detector_config_.longpress_timeout,
|
| + this,
|
| + &TouchExplorationController::OnLongPressTimerFired);
|
| + waiting_for_corner_passthrough_ = true;
|
| + } else {
|
| + waiting_for_corner_passthrough_ = false;
|
| + }
|
| + 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) {
|
| + waiting_for_corner_passthrough_ = false;
|
| + if (long_press_timer_.IsRunning()) {
|
| + long_press_timer_.Stop();
|
| + if (event.time_stamp() - initial_press_->time_stamp() >
|
| + gesture_detector_config_.double_tap_timeout) {
|
| + OnTapTimerFired();
|
| + }
|
| + }
|
| + }
|
| +
|
| if (type == ui::ET_TOUCH_PRESSED) {
|
| + waiting_for_corner_passthrough_ = false;
|
| // Adding a second finger within the timeout period switches to
|
| // passing through every event from the second finger and none form the
|
| // first. The event from the first finger is still saved in initial_press_.
|
| @@ -186,6 +246,7 @@ ui::EventRewriteStatus TouchExplorationController::InSingleTapPressed(
|
| (*rewritten_event)->set_flags(event.flags());
|
| return EVENT_REWRITE_REWRITTEN;
|
| } else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
|
| + waiting_for_corner_passthrough_ = false;
|
| DCHECK_EQ(0U, current_touch_ids_.size());
|
| state_ = SINGLE_TAP_RELEASED;
|
| VLOG_STATE();
|
| @@ -201,15 +262,15 @@ ui::EventRewriteStatus TouchExplorationController::InSingleTapPressed(
|
| 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 (VLOG_on_) {
|
| + 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;
|
| + }
|
| // 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);
|
| @@ -398,7 +459,7 @@ ui::EventRewriteStatus TouchExplorationController::InTwoToOneFinger(
|
| // If a third finger is pressed, we are now going into passthrough mode
|
| // and now need to dispatch the first finger into a press, as well as the
|
| // recent press.
|
| - if (current_touch_ids_.size() == 3){
|
| + if (current_touch_ids_.size() == 3) {
|
| state_ = PASSTHROUGH;
|
| scoped_ptr<ui::TouchEvent> first_finger_press;
|
| first_finger_press.reset(
|
| @@ -437,6 +498,40 @@ ui::EventRewriteStatus TouchExplorationController::InTwoToOneFinger(
|
| return ui::EVENT_REWRITE_CONTINUE;
|
| }
|
|
|
| +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;
|
| + }
|
| +
|
| + waiting_for_corner_passthrough_ = false;
|
| + state_ = WAIT_FOR_RELEASE;
|
| + 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::InPassthrough(
|
| const ui::TouchEvent& event,
|
| scoped_ptr<ui::Event>* rewritten_event) {
|
| @@ -504,6 +599,7 @@ ui::EventRewriteStatus TouchExplorationController::InTouchExploreSecondPress(
|
| ui::EventRewriteStatus TouchExplorationController::InWaitForRelease(
|
| const ui::TouchEvent& event,
|
| scoped_ptr<ui::Event>* rewritten_event) {
|
| + waiting_for_corner_passthrough_ = false;
|
| ui::EventType type = event.type();
|
| if (!(type == ui::ET_TOUCH_PRESSED || type == ui::ET_TOUCH_MOVED ||
|
| type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED)) {
|
| @@ -519,7 +615,7 @@ ui::EventRewriteStatus TouchExplorationController::InWaitForRelease(
|
| }
|
|
|
| void TouchExplorationController::PlaySoundForTimer() {
|
| - delegate_->PlayVolumeAdjustSound();
|
| + delegate_->PlayVolumeAdjustEarcon();
|
| }
|
|
|
| ui::EventRewriteStatus TouchExplorationController::InSlideGesture(
|
| @@ -559,7 +655,7 @@ ui::EventRewriteStatus TouchExplorationController::InSlideGesture(
|
| kSoundDelay,
|
| this,
|
| &ui::TouchExplorationController::PlaySoundForTimer);
|
| - delegate_->PlayVolumeAdjustSound();
|
| + delegate_->PlayVolumeAdjustEarcon();
|
| }
|
|
|
| // There should not be more than one finger down.
|
| @@ -591,6 +687,8 @@ void TouchExplorationController::OnTapTimerFired() {
|
| last_touch_exploration_.reset(new TouchEvent(*initial_press_));
|
| return;
|
| case SINGLE_TAP_PRESSED:
|
| + if (waiting_for_corner_passthrough_)
|
| + return;
|
| case GESTURE_IN_PROGRESS:
|
| // Discard any pending gestures.
|
| delete gesture_provider_.GetAndResetPendingGestures();
|
| @@ -607,6 +705,18 @@ void TouchExplorationController::OnTapTimerFired() {
|
| last_touch_exploration_.reset(new TouchEvent(*initial_press_));
|
| }
|
|
|
| +void TouchExplorationController::OnLongPressTimerFired() {
|
| + if (waiting_for_corner_passthrough_) {
|
| + 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 +756,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
|
| @@ -680,9 +790,11 @@ void TouchExplorationController::SideSlideControl(ui::GestureEvent* gesture) {
|
| root_window_->bounds().height() - 2 * kMaxDistanceFromEdge;
|
| float ratio = (location.y() - kMaxDistanceFromEdge) / volume_adjust_height;
|
| float volume = 100 - 100 * ratio;
|
| - VLOG(0) << "\n Volume = " << volume << "\n Location = " << location.ToString()
|
| - << "\n Bounds = " << root_window_->bounds().right();
|
| -
|
| + if (VLOG_on_) {
|
| + VLOG(0) << "\n Volume = " << volume
|
| + << "\n Location = " << location.ToString()
|
| + << "\n Bounds = " << root_window_->bounds().right();
|
| + }
|
| delegate_->SetOutputLevel(int(volume));
|
| }
|
|
|
| @@ -797,6 +909,7 @@ void TouchExplorationController::EnterTouchToMouseMode() {
|
| }
|
|
|
| void TouchExplorationController::ResetToNoFingersDown() {
|
| + waiting_for_corner_passthrough_ = false;
|
| ProcessGestureEvents();
|
| if (sound_timer_.IsRunning())
|
| sound_timer_.Stop();
|
| @@ -867,6 +980,8 @@ const char* TouchExplorationController::EnumStateToString(State state) {
|
| return "TOUCH_EXPLORE_SECOND_PRESS";
|
| case TWO_TO_ONE_FINGER:
|
| return "TWO_TO_ONE_FINGER";
|
| + case CORNER_PASSTHROUGH:
|
| + return "CORNER_PASSTHROUGH";
|
| case PASSTHROUGH:
|
| return "PASSTHROUGH";
|
| case WAIT_FOR_RELEASE:
|
|
|