Index: ui/chromeos/touch_exploration_controller.h |
diff --git a/ui/chromeos/touch_exploration_controller.h b/ui/chromeos/touch_exploration_controller.h |
index d6fa8af0da7fd47ed933019d57ea2d1cece9efc0..6b5545f9436c76eb0e0bb77b11d424d32126688d 100644 |
--- a/ui/chromeos/touch_exploration_controller.h |
+++ b/ui/chromeos/touch_exploration_controller.h |
@@ -5,9 +5,12 @@ |
#ifndef UI_CHROMEOS_TOUCH_EXPLORATION_CONTROLLER_H_ |
#define UI_CHROMEOS_TOUCH_EXPLORATION_CONTROLLER_H_ |
+#include "base/timer/timer.h" |
#include "base/values.h" |
#include "ui/chromeos/ui_chromeos_export.h" |
+#include "ui/events/event_handler.h" |
#include "ui/events/event_rewriter.h" |
+#include "ui/events/gesture_detection/gesture_detector.h" |
#include "ui/gfx/geometry/point.h" |
namespace aura { |
@@ -17,17 +20,52 @@ class Window; |
namespace ui { |
class Event; |
+class TouchEvent; |
// TouchExplorationController is used in tandem with "Spoken Feedback" to |
-// make the touch UI accessible. TouchExplorationController rewrites the |
-// incoming touch events as follows: |
-// - When one finger is touching the screen, touch events are converted to mouse |
-// moves. This is the "Touch Exploration Mode". (The idea is that mouse moves |
-// will be subsequently used by another component to move focus between UI |
-// elements, and the elements will be read out to the user.) |
-// - When more than one finger is touching the screen, touches from the |
-// first (i.e. "oldest") finger are ignored, and the other touches go through |
-// as is. |
+// make the touch UI accessible. |
+// |
+// At a high-level, single-finger events are used for accessibility - |
+// exploring the screen gets turned into mouse moves (which can then be |
+// spoken by an accessibility service running), a double-tap simulates a |
+// click, and gestures can be used to send high-level accessibility commands. |
+// Whenever two or more fingers are pressed, the events are passed through |
+// withone finger removed - so if you swipe down with two fingers, the |
+// running app will see a one-finger swipe. |
+// |
+// Here are the details of the implementation: |
+// |
+// When the first touch is pressed, a 300 ms grace period timer starts. |
+// |
+// If the user keeps their finger down for more than 300 ms and doesn't |
+// perform a gesture in that time, they enter touch exploration mode, and |
+// all movements are translated into synthesized mouse move events. |
+// |
+// Also, if the user moves their single finger outside a certain slop region |
+// (without performing a gesture), they enter touch exploration mode earlier |
+// than 300 ms. |
+// |
+// If the user taps and releases their finger, after 300 ms from the initial |
+// touch, a single mouse move is fired. |
+// |
+// If the user double-taps, the second tap is passed through, allowing the |
+// user to click - however, the double-tap location is changed to the location |
+// of the last successful touch exploration - that allows the user to explore |
+// anywhere on the screen, hear its description, then double-tap anywhere |
+// to activate it. |
+// |
+// 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 |
+// two-finger scroll gets passed through as a one-finger scroll. However, |
+// once in passthrough mode, if one finger is released, the remaining finger |
+// continues to pass through events, allowing the user to start a scroll |
+// with two fingers but finish it with one. Sometimes this requires rewriting |
+// the touch ids. |
+// |
+// Once either touch exploration or passthrough mode has been activated, |
+// it remains in that mode until all fingers have been released. |
+// |
// The caller is expected to retain ownership of instances of this class and |
// destroy them before |root_window| is destroyed. |
class UI_CHROMEOS_EXPORT TouchExplorationController : |
@@ -36,6 +74,9 @@ class UI_CHROMEOS_EXPORT TouchExplorationController : |
explicit TouchExplorationController(aura::Window* root_window); |
virtual ~TouchExplorationController(); |
+ void CallTapTimerNowForTesting(); |
+ void SetEventHandlerForTesting(ui::EventHandler* event_handler_for_testing); |
+ |
private: |
scoped_ptr<ui::Event> CreateMouseMoveEvent(const gfx::PointF& location, |
int flags); |
@@ -48,16 +89,92 @@ class UI_CHROMEOS_EXPORT TouchExplorationController : |
virtual ui::EventRewriteStatus NextDispatchEvent( |
const ui::Event& last_event, scoped_ptr<ui::Event>* new_event) OVERRIDE; |
+ // Event handlers based on the current state - see State, below. |
+ ui::EventRewriteStatus OnNoFingersDown( |
+ const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event); |
+ ui::EventRewriteStatus OnGracePeriod( |
+ const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event); |
+ ui::EventRewriteStatus OnTouchExploration( |
+ const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event); |
+ ui::EventRewriteStatus OnPassthroughMinusOne( |
+ const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event); |
+ ui::EventRewriteStatus OnSingleTapPending( |
+ const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event); |
+ ui::EventRewriteStatus OnDoubleTapPressed( |
+ 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, |
+ // we treat that as a single mouse move (touch exploration) event. |
+ void OnTapTimerFired(); |
+ |
+ // Dispatch a new event outside of the event rewriting flow. |
+ void DispatchEvent(ui::Event* event); |
+ |
+ enum State { |
+ // No fingers are down and no events are pending. |
+ NO_FINGERS_DOWN, |
+ |
+ // A single finger is down, but we're not yet sure if this is going |
+ // to be touch exploration or something else. |
+ GRACE_PERIOD, |
+ |
+ // A single finger is down and after the grace period the user |
+ // hasn't added a second finger or moved the finger too rapidly. |
+ // We're now in touch exploration mode until this finger is lifted. |
+ TOUCH_EXPLORATION, |
+ |
+ // The user pressed and released a single finger - a tap - but we have |
+ // to wait until the end of the grace period before we will rewrite this |
+ // as touch exploration, in case it's actually the first tap of a mouse |
+ // move. |
+ SINGLE_TAP_PENDING, |
aboxhall
2014/06/02 16:33:14
SINGLE_TAP?
The single tap has occurred; what's p
dmazzoni
2014/06/04 22:46:27
Done.
|
+ |
+ // The user tapped once, and before the grace period expired, pressed |
+ // one finger down to begin a double-tap, but has not released it yet. |
+ DOUBLE_TAP_PRESSED, |
aboxhall
2014/06/02 16:33:14
SECOND_TAP_PRESSED?
(Since we can't quite mark it
dmazzoni
2014/06/04 22:46:27
I think "double tap" is better here - if you hold
|
+ |
+ // The user placed two or more fingers down within the grace period. |
+ // We're now in passthrough mode until all fingers are lifted. When |
+ // two ore more fingers are down, we subtract one finger but otherwise |
+ // pass through all events unchanged. |
+ PASSTHROUGH_MINUS_ONE, |
+ }; |
+ |
+ aura::Window* root_window_; |
+ |
// A set of touch ids for fingers currently touching the screen. |
std::vector<int> touch_ids_; |
// Map of touch ids to their last known location. |
std::map<int, gfx::PointF> touch_locations_; |
- // Initialized from RewriteEvent() and dispatched in NextDispatchEvent(). |
- scoped_ptr<ui::Event> next_dispatch_event_; |
+ // The touch id that any events on the initial finger should be rewritten |
+ // as in passthrough-minus-one mode. If 0, events on the initial finger are |
+ // discarded. |
+ int initial_touch_id_passthrough_mapping_; |
- aura::Window* root_window_; |
+ // The current state. |
+ State state_; |
+ |
+ // 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_; |
+ |
+ // A timer to fire the mouse move event after the double-tap delay. |
+ base::OneShotTimer<TouchExplorationController> tap_timer_; |
+ |
+ // For testing only, an event handler to use for generated events |
+ // outside of the normal event rewriting flow. |
+ ui::EventHandler* event_handler_for_testing_; |
+ |
+ // A default gesture detector config, so we can share the same |
+ // timeout and pixel slop constants. |
+ ui::GestureDetector::Config gesture_detector_config_; |
DISALLOW_COPY_AND_ASSIGN(TouchExplorationController); |
}; |