OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #ifndef UI_CHROMEOS_TOUCH_EXPLORATION_CONTROLLER_H_ | 5 #ifndef UI_CHROMEOS_TOUCH_EXPLORATION_CONTROLLER_H_ |
6 #define UI_CHROMEOS_TOUCH_EXPLORATION_CONTROLLER_H_ | 6 #define UI_CHROMEOS_TOUCH_EXPLORATION_CONTROLLER_H_ |
7 | 7 |
| 8 #include "base/timer/timer.h" |
8 #include "base/values.h" | 9 #include "base/values.h" |
9 #include "ui/chromeos/ui_chromeos_export.h" | 10 #include "ui/chromeos/ui_chromeos_export.h" |
10 #include "ui/events/event_rewriter.h" | 11 #include "ui/events/event_rewriter.h" |
| 12 #include "ui/events/gesture_detection/gesture_detector.h" |
11 #include "ui/gfx/geometry/point.h" | 13 #include "ui/gfx/geometry/point.h" |
12 | 14 |
13 namespace aura { | 15 namespace aura { |
14 class Window; | 16 class Window; |
15 } | 17 } |
16 | 18 |
17 namespace ui { | 19 namespace ui { |
18 | 20 |
19 class Event; | 21 class Event; |
| 22 class EventHandler; |
| 23 class TouchEvent; |
20 | 24 |
21 // TouchExplorationController is used in tandem with "Spoken Feedback" to | 25 // TouchExplorationController is used in tandem with "Spoken Feedback" to |
22 // make the touch UI accessible. TouchExplorationController rewrites the | 26 // make the touch UI accessible. |
23 // incoming touch events as follows: | 27 // |
24 // - When one finger is touching the screen, touch events are converted to mouse | 28 // ** Short version ** |
25 // moves. This is the "Touch Exploration Mode". (The idea is that mouse moves | 29 // |
26 // will be subsequently used by another component to move focus between UI | 30 // At a high-level, single-finger events are used for accessibility - |
27 // elements, and the elements will be read out to the user.) | 31 // exploring the screen gets turned into mouse moves (which can then be |
28 // - When more than one finger is touching the screen, touches from the | 32 // spoken by an accessibility service running), a double-tap simulates a |
29 // first (i.e. "oldest") finger are ignored, and the other touches go through | 33 // click, and gestures can be used to send high-level accessibility commands. |
30 // as is. | 34 // When two or more fingers are pressed initially, from then on the events |
| 35 // are passed through, but with the initial finger removed - so if you swipe |
| 36 // down with two fingers, the running app will see a one-finger swipe. |
| 37 // |
| 38 // ** Long version ** |
| 39 // |
| 40 // Here are the details of the implementation: |
| 41 // |
| 42 // When the first touch is pressed, a 300 ms grace period timer starts. |
| 43 // |
| 44 // If the user keeps their finger down for more than 300 ms and doesn't |
| 45 // perform a supported accessibility gesture in that time (e.g. swipe right), |
| 46 // they enter touch exploration mode, and all movements are translated into |
| 47 // synthesized mouse move events. |
| 48 // |
| 49 // Also, if the user moves their single finger outside a certain slop region |
| 50 // (without performing a gesture), they enter touch exploration mode earlier |
| 51 // than 300 ms. |
| 52 // |
| 53 // If the user taps and releases their finger, after 300 ms from the initial |
| 54 // touch, a single mouse move is fired. |
| 55 // |
| 56 // If the user double-taps, the second tap is passed through, allowing the |
| 57 // user to click - however, the double-tap location is changed to the location |
| 58 // of the last successful touch exploration - that allows the user to explore |
| 59 // anywhere on the screen, hear its description, then double-tap anywhere |
| 60 // to activate it. |
| 61 // |
| 62 // If the user adds a second finger during the grace period, they enter |
| 63 // passthrough mode. In this mode, the first finger is ignored but all |
| 64 // additional touch events are mostly passed through unmodified. So a |
| 65 // two-finger scroll gets passed through as a one-finger scroll. However, |
| 66 // once in passthrough mode, if one finger is released, the remaining fingers |
| 67 // continue to pass through events, allowing the user to start a scroll |
| 68 // with two fingers but finish it with one. Sometimes this requires rewriting |
| 69 // the touch ids. |
| 70 // |
| 71 // Once either touch exploration or passthrough mode has been activated, |
| 72 // it remains in that mode until all fingers have been released. |
| 73 // |
31 // The caller is expected to retain ownership of instances of this class and | 74 // The caller is expected to retain ownership of instances of this class and |
32 // destroy them before |root_window| is destroyed. | 75 // destroy them before |root_window| is destroyed. |
33 class UI_CHROMEOS_EXPORT TouchExplorationController : | 76 class UI_CHROMEOS_EXPORT TouchExplorationController : |
34 public ui::EventRewriter { | 77 public ui::EventRewriter { |
35 public: | 78 public: |
36 explicit TouchExplorationController(aura::Window* root_window); | 79 explicit TouchExplorationController(aura::Window* root_window); |
37 virtual ~TouchExplorationController(); | 80 virtual ~TouchExplorationController(); |
38 | 81 |
| 82 void CallTapTimerNowForTesting(); |
| 83 void SetEventHandlerForTesting(ui::EventHandler* event_handler_for_testing); |
| 84 bool IsInNoFingersDownStateForTesting() const; |
| 85 |
39 private: | 86 private: |
40 scoped_ptr<ui::Event> CreateMouseMoveEvent(const gfx::PointF& location, | |
41 int flags); | |
42 | |
43 void EnterTouchToMouseMode(); | |
44 | |
45 // Overridden from ui::EventRewriter | 87 // Overridden from ui::EventRewriter |
46 virtual ui::EventRewriteStatus RewriteEvent( | 88 virtual ui::EventRewriteStatus RewriteEvent( |
47 const ui::Event& event, scoped_ptr<ui::Event>* rewritten_event) OVERRIDE; | 89 const ui::Event& event, scoped_ptr<ui::Event>* rewritten_event) OVERRIDE; |
48 virtual ui::EventRewriteStatus NextDispatchEvent( | 90 virtual ui::EventRewriteStatus NextDispatchEvent( |
49 const ui::Event& last_event, scoped_ptr<ui::Event>* new_event) OVERRIDE; | 91 const ui::Event& last_event, scoped_ptr<ui::Event>* new_event) OVERRIDE; |
50 | 92 |
| 93 // Event handlers based on the current state - see State, below. |
| 94 ui::EventRewriteStatus InNoFingersDown( |
| 95 const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event); |
| 96 ui::EventRewriteStatus InSingleTapPressed( |
| 97 const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event); |
| 98 ui::EventRewriteStatus InSingleTapReleased( |
| 99 const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event); |
| 100 ui::EventRewriteStatus InDoubleTapPressed( |
| 101 const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event); |
| 102 ui::EventRewriteStatus InTouchExploration( |
| 103 const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event); |
| 104 ui::EventRewriteStatus InPassthroughMinusOne( |
| 105 const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event); |
| 106 |
| 107 // This timer is started every time we get the first press event, and |
| 108 // it fires after the double-click timeout elapses (300 ms by default). |
| 109 // If the user taps and releases within 300 ms and doesn't press again, |
| 110 // we treat that as a single mouse move (touch exploration) event. |
| 111 void OnTapTimerFired(); |
| 112 |
| 113 // Dispatch a new event outside of the event rewriting flow. |
| 114 void DispatchEvent(ui::Event* event); |
| 115 |
| 116 scoped_ptr<ui::Event> CreateMouseMoveEvent(const gfx::PointF& location, |
| 117 int flags); |
| 118 |
| 119 void EnterTouchToMouseMode(); |
| 120 |
| 121 // Set the state to NO_FINGERS_DOWN and reset any other fields to their |
| 122 // default value. |
| 123 void ResetToNoFingersDown(); |
| 124 |
| 125 enum State { |
| 126 // No fingers are down and no events are pending. |
| 127 NO_FINGERS_DOWN, |
| 128 |
| 129 // A single finger is down, but we're not yet sure if this is going |
| 130 // to be touch exploration or something else. |
| 131 SINGLE_TAP_PRESSED, |
| 132 |
| 133 // The user pressed and released a single finger - a tap - but we have |
| 134 // to wait until the end of the grace period to allow the user to tap the |
| 135 // second time. If the second tap doesn't occurs within the grace period, |
| 136 // we dispatch a mouse move at the location of the first tap. |
| 137 SINGLE_TAP_RELEASED, |
| 138 |
| 139 // The user tapped once, and before the grace period expired, pressed |
| 140 // one finger down to begin a double-tap, but has not released it yet. |
| 141 DOUBLE_TAP_PRESSED, |
| 142 |
| 143 // We're in touch exploration mode. Anything other than the first finger |
| 144 // is ignored, and movements of the first finger are rewritten as mouse |
| 145 // move events. This mode is entered if a single finger is pressed and |
| 146 // after the grace period the user hasn't added a second finger or |
| 147 // moved the finger outside of the slop region. We'll stay in this |
| 148 // mode until all fingers are lifted. |
| 149 TOUCH_EXPLORATION, |
| 150 |
| 151 // The user placed two or more fingers down within the grace period. |
| 152 // We're now in passthrough mode until all fingers are lifted. Initially |
| 153 // the first finger is ignored and other fingers are passed through |
| 154 // as-is. If a finger other than the initial one is the first to be |
| 155 // released, we rewrite the first finger with the touch id of the finger |
| 156 // that was released, from now on. The motivation for this is that if |
| 157 // the user starts a scroll with 2 fingers, they can release either one |
| 158 // and continue the scrolling. |
| 159 PASSTHROUGH_MINUS_ONE, |
| 160 }; |
| 161 |
| 162 aura::Window* root_window_; |
| 163 |
51 // A set of touch ids for fingers currently touching the screen. | 164 // A set of touch ids for fingers currently touching the screen. |
52 std::vector<int> touch_ids_; | 165 std::vector<int> current_touch_ids_; |
53 | 166 |
54 // Map of touch ids to their last known location. | 167 // Map of touch ids to their last known location. |
55 std::map<int, gfx::PointF> touch_locations_; | 168 std::map<int, gfx::PointF> touch_locations_; |
56 | 169 |
57 // Initialized from RewriteEvent() and dispatched in NextDispatchEvent(). | 170 // The touch id that any events on the initial finger should be rewritten |
58 scoped_ptr<ui::Event> next_dispatch_event_; | 171 // as in passthrough-minus-one mode. If kTouchIdUnassigned, events on the |
| 172 // initial finger are discarded. If kTouchIdNone, the initial finger |
| 173 // has been released and no more rewriting will be done. |
| 174 int initial_touch_id_passthrough_mapping_; |
59 | 175 |
60 aura::Window* root_window_; | 176 // The current state. |
| 177 State state_; |
| 178 |
| 179 // A copy of the event from the initial touch press. |
| 180 scoped_ptr<ui::TouchEvent> initial_press_; |
| 181 |
| 182 // The last location where we synthesized a mouse move event. |
| 183 // When the user double-taps, we send the passed-through tap here. |
| 184 gfx::PointF last_touch_exploration_location_; |
| 185 |
| 186 // A timer to fire the mouse move event after the double-tap delay. |
| 187 base::OneShotTimer<TouchExplorationController> tap_timer_; |
| 188 |
| 189 // For testing only, an event handler to use for generated events |
| 190 // outside of the normal event rewriting flow. |
| 191 ui::EventHandler* event_handler_for_testing_; |
| 192 |
| 193 // A default gesture detector config, so we can share the same |
| 194 // timeout and pixel slop constants. |
| 195 ui::GestureDetector::Config gesture_detector_config_; |
61 | 196 |
62 DISALLOW_COPY_AND_ASSIGN(TouchExplorationController); | 197 DISALLOW_COPY_AND_ASSIGN(TouchExplorationController); |
63 }; | 198 }; |
64 | 199 |
65 } // namespace ui | 200 } // namespace ui |
66 | 201 |
67 #endif // UI_CHROMEOS_TOUCH_EXPLORATION_CONTROLLER_H_ | 202 #endif // UI_CHROMEOS_TOUCH_EXPLORATION_CONTROLLER_H_ |
OLD | NEW |