| Index: ui/chromeos/touch_exploration_controller.cc
|
| diff --git a/ui/chromeos/touch_exploration_controller.cc b/ui/chromeos/touch_exploration_controller.cc
|
| index 287e59fd6d39626498b31342d6cde3915ff92020..a8c54ef03b420b5c4140356b261f2b3e55721d56 100644
|
| --- a/ui/chromeos/touch_exploration_controller.cc
|
| +++ b/ui/chromeos/touch_exploration_controller.cc
|
| @@ -4,7 +4,6 @@
|
|
|
| #include "ui/chromeos/touch_exploration_controller.h"
|
|
|
| -#include "base/logging.h"
|
| #include "base/strings/string_number_conversions.h"
|
| #include "ui/aura/client/cursor_client.h"
|
| #include "ui/aura/window.h"
|
| @@ -12,6 +11,7 @@
|
| #include "ui/aura/window_tree_host.h"
|
| #include "ui/events/event.h"
|
| #include "ui/events/event_processor.h"
|
| +#include "ui/gfx/geometry/rect.h"
|
|
|
| #define VLOG_STATE() if (VLOG_IS_ON(0)) VlogState(__func__)
|
| #define VLOG_EVENT(event) if (VLOG_IS_ON(0)) VlogEvent(event, __func__)
|
| @@ -19,13 +19,29 @@
|
| namespace ui {
|
|
|
| namespace {
|
| +
|
| +// Delay between adjustment sounds.
|
| +const base::TimeDelta kSoundDelay = base::TimeDelta::FromMilliseconds(150);
|
| +
|
| +const float kLeavingScreenEdge = 6;
|
| +
|
| +// Swipe/scroll gestures within these bounds (in dips) will change preset
|
| +// settings.
|
| +const float kMaxDistanceFromEdge = 75;
|
| +
|
| +// After a slide gesture has been triggered, if the finger is still within these
|
| +// bounds, the preset settings will still change.
|
| +const float kSlopDistanceFromEdge = kMaxDistanceFromEdge + 40;
|
| +
|
| // In ChromeOS, VKEY_LWIN is synonymous for the search key.
|
| const ui::KeyboardCode kChromeOSSearchKey = ui::VKEY_LWIN;
|
| } // namespace
|
|
|
| TouchExplorationController::TouchExplorationController(
|
| - aura::Window* root_window)
|
| + aura::Window* root_window,
|
| + TouchExplorationControllerDelegate* delegate)
|
| : root_window_(root_window),
|
| + delegate_(delegate),
|
| state_(NO_FINGERS_DOWN),
|
| event_handler_for_testing_(NULL),
|
| gesture_provider_(this),
|
| @@ -67,7 +83,15 @@ bool TouchExplorationController::IsInGestureInProgressStateForTesting() const {
|
| }
|
|
|
| void TouchExplorationController::SuppressVLOGsForTesting(bool suppress) {
|
| - VLOG_on_ = !suppress;
|
| + VLOG_on_ = !suppress;
|
| +}
|
| +
|
| +gfx::Rect TouchExplorationController::BoundsOfRootWindowInDIPForTesting() {
|
| + return root_window_->GetBoundsInScreen();
|
| +}
|
| +
|
| +bool TouchExplorationController::IsInSlideGestureStateForTesting() const {
|
| + return state_ == SLIDE_GESTURE;
|
| }
|
|
|
| ui::EventRewriteStatus TouchExplorationController::RewriteEvent(
|
| @@ -108,6 +132,16 @@ ui::EventRewriteStatus TouchExplorationController::RewriteEvent(
|
| current_touch_ids_.push_back(touch_id);
|
| touch_locations_.insert(std::pair<int, gfx::PointF>(touch_id, location));
|
| } else if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
|
| + // If the release is too close to the edge.
|
| + TouchEvent touch_event = static_cast<const TouchEvent&>(event);
|
| + if (WithinBoundsOfEdge(touch_event.location(), kLeavingScreenEdge) !=
|
| + SCREEN_CENTER) {
|
| + if (current_touch_ids_.size() == 0)
|
| + ResetToNoFingersDown();
|
| + else
|
| + state_ = WAIT_FOR_RELEASE;
|
| + }
|
| +
|
| std::vector<int>::iterator it = std::find(
|
| current_touch_ids_.begin(), current_touch_ids_.end(), touch_id);
|
|
|
| @@ -152,6 +186,8 @@ ui::EventRewriteStatus TouchExplorationController::RewriteEvent(
|
| return InPassthrough(touch_event, rewritten_event);
|
| case WAIT_FOR_RELEASE:
|
| return InWaitForRelease(touch_event, rewritten_event);
|
| + case SLIDE_GESTURE:
|
| + return InSlideGesture(touch_event, rewritten_event);
|
| }
|
| NOTREACHED();
|
| return ui::EVENT_REWRITE_CONTINUE;
|
| @@ -222,6 +258,14 @@ ui::EventRewriteStatus TouchExplorationController::InSingleTapPressed(
|
| << "\n Minimum swipe velocity: "
|
| << gesture_detector_config_.minimum_swipe_velocity;
|
|
|
| + // Change to slide gesture if the slide occurred at the right edge.
|
| + int where = WithinBoundsOfEdge(event.location(), kMaxDistanceFromEdge);
|
| + if ((where | RIGHT_EDGE) == where) {
|
| + state_ = SLIDE_GESTURE;
|
| + VLOG_STATE();
|
| + return InSlideGesture(event, rewritten_event);
|
| + }
|
| +
|
| // If the user moves fast enough from the initial touch location, start
|
| // gesture detection. Otherwise, jump to the touch exploration mode early.
|
| if (velocity > gesture_detector_config_.minimum_swipe_velocity) {
|
| @@ -502,6 +546,7 @@ ui::EventRewriteStatus TouchExplorationController::InTouchExploreSecondPress(
|
| initial_press_->touch_id(),
|
| event.time_stamp()));
|
| (*rewritten_event)->set_flags(event.flags());
|
| + EnterTouchToMouseMode();
|
| state_ = TOUCH_EXPLORATION;
|
| EnterTouchToMouseMode();
|
| VLOG_STATE();
|
| @@ -528,6 +573,77 @@ ui::EventRewriteStatus TouchExplorationController::InWaitForRelease(
|
| return EVENT_REWRITE_DISCARD;
|
| }
|
|
|
| +void TouchExplorationController::PlaySoundForTimer() {
|
| + delegate_->PlayVolumeAdjustSound();
|
| +}
|
| +
|
| +ui::EventRewriteStatus TouchExplorationController::InSlideGesture(
|
| + const ui::TouchEvent& event,
|
| + scoped_ptr<ui::Event>* rewritten_event) {
|
| + ui::EventType type = event.type();
|
| + // If additional fingers are added before a swipe gesture has been registered,
|
| + // then go into two-to-one passthrough.
|
| + if (type == ui::ET_TOUCH_PRESSED ||
|
| + event.touch_id() != initial_press_->touch_id()) {
|
| + if (tap_timer_.IsRunning())
|
| + tap_timer_.Stop();
|
| + if (sound_timer_.IsRunning())
|
| + sound_timer_.Stop();
|
| + // Discard any pending gestures.
|
| + ignore_result(gesture_provider_.GetAndResetPendingGestures());
|
| + state_ = TWO_TO_ONE_FINGER;
|
| + last_two_to_one_.reset(new TouchEvent(event));
|
| + rewritten_event->reset(new ui::TouchEvent(ui::ET_TOUCH_PRESSED,
|
| + event.location(),
|
| + event.touch_id(),
|
| + event.time_stamp()));
|
| + (*rewritten_event)->set_flags(event.flags());
|
| + return EVENT_REWRITE_REWRITTEN;
|
| + }
|
| +
|
| + VLOG(0) << "Location " << event.location().ToString();
|
| + // Allows user to return to the edge to adjust the sound if they have left the
|
| + // boundaries.
|
| + int where = WithinBoundsOfEdge(event.location(), kSlopDistanceFromEdge);
|
| + if (((where | RIGHT_EDGE) != where) && (type != ui::ET_TOUCH_RELEASED)) {
|
| + if (sound_timer_.IsRunning()) {
|
| + sound_timer_.Stop();
|
| + }
|
| + return EVENT_REWRITE_DISCARD;
|
| + }
|
| +
|
| + // This can occur if the user leaves the screen edge and then returns to it to
|
| + // continue adjusting the sound.
|
| + if (!sound_timer_.IsRunning()) {
|
| + sound_timer_.Start(FROM_HERE,
|
| + kSoundDelay,
|
| + this,
|
| + &ui::TouchExplorationController::PlaySoundForTimer);
|
| + delegate_->PlayVolumeAdjustSound();
|
| + }
|
| + // The timer should not fire when sliding.
|
| + if (tap_timer_.IsRunning())
|
| + tap_timer_.Stop();
|
| +
|
| + // There should not be more than one finger down.
|
| + DCHECK(current_touch_ids_.size() <= 1);
|
| + if (type == ui::ET_TOUCH_MOVED) {
|
| + gesture_provider_.OnTouchEvent(event);
|
| + gesture_provider_.OnTouchEventAck(false);
|
| + }
|
| + if (type == ui::ET_TOUCH_RELEASED || type == ui::ET_TOUCH_CANCELLED) {
|
| + gesture_provider_.OnTouchEvent(event);
|
| + gesture_provider_.OnTouchEventAck(false);
|
| + ignore_result(gesture_provider_.GetAndResetPendingGestures());
|
| + if (current_touch_ids_.size() == 0)
|
| + ResetToNoFingersDown();
|
| + return ui::EVENT_REWRITE_DISCARD;
|
| + }
|
| +
|
| + ProcessGestureEvents();
|
| + return ui::EVENT_REWRITE_DISCARD;
|
| +}
|
| +
|
| void TouchExplorationController::OnTapTimerFired() {
|
| switch (state_) {
|
| case SINGLE_TAP_RELEASED:
|
| @@ -552,7 +668,7 @@ void TouchExplorationController::OnTapTimerFired() {
|
| CreateMouseMoveEvent(initial_press_->location(), initial_press_->flags());
|
| DispatchEvent(mouse_move.get());
|
| last_touch_exploration_.reset(new TouchEvent(*initial_press_));
|
| - }
|
| +}
|
|
|
| void TouchExplorationController::DispatchEvent(ui::Event* event) {
|
| if (event_handler_for_testing_) {
|
| @@ -563,12 +679,14 @@ void TouchExplorationController::DispatchEvent(ui::Event* event) {
|
| root_window_->GetHost()->dispatcher()->OnEventFromSource(event);
|
| }
|
|
|
| -void TouchExplorationController::OnGestureEvent(ui::GestureEvent* gesture) {
|
| +void TouchExplorationController::OnGestureEvent(
|
| + ui::GestureEvent* gesture) {
|
| CHECK(gesture->IsGestureEvent());
|
| + ui::EventType type = gesture->type();
|
| VLOG(0) << " \n Gesture Triggered: " << gesture->name();
|
| - if (gesture->type() == ui::ET_GESTURE_SWIPE) {
|
| - if (tap_timer_.IsRunning())
|
| - tap_timer_.Stop();
|
| + if (type == ui::ET_GESTURE_SWIPE && state_ != SLIDE_GESTURE) {
|
| + VLOG(0) << "Swipe!";
|
| + ignore_result(gesture_provider_.GetAndResetPendingGestures());
|
| OnSwipeEvent(gesture);
|
| return;
|
| }
|
| @@ -581,11 +699,84 @@ void TouchExplorationController::ProcessGestureEvents() {
|
| for (ScopedVector<GestureEvent>::iterator i = gestures->begin();
|
| i != gestures->end();
|
| ++i) {
|
| - OnGestureEvent(*i);
|
| + if (state_ == SLIDE_GESTURE)
|
| + SideSlideControl(*i);
|
| + else
|
| + OnGestureEvent(*i);
|
| }
|
| }
|
| }
|
|
|
| +void TouchExplorationController::SideSlideControl(ui::GestureEvent* gesture) {
|
| + ui::EventType type = gesture->type();
|
| + if (!gesture->IsScrollGestureEvent())
|
| + return;
|
| +
|
| + gfx::Point location = gesture->location();
|
| + VLOG(0) << "Location " << location.ToString();
|
| + root_window_->GetHost()->ConvertPointToNativeScreen(&location);
|
| + if (WithinBoundsOfEdge(location, kSlopDistanceFromEdge) == SCREEN_CENTER) {
|
| + VLOG(0) << "No more Slide gesture ";
|
| + // Discard any pending gestures.
|
| + ignore_result(gesture_provider_.GetAndResetPendingGestures());
|
| + if (current_touch_ids_.size() > 1) {
|
| + state_ = WAIT_FOR_RELEASE;
|
| + } else if (current_touch_ids_.size() == 0) {
|
| + ResetToNoFingersDown();
|
| + } else {
|
| + EnterTouchToMouseMode();
|
| + state_ = TOUCH_EXPLORATION;
|
| + }
|
| + VLOG_STATE();
|
| + return;
|
| + }
|
| +
|
| + if (type == ET_GESTURE_SCROLL_BEGIN) {
|
| + delegate_->PlayVolumeAdjustSound();
|
| + }
|
| +
|
| + if (type == ET_GESTURE_SCROLL_END) {
|
| + if (sound_timer_.IsRunning())
|
| + sound_timer_.Stop();
|
| + delegate_->PlayVolumeAdjustSound();
|
| + }
|
| +
|
| + location = gesture->location();
|
| + int where = WithinBoundsOfEdge(location, kSlopDistanceFromEdge);
|
| +
|
| + // If the user is in the corner of the right side of the screen, the volume
|
| + // will be automatically set to 100% or muted depending on which corner they
|
| + // are in. Otherwise, the user will be able to adjust the volume by sliding
|
| + // their finger along the right side of the screen. Volume is relative to
|
| + // where they are on the right side of the screen.
|
| + if ((where | RIGHT_EDGE) != where)
|
| + return;
|
| +
|
| + if (where == RIGHT_EDGE) {
|
| + location = gesture->location();
|
| + root_window_->GetHost()->ConvertPointFromNativeScreen(&location);
|
| + float volume =
|
| + 100 -
|
| + ((float)location.y() - kMaxDistanceFromEdge) /
|
| + (root_window_->bounds().bottom() - 2 * kMaxDistanceFromEdge) * 100;
|
| + VLOG(0) << "\n Volume = " << volume
|
| + << "\n Location = " << location.ToString()
|
| + << "\n Bounds = " << root_window_->bounds().right();
|
| +
|
| + delegate_->SetOutputLevel(volume);
|
| + return;
|
| + }
|
| + else if ((where | TOP_EDGE) == where) {
|
| + delegate_->SetOutputLevel(100);
|
| + return;
|
| + } else if ((where | BOTTOM_EDGE) == where) {
|
| + delegate_->SetOutputLevel(0);
|
| + return;
|
| + }
|
| + NOTREACHED() << "Invalid location " << where;
|
| +}
|
| +
|
| +
|
| void TouchExplorationController::OnSwipeEvent(ui::GestureEvent* swipe_gesture) {
|
| // A swipe gesture contains details for the direction in which the swipe
|
| // occurred.
|
| @@ -605,6 +796,34 @@ void TouchExplorationController::OnSwipeEvent(ui::GestureEvent* swipe_gesture) {
|
| }
|
| }
|
|
|
| +int TouchExplorationController::WithinBoundsOfEdge(gfx::Point point,
|
| + float bounds) {
|
| + // Since GetBoundsInScreen is in DIPs but p is not, then p needs to be
|
| + // converted.
|
| + root_window_->GetHost()->ConvertPointFromNativeScreen(&point);
|
| + gfx::Rect window= root_window_->GetBoundsInScreen();
|
| +
|
| + float left_edge_limit = window.x() + bounds;
|
| + float right_edge_limit = window.right() - bounds;
|
| + float top_edge_limit = window.y() + bounds;
|
| + float bottom_edge_limit = window.bottom() - bounds;
|
| +
|
| + // Bitwise manipulation in order to determine where on the screen the point
|
| + // lies. If more than one bit is turned on, then it is a corner where the two
|
| + // bit/edges intersect. Otherwise, if no bits are turned on, the point must be
|
| + // in the center of the screen.
|
| + int result = SCREEN_CENTER;
|
| + if (point.x() < left_edge_limit)
|
| + result = result | LEFT_EDGE;
|
| + if (point.x() > right_edge_limit)
|
| + result = result | RIGHT_EDGE;
|
| + if (point.y() < top_edge_limit)
|
| + result = result | TOP_EDGE;
|
| + if (point.y() > bottom_edge_limit)
|
| + result = result | BOTTOM_EDGE;
|
| + return result;
|
| +}
|
| +
|
| void TouchExplorationController::DispatchShiftSearchKeyEvent(
|
| const ui::KeyboardCode direction) {
|
| // In order to activate the shortcut shift+search+<arrow key>
|
| @@ -654,6 +873,9 @@ void TouchExplorationController::EnterTouchToMouseMode() {
|
| }
|
|
|
| void TouchExplorationController::ResetToNoFingersDown() {
|
| + ProcessGestureEvents();
|
| + if (sound_timer_.IsRunning())
|
| + sound_timer_.Stop();
|
| state_ = NO_FINGERS_DOWN;
|
| VLOG_STATE();
|
| if (tap_timer_.IsRunning())
|
| @@ -725,6 +947,8 @@ const char* TouchExplorationController::EnumStateToString(State state) {
|
| return "PASSTHROUGH";
|
| case WAIT_FOR_RELEASE:
|
| return "WAIT_FOR_RELEASE";
|
| + case SLIDE_GESTURE:
|
| + return "SLIDE_GESTURE";
|
| }
|
| return "Not a state";
|
| }
|
|
|