Commit bfc968aa authored by evy@chromium.org's avatar evy@chromium.org

Added split tap to TouchExplorationController.

Split tap allows a user to click while in touch exploration mode by tapping a second finger anywhere on the screen.

*Added TOUCH_EXPL_SECOND_PRESS state and a InTouchExplSecondPress function in the controller
*Added tests for tap and tap hold events, and also tested the case where the user lifts the touch exploration finger first.

BUG= 385151
TEST = TouchExplorationTest.*

Review URL: https://codereview.chromium.org/333623003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@278093 0039d316-1c4b-4281-b951-d872f2087c98
parent cf1ce86a
...@@ -129,6 +129,8 @@ ui::EventRewriteStatus TouchExplorationController::RewriteEvent( ...@@ -129,6 +129,8 @@ ui::EventRewriteStatus TouchExplorationController::RewriteEvent(
return InTouchExploration(touch_event, rewritten_event); return InTouchExploration(touch_event, rewritten_event);
case PASSTHROUGH_MINUS_ONE: case PASSTHROUGH_MINUS_ONE:
return InPassthroughMinusOne(touch_event, rewritten_event); return InPassthroughMinusOne(touch_event, rewritten_event);
case TOUCH_EXPLORE_SECOND_PRESS:
return InTouchExploreSecondPress(touch_event, rewritten_event);
} }
NOTREACHED(); NOTREACHED();
...@@ -199,18 +201,27 @@ ui::EventRewriteStatus TouchExplorationController::InSingleTapReleased( ...@@ -199,18 +201,27 @@ ui::EventRewriteStatus TouchExplorationController::InSingleTapReleased(
if (type == ui::ET_TOUCH_PRESSED) { if (type == ui::ET_TOUCH_PRESSED) {
// This is the second tap in a double-tap (or double tap-hold). // This is the second tap in a double-tap (or double tap-hold).
// Rewrite at location of last touch exploration. // Rewrite at location of last touch exploration.
ui::TouchEvent* rewritten_press_event = new ui::TouchEvent( // If there is no touch exploration yet, discard instead.
ui::ET_TOUCH_PRESSED, if (!last_touch_exploration_) {
last_touch_exploration_location_, return ui::EVENT_REWRITE_DISCARD;
event.touch_id(), }
event.time_stamp()); rewritten_event->reset(
rewritten_press_event->set_flags(event.flags()); new ui::TouchEvent(ui::ET_TOUCH_PRESSED,
rewritten_event->reset(rewritten_press_event); last_touch_exploration_->location(),
event.touch_id(),
event.time_stamp()));
(*rewritten_event)->set_flags(event.flags());
state_ = DOUBLE_TAP_PRESSED; state_ = DOUBLE_TAP_PRESSED;
VLOG_STATE(); VLOG_STATE();
return ui::EVENT_REWRITE_REWRITTEN; return ui::EVENT_REWRITE_REWRITTEN;
} }
// If the previous press was discarded, we need to also handle its release.
if (type == ui::ET_TOUCH_RELEASED && !last_touch_exploration_) {
if (current_touch_ids_.size() == 0) {
state_ = NO_FINGERS_DOWN;
}
return ui::EVENT_REWRITE_DISCARD;
}
NOTREACHED(); NOTREACHED();
return ui::EVENT_REWRITE_CONTINUE; return ui::EVENT_REWRITE_CONTINUE;
} }
...@@ -225,13 +236,12 @@ ui::EventRewriteStatus TouchExplorationController::InDoubleTapPressed( ...@@ -225,13 +236,12 @@ ui::EventRewriteStatus TouchExplorationController::InDoubleTapPressed(
return EVENT_REWRITE_DISCARD; return EVENT_REWRITE_DISCARD;
// Rewrite at location of last touch exploration. // Rewrite at location of last touch exploration.
ui::TouchEvent* rewritten_release_event = new ui::TouchEvent( rewritten_event->reset(
ui::ET_TOUCH_RELEASED, new ui::TouchEvent(ui::ET_TOUCH_RELEASED,
last_touch_exploration_location_, last_touch_exploration_->location(),
event.touch_id(), event.touch_id(),
event.time_stamp()); event.time_stamp()));
rewritten_release_event->set_flags(event.flags()); (*rewritten_event)->set_flags(event.flags());
rewritten_event->reset(rewritten_release_event);
ResetToNoFingersDown(); ResetToNoFingersDown();
return ui::EVENT_REWRITE_REWRITTEN; return ui::EVENT_REWRITE_REWRITTEN;
} else if (type == ui::ET_TOUCH_MOVED) { } else if (type == ui::ET_TOUCH_MOVED) {
...@@ -245,9 +255,19 @@ ui::EventRewriteStatus TouchExplorationController::InTouchExploration( ...@@ -245,9 +255,19 @@ ui::EventRewriteStatus TouchExplorationController::InTouchExploration(
const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event) { const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event) {
const ui::EventType type = event.type(); const ui::EventType type = event.type();
if (type == ui::ET_TOUCH_PRESSED) { if (type == ui::ET_TOUCH_PRESSED) {
// Ignore any additional fingers when we're already in touch exploration // Handle split-tap.
// mode. TODO(evy, lisayin): Support "split-tap" here instead. initial_press_.reset(new TouchEvent(event));
return ui::EVENT_REWRITE_DISCARD; if (tap_timer_.IsRunning())
tap_timer_.Stop();
rewritten_event->reset(
new ui::TouchEvent(ui::ET_TOUCH_PRESSED,
last_touch_exploration_->location(),
event.touch_id(),
event.time_stamp()));
(*rewritten_event)->set_flags(event.flags());
state_ = TOUCH_EXPLORE_SECOND_PRESS;
VLOG_STATE();
return ui::EVENT_REWRITE_REWRITTEN;
} else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) { } else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
if (current_touch_ids_.size() == 0) if (current_touch_ids_.size() == 0)
ResetToNoFingersDown(); ResetToNoFingersDown();
...@@ -258,7 +278,7 @@ ui::EventRewriteStatus TouchExplorationController::InTouchExploration( ...@@ -258,7 +278,7 @@ ui::EventRewriteStatus TouchExplorationController::InTouchExploration(
// Rewrite as a mouse-move event. // Rewrite as a mouse-move event.
*rewritten_event = CreateMouseMoveEvent(event.location(), event.flags()); *rewritten_event = CreateMouseMoveEvent(event.location(), event.flags());
last_touch_exploration_location_ = event.location(); last_touch_exploration_.reset(new TouchEvent(event));
return ui::EVENT_REWRITE_REWRITTEN; return ui::EVENT_REWRITE_REWRITTEN;
} }
...@@ -278,13 +298,12 @@ ui::EventRewriteStatus TouchExplorationController::InPassthroughMinusOne( ...@@ -278,13 +298,12 @@ ui::EventRewriteStatus TouchExplorationController::InPassthroughMinusOne(
// If the only finger now remaining is the first finger, // If the only finger now remaining is the first finger,
// rewrite as a move to the location of the first finger. // rewrite as a move to the location of the first finger.
initial_touch_id_passthrough_mapping_ = event.touch_id(); initial_touch_id_passthrough_mapping_ = event.touch_id();
ui::TouchEvent* rewritten_passthrough_event = new ui::TouchEvent( rewritten_event->reset(
ui::ET_TOUCH_MOVED, new ui::TouchEvent(ui::ET_TOUCH_MOVED,
touch_locations_[initial_press_->touch_id()], touch_locations_[initial_press_->touch_id()],
initial_touch_id_passthrough_mapping_, initial_touch_id_passthrough_mapping_,
event.time_stamp()); event.time_stamp()));
rewritten_passthrough_event->set_flags(event.flags()); (*rewritten_event)->set_flags(event.flags());
rewritten_event->reset(rewritten_passthrough_event);
return ui::EVENT_REWRITE_REWRITTEN; return ui::EVENT_REWRITE_REWRITTEN;
} }
} }
...@@ -296,19 +315,59 @@ ui::EventRewriteStatus TouchExplorationController::InPassthroughMinusOne( ...@@ -296,19 +315,59 @@ ui::EventRewriteStatus TouchExplorationController::InPassthroughMinusOne(
return ui::EVENT_REWRITE_DISCARD; return ui::EVENT_REWRITE_DISCARD;
} }
ui::TouchEvent* rewritten_passthrough_event = new ui::TouchEvent( rewritten_event->reset(
type, new ui::TouchEvent(type,
location, location,
initial_touch_id_passthrough_mapping_, initial_touch_id_passthrough_mapping_,
event.time_stamp()); event.time_stamp()));
rewritten_passthrough_event->set_flags(event.flags()); (*rewritten_event)->set_flags(event.flags());
rewritten_event->reset(rewritten_passthrough_event);
return ui::EVENT_REWRITE_REWRITTEN; return ui::EVENT_REWRITE_REWRITTEN;
} }
return ui::EVENT_REWRITE_CONTINUE; return ui::EVENT_REWRITE_CONTINUE;
} }
ui::EventRewriteStatus TouchExplorationController::InTouchExploreSecondPress(
const ui::TouchEvent& event,
scoped_ptr<ui::Event>* rewritten_event) {
ui::EventType type = event.type();
gfx::PointF location = event.location_f();
if (type == ui::ET_TOUCH_PRESSED) {
return ui::EVENT_REWRITE_DISCARD;
} else if (type == ui::ET_TOUCH_MOVED) {
// Currently this is a discard, but could be something like rotor
// in the future.
return ui::EVENT_REWRITE_DISCARD;
} else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
// If the touch exploration finger is lifted, there is no option to return
// to touch explore anymore. The remaining finger acts as a pending
// tap or long tap for the last touch explore location.
if (event.touch_id() == last_touch_exploration_->touch_id()){
state_ = DOUBLE_TAP_PRESSED;
VLOG_STATE();
return EVENT_REWRITE_DISCARD;
}
// Continue to release the touch only if the touch explore finger is the
// only finger remaining.
if (current_touch_ids_.size() != 1)
return EVENT_REWRITE_DISCARD;
// Rewrite at location of last touch exploration.
rewritten_event->reset(
new ui::TouchEvent(ui::ET_TOUCH_RELEASED,
last_touch_exploration_->location(),
initial_press_->touch_id(),
event.time_stamp()));
(*rewritten_event)->set_flags(event.flags());
state_ = TOUCH_EXPLORATION;
VLOG_STATE();
return ui::EVENT_REWRITE_REWRITTEN;
}
NOTREACHED() << "Unexpected event type received.";
return ui::EVENT_REWRITE_CONTINUE;
}
void TouchExplorationController::OnTapTimerFired() { void TouchExplorationController::OnTapTimerFired() {
if (state_ != SINGLE_TAP_RELEASED && state_ != SINGLE_TAP_PRESSED) if (state_ != SINGLE_TAP_RELEASED && state_ != SINGLE_TAP_PRESSED)
return; return;
...@@ -324,7 +383,7 @@ void TouchExplorationController::OnTapTimerFired() { ...@@ -324,7 +383,7 @@ void TouchExplorationController::OnTapTimerFired() {
scoped_ptr<ui::Event> mouse_move = CreateMouseMoveEvent( scoped_ptr<ui::Event> mouse_move = CreateMouseMoveEvent(
initial_press_->location(), initial_press_->flags()); initial_press_->location(), initial_press_->flags());
DispatchEvent(mouse_move.get()); DispatchEvent(mouse_move.get());
last_touch_exploration_location_ = initial_press_->location(); last_touch_exploration_.reset(new TouchEvent(*initial_press_));
} }
void TouchExplorationController::DispatchEvent(ui::Event* event) { void TouchExplorationController::DispatchEvent(ui::Event* event) {
...@@ -406,6 +465,8 @@ const char* TouchExplorationController::EnumStateToString(State state) { ...@@ -406,6 +465,8 @@ const char* TouchExplorationController::EnumStateToString(State state) {
return "TOUCH_EXPLORATION"; return "TOUCH_EXPLORATION";
case PASSTHROUGH_MINUS_ONE: case PASSTHROUGH_MINUS_ONE:
return "PASSTHROUGH_MINUS_ONE"; return "PASSTHROUGH_MINUS_ONE";
case TOUCH_EXPLORE_SECOND_PRESS:
return "TOUCH_EXPLORE_SECOND_PRESS";
} }
return "Not a state"; return "Not a state";
} }
......
...@@ -60,6 +60,10 @@ class TouchEvent; ...@@ -60,6 +60,10 @@ class TouchEvent;
// anywhere on the screen, hear its description, then double-tap anywhere // anywhere on the screen, hear its description, then double-tap anywhere
// to activate it. // to activate it.
// //
// If the user enters touch exploration mode, they can click without lifting
// their touch exploration finger by tapping anywhere else on the screen with
// a second finger, while the touch exploration finger is still pressed.
//
// If the user adds a second finger during the grace period, they enter // If the user adds a second finger during the grace period, they enter
// passthrough mode. In this mode, the first finger is ignored but all // passthrough mode. In this mode, the first finger is ignored but all
// additional touch events are mostly passed through unmodified. So a // additional touch events are mostly passed through unmodified. So a
...@@ -105,7 +109,8 @@ class UI_CHROMEOS_EXPORT TouchExplorationController : ...@@ -105,7 +109,8 @@ class UI_CHROMEOS_EXPORT TouchExplorationController :
const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event); const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event);
ui::EventRewriteStatus InPassthroughMinusOne( ui::EventRewriteStatus InPassthroughMinusOne(
const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event); const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event);
ui::EventRewriteStatus InTouchExploreSecondPress(
const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event);
// This timer is started every time we get the first press event, and // This timer is started every time we get the first press event, and
// it fires after the double-click timeout elapses (300 ms by default). // it fires after the double-click timeout elapses (300 ms by default).
// If the user taps and releases within 300 ms and doesn't press again, // If the user taps and releases within 300 ms and doesn't press again,
...@@ -159,6 +164,13 @@ class UI_CHROMEOS_EXPORT TouchExplorationController : ...@@ -159,6 +164,13 @@ class UI_CHROMEOS_EXPORT TouchExplorationController :
// the user starts a scroll with 2 fingers, they can release either one // the user starts a scroll with 2 fingers, they can release either one
// and continue the scrolling. // and continue the scrolling.
PASSTHROUGH_MINUS_ONE, PASSTHROUGH_MINUS_ONE,
// 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
// releases the touch explore finger, the other finger will continue with
// touch explore. Any fingers pressed past the first two are ignored.
TOUCH_EXPLORE_SECOND_PRESS,
}; };
void VlogState(const char* function_name); void VlogState(const char* function_name);
...@@ -190,9 +202,9 @@ class UI_CHROMEOS_EXPORT TouchExplorationController : ...@@ -190,9 +202,9 @@ class UI_CHROMEOS_EXPORT TouchExplorationController :
// A copy of the event from the initial touch press. // A copy of the event from the initial touch press.
scoped_ptr<ui::TouchEvent> initial_press_; scoped_ptr<ui::TouchEvent> initial_press_;
// The last location where we synthesized a mouse move event. // The last synthesized mouse move event. When the user double-taps,
// When the user double-taps, we send the passed-through tap here. // we send the passed-through tap to the location of this event.
gfx::PointF last_touch_exploration_location_; scoped_ptr<ui::TouchEvent> last_touch_exploration_;
// A timer to fire the mouse move event after the double-tap delay. // A timer to fire the mouse move event after the double-tap delay.
base::OneShotTimer<TouchExplorationController> tap_timer_; base::OneShotTimer<TouchExplorationController> tap_timer_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment