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: |