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(
return InTouchExploration(touch_event, rewritten_event);
case PASSTHROUGH_MINUS_ONE:
return InPassthroughMinusOne(touch_event, rewritten_event);
case TOUCH_EXPLORE_SECOND_PRESS:
return InTouchExploreSecondPress(touch_event, rewritten_event);
}
NOTREACHED();
......@@ -199,18 +201,27 @@ ui::EventRewriteStatus TouchExplorationController::InSingleTapReleased(
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.
ui::TouchEvent* rewritten_press_event = new ui::TouchEvent(
ui::ET_TOUCH_PRESSED,
last_touch_exploration_location_,
event.touch_id(),
event.time_stamp());
rewritten_press_event->set_flags(event.flags());
rewritten_event->reset(rewritten_press_event);
// 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(),
event.touch_id(),
event.time_stamp()));
(*rewritten_event)->set_flags(event.flags());
state_ = DOUBLE_TAP_PRESSED;
VLOG_STATE();
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();
return ui::EVENT_REWRITE_CONTINUE;
}
......@@ -225,13 +236,12 @@ ui::EventRewriteStatus TouchExplorationController::InDoubleTapPressed(
return EVENT_REWRITE_DISCARD;
// Rewrite at location of last touch exploration.
ui::TouchEvent* rewritten_release_event = new ui::TouchEvent(
ui::ET_TOUCH_RELEASED,
last_touch_exploration_location_,
event.touch_id(),
event.time_stamp());
rewritten_release_event->set_flags(event.flags());
rewritten_event->reset(rewritten_release_event);
rewritten_event->reset(
new ui::TouchEvent(ui::ET_TOUCH_RELEASED,
last_touch_exploration_->location(),
event.touch_id(),
event.time_stamp()));
(*rewritten_event)->set_flags(event.flags());
ResetToNoFingersDown();
return ui::EVENT_REWRITE_REWRITTEN;
} else if (type == ui::ET_TOUCH_MOVED) {
......@@ -245,9 +255,19 @@ ui::EventRewriteStatus TouchExplorationController::InTouchExploration(
const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event) {
const ui::EventType type = event.type();
if (type == ui::ET_TOUCH_PRESSED) {
// Ignore any additional fingers when we're already in touch exploration
// mode. TODO(evy, lisayin): Support "split-tap" here instead.
return ui::EVENT_REWRITE_DISCARD;
// Handle split-tap.
initial_press_.reset(new TouchEvent(event));
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) {
if (current_touch_ids_.size() == 0)
ResetToNoFingersDown();
......@@ -258,7 +278,7 @@ ui::EventRewriteStatus TouchExplorationController::InTouchExploration(
// Rewrite as a mouse-move event.
*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;
}
......@@ -278,13 +298,12 @@ ui::EventRewriteStatus TouchExplorationController::InPassthroughMinusOne(
// If the only finger now remaining is the first finger,
// rewrite as a move to the location of the first finger.
initial_touch_id_passthrough_mapping_ = event.touch_id();
ui::TouchEvent* rewritten_passthrough_event = new ui::TouchEvent(
ui::ET_TOUCH_MOVED,
touch_locations_[initial_press_->touch_id()],
initial_touch_id_passthrough_mapping_,
event.time_stamp());
rewritten_passthrough_event->set_flags(event.flags());
rewritten_event->reset(rewritten_passthrough_event);
rewritten_event->reset(
new ui::TouchEvent(ui::ET_TOUCH_MOVED,
touch_locations_[initial_press_->touch_id()],
initial_touch_id_passthrough_mapping_,
event.time_stamp()));
(*rewritten_event)->set_flags(event.flags());
return ui::EVENT_REWRITE_REWRITTEN;
}
}
......@@ -296,19 +315,59 @@ ui::EventRewriteStatus TouchExplorationController::InPassthroughMinusOne(
return ui::EVENT_REWRITE_DISCARD;
}
ui::TouchEvent* rewritten_passthrough_event = new ui::TouchEvent(
type,
location,
initial_touch_id_passthrough_mapping_,
event.time_stamp());
rewritten_passthrough_event->set_flags(event.flags());
rewritten_event->reset(rewritten_passthrough_event);
rewritten_event->reset(
new ui::TouchEvent(type,
location,
initial_touch_id_passthrough_mapping_,
event.time_stamp()));
(*rewritten_event)->set_flags(event.flags());
return ui::EVENT_REWRITE_REWRITTEN;
}
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() {
if (state_ != SINGLE_TAP_RELEASED && state_ != SINGLE_TAP_PRESSED)
return;
......@@ -324,7 +383,7 @@ void TouchExplorationController::OnTapTimerFired() {
scoped_ptr<ui::Event> mouse_move = CreateMouseMoveEvent(
initial_press_->location(), initial_press_->flags());
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) {
......@@ -406,6 +465,8 @@ const char* TouchExplorationController::EnumStateToString(State state) {
return "TOUCH_EXPLORATION";
case PASSTHROUGH_MINUS_ONE:
return "PASSTHROUGH_MINUS_ONE";
case TOUCH_EXPLORE_SECOND_PRESS:
return "TOUCH_EXPLORE_SECOND_PRESS";
}
return "Not a state";
}
......
......@@ -60,6 +60,10 @@ class TouchEvent;
// anywhere on the screen, hear its description, then double-tap anywhere
// 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
// passthrough mode. In this mode, the first finger is ignored but all
// additional touch events are mostly passed through unmodified. So a
......@@ -105,7 +109,8 @@ class UI_CHROMEOS_EXPORT TouchExplorationController :
const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event);
ui::EventRewriteStatus InPassthroughMinusOne(
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
// 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,
......@@ -159,6 +164,13 @@ class UI_CHROMEOS_EXPORT TouchExplorationController :
// the user starts a scroll with 2 fingers, they can release either one
// and continue the scrolling.
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);
......@@ -190,9 +202,9 @@ class UI_CHROMEOS_EXPORT TouchExplorationController :
// A copy of the event from the initial touch press.
scoped_ptr<ui::TouchEvent> initial_press_;
// The last location where we synthesized a mouse move event.
// When the user double-taps, we send the passed-through tap here.
gfx::PointF last_touch_exploration_location_;
// The last synthesized mouse move event. When the user double-taps,
// we send the passed-through tap to the location of this event.
scoped_ptr<ui::TouchEvent> last_touch_exploration_;
// A timer to fire the mouse move event after the double-tap delay.
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