Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(20)

Side by Side Diff: ui/chromeos/touch_exploration_controller.cc

Issue 410783002: Corner Passthrough for Accessibility (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@side-gestures
Patch Set: Added tests and Rebase off Master Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 #include "ui/chromeos/touch_exploration_controller.h" 5 #include "ui/chromeos/touch_exploration_controller.h"
6 6
7 #include "base/strings/string_number_conversions.h" 7 #include "base/strings/string_number_conversions.h"
8 #include "ui/aura/client/cursor_client.h" 8 #include "ui/aura/client/cursor_client.h"
9 #include "ui/aura/window.h" 9 #include "ui/aura/window.h"
10 #include "ui/aura/window_event_dispatcher.h" 10 #include "ui/aura/window_event_dispatcher.h"
11 #include "ui/aura/window_tree_host.h" 11 #include "ui/aura/window_tree_host.h"
12 #include "ui/events/event.h" 12 #include "ui/events/event.h"
13 #include "ui/events/event_processor.h" 13 #include "ui/events/event_processor.h"
14 #include "ui/gfx/geometry/rect.h" 14 #include "ui/gfx/geometry/rect.h"
15 15
16 #define VLOG_STATE() if (VLOG_IS_ON(0)) VlogState(__func__) 16 #define VLOG_STATE() if (VLOG_IS_ON(0)) VlogState(__func__)
17 #define VLOG_EVENT(event) if (VLOG_IS_ON(0)) VlogEvent(event, __func__) 17 #define VLOG_EVENT(event) if (VLOG_IS_ON(0)) VlogEvent(event, __func__)
18 18
19 namespace ui { 19 namespace ui {
20 20
21 namespace { 21 namespace {
22 22
23 // Delay between adjustment sounds. 23 // Delay between adjustment sounds.
24 const base::TimeDelta kSoundDelay = base::TimeDelta::FromMilliseconds(150); 24 const base::TimeDelta kSoundDelay = base::TimeDelta::FromMilliseconds(150);
25 25
26 // Delay before corner passthrough activates.
27 const base::TimeDelta kCornerPassthroughDelay =
28 base::TimeDelta::FromMilliseconds(500);
29
26 // In ChromeOS, VKEY_LWIN is synonymous for the search key. 30 // In ChromeOS, VKEY_LWIN is synonymous for the search key.
27 const ui::KeyboardCode kChromeOSSearchKey = ui::VKEY_LWIN; 31 const ui::KeyboardCode kChromeOSSearchKey = ui::VKEY_LWIN;
28 } // namespace 32 } // namespace
29 33
30 TouchExplorationController::TouchExplorationController( 34 TouchExplorationController::TouchExplorationController(
31 aura::Window* root_window, 35 aura::Window* root_window,
32 TouchExplorationControllerDelegate* delegate) 36 TouchExplorationControllerDelegate* delegate)
33 : root_window_(root_window), 37 : root_window_(root_window),
34 delegate_(delegate), 38 delegate_(delegate),
35 state_(NO_FINGERS_DOWN), 39 state_(NO_FINGERS_DOWN),
36 event_handler_for_testing_(NULL), 40 event_handler_for_testing_(NULL),
37 gesture_provider_(this), 41 gesture_provider_(this),
38 prev_state_(NO_FINGERS_DOWN), 42 prev_state_(NO_FINGERS_DOWN),
39 VLOG_on_(true) { 43 VLOG_on_(true),
44 waiting_for_corner_passthrough_(false) {
40 CHECK(root_window); 45 CHECK(root_window);
41 root_window->GetHost()->GetEventSource()->AddEventRewriter(this); 46 root_window->GetHost()->GetEventSource()->AddEventRewriter(this);
42 } 47 }
43 48
44 TouchExplorationController::~TouchExplorationController() { 49 TouchExplorationController::~TouchExplorationController() {
45 root_window_->GetHost()->GetEventSource()->RemoveEventRewriter(this); 50 root_window_->GetHost()->GetEventSource()->RemoveEventRewriter(this);
46 } 51 }
47 52
48 ui::EventRewriteStatus TouchExplorationController::RewriteEvent( 53 ui::EventRewriteStatus TouchExplorationController::RewriteEvent(
49 const ui::Event& event, 54 const ui::Event& event,
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
125 case DOUBLE_TAP_PRESSED: 130 case DOUBLE_TAP_PRESSED:
126 return InDoubleTapPressed(touch_event, rewritten_event); 131 return InDoubleTapPressed(touch_event, rewritten_event);
127 case TOUCH_EXPLORATION: 132 case TOUCH_EXPLORATION:
128 return InTouchExploration(touch_event, rewritten_event); 133 return InTouchExploration(touch_event, rewritten_event);
129 case GESTURE_IN_PROGRESS: 134 case GESTURE_IN_PROGRESS:
130 return InGestureInProgress(touch_event, rewritten_event); 135 return InGestureInProgress(touch_event, rewritten_event);
131 case TOUCH_EXPLORE_SECOND_PRESS: 136 case TOUCH_EXPLORE_SECOND_PRESS:
132 return InTouchExploreSecondPress(touch_event, rewritten_event); 137 return InTouchExploreSecondPress(touch_event, rewritten_event);
133 case TWO_TO_ONE_FINGER: 138 case TWO_TO_ONE_FINGER:
134 return InTwoToOneFinger(touch_event, rewritten_event); 139 return InTwoToOneFinger(touch_event, rewritten_event);
140 case CORNER_PASSTHROUGH:
141 return InCornerPassthrough(touch_event, rewritten_event);
135 case PASSTHROUGH: 142 case PASSTHROUGH:
136 return InPassthrough(touch_event, rewritten_event); 143 return InPassthrough(touch_event, rewritten_event);
137 case WAIT_FOR_RELEASE: 144 case WAIT_FOR_RELEASE:
138 return InWaitForRelease(touch_event, rewritten_event); 145 return InWaitForRelease(touch_event, rewritten_event);
139 case SLIDE_GESTURE: 146 case SLIDE_GESTURE:
140 return InSlideGesture(touch_event, rewritten_event); 147 return InSlideGesture(touch_event, rewritten_event);
141 } 148 }
142 NOTREACHED(); 149 NOTREACHED();
143 return ui::EVENT_REWRITE_CONTINUE; 150 return ui::EVENT_REWRITE_CONTINUE;
144 } 151 }
145 152
146 ui::EventRewriteStatus TouchExplorationController::NextDispatchEvent( 153 ui::EventRewriteStatus TouchExplorationController::NextDispatchEvent(
147 const ui::Event& last_event, scoped_ptr<ui::Event>* new_event) { 154 const ui::Event& last_event, scoped_ptr<ui::Event>* new_event) {
148 NOTREACHED(); 155 NOTREACHED();
149 return ui::EVENT_REWRITE_CONTINUE; 156 return ui::EVENT_REWRITE_CONTINUE;
150 } 157 }
151 158
152 ui::EventRewriteStatus TouchExplorationController::InNoFingersDown( 159 ui::EventRewriteStatus TouchExplorationController::InNoFingersDown(
153 const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event) { 160 const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event) {
154 const ui::EventType type = event.type(); 161 const ui::EventType type = event.type();
155 if (type == ui::ET_TOUCH_PRESSED) { 162 if (type != ui::ET_TOUCH_PRESSED) {
156 initial_press_.reset(new TouchEvent(event)); 163 NOTREACHED() << "Unexpected event type received: " << event.name();
157 last_unused_finger_event_.reset(new TouchEvent(event)); 164 return ui::EVENT_REWRITE_CONTINUE;
158 tap_timer_.Start(FROM_HERE,
159 gesture_detector_config_.double_tap_timeout,
160 this,
161 &TouchExplorationController::OnTapTimerFired);
162 gesture_provider_.OnTouchEvent(event);
163 gesture_provider_.OnTouchEventAck(false);
164 ProcessGestureEvents();
165 state_ = SINGLE_TAP_PRESSED;
166 VLOG_STATE();
167 return ui::EVENT_REWRITE_DISCARD;
168 } 165 }
169 NOTREACHED() << "Unexpected event type received: " << event.name();; 166 int location = FindEdgesWithinBounds(event.location(), kSlopDistanceFromEdge);
170 return ui::EVENT_REWRITE_CONTINUE; 167 base::TimeDelta timeout;
168
169 // If the press was at a corner, the user might go into corner passthrough
170 // instead.
171 bool in_a_bottom_corner =
172 (BOTTOM_LEFT_CORNER == location) || (BOTTOM_RIGHT_CORNER == location);
173 if (in_a_bottom_corner) {
174 VLOG(0) << "Location: " << location;
175 timeout = kCornerPassthroughDelay;
176 waiting_for_corner_passthrough_ = true;
177 } else {
178 timeout = gesture_detector_config_.double_tap_timeout;
179 waiting_for_corner_passthrough_ = false;
180 }
181
182 initial_press_.reset(new TouchEvent(event));
183 last_unused_finger_event_.reset(new TouchEvent(event));
184 tap_timer_.Start(
185 FROM_HERE, timeout, this, &TouchExplorationController::OnTapTimerFired);
186 gesture_provider_.OnTouchEvent(event);
187 gesture_provider_.OnTouchEventAck(false);
188 ProcessGestureEvents();
189 state_ = SINGLE_TAP_PRESSED;
190 VLOG_STATE();
191 return ui::EVENT_REWRITE_DISCARD;
171 } 192 }
172 193
173 ui::EventRewriteStatus TouchExplorationController::InSingleTapPressed( 194 ui::EventRewriteStatus TouchExplorationController::InSingleTapPressed(
174 const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event) { 195 const ui::TouchEvent& event, scoped_ptr<ui::Event>* rewritten_event) {
175 const ui::EventType type = event.type(); 196 const ui::EventType type = event.type();
176 197
198 int location = FindEdgesWithinBounds(event.location(), kMaxDistanceFromEdge);
199 bool in_a_bottom_corner =
200 (location == BOTTOM_LEFT_CORNER) || (location == BOTTOM_RIGHT_CORNER);
201 // If the event is from the initial press and the location is no longer in the
202 // corner, then we are not waiting for a corner passthrough anymore.
203 if (event.touch_id() == initial_press_->touch_id() && in_a_bottom_corner) {
aboxhall 2014/07/24 18:26:34 I think |in_a_bottom_corner| should be negated...
lisayin 2014/07/25 20:11:57 Opps...
204 waiting_for_corner_passthrough_ = false;
205 // If the initial press was in a corner, then more than the double tap
206 // timeout could have elapsed.
207 if (tap_timer_.IsRunning() &&
208 event.time_stamp() - initial_press_->time_stamp() >
209 gesture_detector_config_.double_tap_timeout) {
210 tap_timer_.Stop();
211 OnTapTimerFired();
212 return EVENT_REWRITE_DISCARD;
213 }
214 }
215
177 if (type == ui::ET_TOUCH_PRESSED) { 216 if (type == ui::ET_TOUCH_PRESSED) {
178 // Adding a second finger within the timeout period switches to 217 // Adding a second finger within the timeout period switches to
179 // passing through every event from the second finger and none form the 218 // passing through every event from the second finger and none form the
180 // first. The event from the first finger is still saved in initial_press_. 219 // first. The event from the first finger is still saved in initial_press_.
181 state_ = TWO_TO_ONE_FINGER; 220 state_ = TWO_TO_ONE_FINGER;
182 last_two_to_one_.reset(new TouchEvent(event)); 221 last_two_to_one_.reset(new TouchEvent(event));
183 rewritten_event->reset(new ui::TouchEvent(ui::ET_TOUCH_PRESSED, 222 rewritten_event->reset(new ui::TouchEvent(ui::ET_TOUCH_PRESSED,
184 event.location(), 223 event.location(),
185 event.touch_id(), 224 event.touch_id(),
186 event.time_stamp())); 225 event.time_stamp()));
(...skipping 208 matching lines...) Expand 10 before | Expand all | Expand 10 after
395 event.time_stamp())); 434 event.time_stamp()));
396 (*rewritten_event)->set_flags(event.flags()); 435 (*rewritten_event)->set_flags(event.flags());
397 state_ = WAIT_FOR_RELEASE; 436 state_ = WAIT_FOR_RELEASE;
398 return ui::EVENT_REWRITE_REWRITTEN; 437 return ui::EVENT_REWRITE_REWRITTEN;
399 } 438 }
400 } else if (type == ui::ET_TOUCH_PRESSED) { 439 } else if (type == ui::ET_TOUCH_PRESSED) {
401 DCHECK(current_touch_ids_.size() == 3); 440 DCHECK(current_touch_ids_.size() == 3);
402 // If a third finger is pressed, we are now going into passthrough mode 441 // If a third finger is pressed, we are now going into passthrough mode
403 // and now need to dispatch the first finger into a press, as well as the 442 // and now need to dispatch the first finger into a press, as well as the
404 // recent press. 443 // recent press.
405 if (current_touch_ids_.size() == 3){ 444 if (current_touch_ids_.size() == 3) {
406 state_ = PASSTHROUGH; 445 state_ = PASSTHROUGH;
407 scoped_ptr<ui::TouchEvent> first_finger_press; 446 scoped_ptr<ui::TouchEvent> first_finger_press;
408 first_finger_press.reset( 447 first_finger_press.reset(
409 new ui::TouchEvent(ui::ET_TOUCH_PRESSED, 448 new ui::TouchEvent(ui::ET_TOUCH_PRESSED,
410 last_unused_finger_event_->location(), 449 last_unused_finger_event_->location(),
411 last_unused_finger_event_->touch_id(), 450 last_unused_finger_event_->touch_id(),
412 event.time_stamp())); 451 event.time_stamp()));
413 DispatchEvent(first_finger_press.get()); 452 DispatchEvent(first_finger_press.get());
414 rewritten_event->reset(new ui::TouchEvent(ui::ET_TOUCH_PRESSED, 453 rewritten_event->reset(new ui::TouchEvent(ui::ET_TOUCH_PRESSED,
415 event.location(), 454 event.location(),
(...skipping 18 matching lines...) Expand all
434 event.touch_id(), 473 event.touch_id(),
435 event.time_stamp())); 474 event.time_stamp()));
436 (*rewritten_event)->set_flags(event.flags()); 475 (*rewritten_event)->set_flags(event.flags());
437 return ui::EVENT_REWRITE_REWRITTEN; 476 return ui::EVENT_REWRITE_REWRITTEN;
438 } 477 }
439 } 478 }
440 NOTREACHED() << "Unexpected event type received: " << event.name(); 479 NOTREACHED() << "Unexpected event type received: " << event.name();
441 return ui::EVENT_REWRITE_CONTINUE; 480 return ui::EVENT_REWRITE_CONTINUE;
442 } 481 }
443 482
483 ui::EventRewriteStatus TouchExplorationController::InCornerPassthrough(
484 const ui::TouchEvent& event,
485 scoped_ptr<ui::Event>* rewritten_event) {
486 ui::EventType type = event.type();
487 if (current_touch_ids_.size() == 0) {
488 ResetToNoFingersDown();
489 }
490
491 if (event.touch_id() == initial_press_->touch_id()) {
aboxhall 2014/07/24 18:26:34 Some comments explaining this block would be helpf
lisayin 2014/07/25 20:11:57 Done.
492 int edge = FindEdgesWithinBounds(event.location(), kSlopDistanceFromEdge);
aboxhall 2014/07/24 18:26:34 suggestion: s/edge/edges/
lisayin 2014/07/25 20:11:57 Done.
493 bool in_a_bottom_corner = (edge == BOTTOM_LEFT_CORNER) ||
494 (edge == BOTTOM_RIGHT_CORNER);
495 if (type == ui::ET_TOUCH_MOVED && in_a_bottom_corner)
496 return ui::EVENT_REWRITE_DISCARD;
497
498 waiting_for_corner_passthrough_ = false;
499 state_ = WAIT_FOR_RELEASE;
500 return ui::EVENT_REWRITE_DISCARD;
501 }
502
503 rewritten_event->reset(new ui::TouchEvent(
504 type, event.location(), event.touch_id(), event.time_stamp()));
505 (*rewritten_event)->set_flags(event.flags());
506
507 return ui::EVENT_REWRITE_REWRITTEN;
508 }
509
444 ui::EventRewriteStatus TouchExplorationController::InPassthrough( 510 ui::EventRewriteStatus TouchExplorationController::InPassthrough(
445 const ui::TouchEvent& event, 511 const ui::TouchEvent& event,
446 scoped_ptr<ui::Event>* rewritten_event) { 512 scoped_ptr<ui::Event>* rewritten_event) {
447 ui::EventType type = event.type(); 513 ui::EventType type = event.type();
448 514
449 if (!(type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED || 515 if (!(type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED ||
450 type == ui::ET_TOUCH_MOVED || type == ui::ET_TOUCH_PRESSED)) { 516 type == ui::ET_TOUCH_MOVED || type == ui::ET_TOUCH_PRESSED)) {
451 NOTREACHED() << "Unexpected event type received: " << event.name(); 517 NOTREACHED() << "Unexpected event type received: " << event.name();
452 return ui::EVENT_REWRITE_CONTINUE; 518 return ui::EVENT_REWRITE_CONTINUE;
453 } 519 }
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after
579 if (current_touch_ids_.size() == 0) 645 if (current_touch_ids_.size() == 0)
580 ResetToNoFingersDown(); 646 ResetToNoFingersDown();
581 return ui::EVENT_REWRITE_DISCARD; 647 return ui::EVENT_REWRITE_DISCARD;
582 } 648 }
583 649
584 ProcessGestureEvents(); 650 ProcessGestureEvents();
585 return ui::EVENT_REWRITE_DISCARD; 651 return ui::EVENT_REWRITE_DISCARD;
586 } 652 }
587 653
588 void TouchExplorationController::OnTapTimerFired() { 654 void TouchExplorationController::OnTapTimerFired() {
655 if (waiting_for_corner_passthrough_) {
656 delegate_->PlayEarCon();
657 delete gesture_provider_.GetAndResetPendingGestures();
658 state_ = CORNER_PASSTHROUGH;
659 VLOG_STATE();
660 return;
661 }
662
589 switch (state_) { 663 switch (state_) {
590 case SINGLE_TAP_RELEASED: 664 case SINGLE_TAP_RELEASED:
591 ResetToNoFingersDown(); 665 ResetToNoFingersDown();
592 break; 666 break;
593 case TOUCH_EXPLORE_RELEASED: 667 case TOUCH_EXPLORE_RELEASED:
594 ResetToNoFingersDown(); 668 ResetToNoFingersDown();
595 last_touch_exploration_.reset(new TouchEvent(*initial_press_)); 669 last_touch_exploration_.reset(new TouchEvent(*initial_press_));
596 return; 670 return;
597 case SINGLE_TAP_PRESSED: 671 case SINGLE_TAP_PRESSED:
598 case GESTURE_IN_PROGRESS: 672 case GESTURE_IN_PROGRESS:
(...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after
798 void TouchExplorationController::EnterTouchToMouseMode() { 872 void TouchExplorationController::EnterTouchToMouseMode() {
799 aura::client::CursorClient* cursor_client = 873 aura::client::CursorClient* cursor_client =
800 aura::client::GetCursorClient(root_window_); 874 aura::client::GetCursorClient(root_window_);
801 if (cursor_client && !cursor_client->IsMouseEventsEnabled()) 875 if (cursor_client && !cursor_client->IsMouseEventsEnabled())
802 cursor_client->EnableMouseEvents(); 876 cursor_client->EnableMouseEvents();
803 if (cursor_client && cursor_client->IsCursorVisible()) 877 if (cursor_client && cursor_client->IsCursorVisible())
804 cursor_client->HideCursor(); 878 cursor_client->HideCursor();
805 } 879 }
806 880
807 void TouchExplorationController::ResetToNoFingersDown() { 881 void TouchExplorationController::ResetToNoFingersDown() {
882 waiting_for_corner_passthrough_ = false;
808 ProcessGestureEvents(); 883 ProcessGestureEvents();
809 if (sound_timer_.IsRunning()) 884 if (sound_timer_.IsRunning())
810 sound_timer_.Stop(); 885 sound_timer_.Stop();
811 state_ = NO_FINGERS_DOWN; 886 state_ = NO_FINGERS_DOWN;
812 VLOG_STATE(); 887 VLOG_STATE();
813 if (tap_timer_.IsRunning()) 888 if (tap_timer_.IsRunning())
814 tap_timer_.Stop(); 889 tap_timer_.Stop();
815 } 890 }
816 891
817 void TouchExplorationController::VlogState(const char* function_name) { 892 void TouchExplorationController::VlogState(const char* function_name) {
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
868 case DOUBLE_TAP_PRESSED: 943 case DOUBLE_TAP_PRESSED:
869 return "DOUBLE_TAP_PRESSED"; 944 return "DOUBLE_TAP_PRESSED";
870 case TOUCH_EXPLORATION: 945 case TOUCH_EXPLORATION:
871 return "TOUCH_EXPLORATION"; 946 return "TOUCH_EXPLORATION";
872 case GESTURE_IN_PROGRESS: 947 case GESTURE_IN_PROGRESS:
873 return "GESTURE_IN_PROGRESS"; 948 return "GESTURE_IN_PROGRESS";
874 case TOUCH_EXPLORE_SECOND_PRESS: 949 case TOUCH_EXPLORE_SECOND_PRESS:
875 return "TOUCH_EXPLORE_SECOND_PRESS"; 950 return "TOUCH_EXPLORE_SECOND_PRESS";
876 case TWO_TO_ONE_FINGER: 951 case TWO_TO_ONE_FINGER:
877 return "TWO_TO_ONE_FINGER"; 952 return "TWO_TO_ONE_FINGER";
953 case CORNER_PASSTHROUGH:
954 return "CORNER_PASSTHROUGH";
878 case PASSTHROUGH: 955 case PASSTHROUGH:
879 return "PASSTHROUGH"; 956 return "PASSTHROUGH";
880 case WAIT_FOR_RELEASE: 957 case WAIT_FOR_RELEASE:
881 return "WAIT_FOR_RELEASE"; 958 return "WAIT_FOR_RELEASE";
882 case SLIDE_GESTURE: 959 case SLIDE_GESTURE:
883 return "SLIDE_GESTURE"; 960 return "SLIDE_GESTURE";
884 } 961 }
885 return "Not a state"; 962 return "Not a state";
886 } 963 }
887 964
888 } // namespace ui 965 } // namespace ui
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698