diff options
author | sadrul@chromium.org <sadrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-29 16:05:18 +0000 |
---|---|---|
committer | sadrul@chromium.org <sadrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-29 16:05:18 +0000 |
commit | c57c18bd667a8d4a610244f094a98c3f46d7c0b2 (patch) | |
tree | c231974f80e6d8f40525598131a69cfe0a557291 /ash/wm/gestures | |
parent | bc09dc9034ff4642c7d2a61947a8a9e4a7f70c9d (diff) | |
download | chromium_src-c57c18bd667a8d4a610244f094a98c3f46d7c0b2.zip chromium_src-c57c18bd667a8d4a610244f094a98c3f46d7c0b2.tar.gz chromium_src-c57c18bd667a8d4a610244f094a98c3f46d7c0b2.tar.bz2 |
ash: Move bezel gesture handling into a separate file.
BUG=none
Review URL: https://chromiumcodereview.appspot.com/10890029
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@153891 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ash/wm/gestures')
-rw-r--r-- | ash/wm/gestures/bezel_gesture_handler.cc | 296 | ||||
-rw-r--r-- | ash/wm/gestures/bezel_gesture_handler.h | 120 |
2 files changed, 416 insertions, 0 deletions
diff --git a/ash/wm/gestures/bezel_gesture_handler.cc b/ash/wm/gestures/bezel_gesture_handler.cc new file mode 100644 index 0000000..9008c11 --- /dev/null +++ b/ash/wm/gestures/bezel_gesture_handler.cc @@ -0,0 +1,296 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/wm/gestures/bezel_gesture_handler.h" + +#include "ash/accelerators/accelerator_controller.h" +#include "ash/accelerators/accelerator_table.h" +#include "ash/ash_switches.h" +#include "ash/launcher/launcher.h" +#include "ash/root_window_controller.h" +#include "ash/screen_ash.h" +#include "ash/shell.h" +#include "ash/shell_delegate.h" +#include "ash/shell_window_ids.h" +#include "ash/system/brightness/brightness_control_delegate.h" +#include "ash/system/tray/system_tray_delegate.h" +#include "ash/volume_control_delegate.h" +#include "ash/wm/property_util.h" +#include "ash/wm/window_resizer.h" +#include "ash/wm/window_util.h" +#include "base/command_line.h" +#include "ui/aura/root_window.h" +#include "ui/base/event.h" +#include "ui/base/ui_base_switches.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/point.h" +#include "ui/gfx/rect.h" +#include "ui/gfx/screen.h" +#include "ui/gfx/size.h" +#include "ui/views/view.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_delegate.h" + +namespace { + +// Device bezel operation constants (volume/brightness slider). + +// This is the minimal brightness value allowed for the display. +const double kMinBrightnessPercent = 5.0; +// For device operation, the finger is not allowed to enter the screen more +// then this fraction of the size of the screen. +const double kAllowableScreenOverlapForDeviceCommand = 0.0005; + +// TODO(skuhne): The noise reduction can be removed when / if we are adding a +// more general reduction. +// To avoid unwanted noise activation, the first 'n' events are being ignored +// for bezel device gestures. +const int kIgnoreFirstBezelDeviceEvents = 10; +// Within these 'n' huge coordinate changes are not allowed. The threshold is +// given in fraction of screen resolution changes. +const double kBezelNoiseDeltaFilter = 0.1; +// To avoid the most frequent noise (extreme locations) the bezel percent +// sliders will not cover the entire screen. We scale therefore the percent +// value by this many percent for minima and maxima extension. +// (Range extends to -kMinMaxInsetPercent .. 100 + kMinMaxInsetPercent). +const double kMinMaxInsetPercent = 5.0; +// To make it possible to reach minimas and maximas easily a range extension +// of -kMinMaxCutOffPercent .. 100 + kMinMaxCutOffPercent will be clamped to +// 0..100%. Everything beyond that will be ignored. +const double kMinMaxCutOffPercent = 2.0; + +} // namespace + +namespace ash { +namespace internal { + +BezelGestureHandler::BezelGestureHandler() + : overlap_percent_(5), + start_location_(BEZEL_START_UNSET), + orientation_(SCROLL_ORIENTATION_UNSET), + is_scrubbing_(false), + initiation_delay_events_(0) { +} + +BezelGestureHandler::~BezelGestureHandler() { +} + +void BezelGestureHandler::ProcessGestureEvent(aura::Window* target, + const ui::GestureEvent& event) { + switch (event.type()) { + case ui::ET_GESTURE_SCROLL_BEGIN: + HandleBezelGestureStart(target, event); + break; + case ui::ET_GESTURE_SCROLL_UPDATE: + // Check if a valid start position has been set. + if (start_location_ == BEZEL_START_UNSET) + break; + + if (DetermineGestureOrientation(event)) + HandleBezelGestureUpdate(target, event); + break; + case ui::ET_GESTURE_SCROLL_END: + HandleBezelGestureEnd(); + break; + default: + break; + } +} + +bool BezelGestureHandler::HandleDeviceControl( + const gfx::Rect& screen, + const ui::GestureEvent& event) { + // Get the slider position as value from the absolute position. + // Note that the highest value is at the top. + double percent = 100.0 - 100.0 * (event.y() - screen.y()) / screen.height(); + if (!DeNoiseBezelSliderPosition(&percent)) { + // Note: Even though this particular event might be noise, the gesture + // itself is still valid and should not get cancelled. + return false; + } + ash::AcceleratorController* accelerator = + ash::Shell::GetInstance()->accelerator_controller(); + if (start_location_ == BEZEL_START_LEFT) { + ash::BrightnessControlDelegate* delegate = + accelerator->brightness_control_delegate(); + if (delegate) + delegate->SetBrightnessPercent( + LimitBezelBrightnessFromSlider(percent), true); + } else if (start_location_ == BEZEL_START_RIGHT) { + Shell::GetInstance()->tray_delegate()->GetVolumeControlDelegate()-> + SetVolumePercent(percent); + } else { + // No further events are necessary. + return true; + } + + // More notifications can be send. + return false; +} + +bool BezelGestureHandler::HandleLauncherControl( + const ui::GestureEvent& event) { + if (start_location_ == BEZEL_START_BOTTOM && + event.details().scroll_y() < 0) { + ash::AcceleratorController* accelerator = + ash::Shell::GetInstance()->accelerator_controller(); + accelerator->PerformAction(FOCUS_LAUNCHER, ui::Accelerator()); + } else { + return false; + } + // No further notifications for this gesture. + return true; +} + +bool BezelGestureHandler::HandleApplicationControl( + const ui::GestureEvent& event) { + ash::AcceleratorController* accelerator = + ash::Shell::GetInstance()->accelerator_controller(); + if (start_location_ == BEZEL_START_LEFT && event.details().scroll_x() > 0) + accelerator->PerformAction(CYCLE_BACKWARD_LINEAR, ui::Accelerator()); + else if (start_location_ == BEZEL_START_RIGHT && + event.details().scroll_x() < 0) + accelerator->PerformAction(CYCLE_FORWARD_LINEAR, ui::Accelerator()); + else + return false; + + // No further notifications for this gesture. + return true; +} + +void BezelGestureHandler::HandleBezelGestureStart( + aura::Window* target, + const ui::GestureEvent& event) { + gfx::Rect screen = gfx::Screen::GetDisplayNearestWindow(target).bounds(); + int overlap_area = screen.width() * overlap_percent_ / 100; + orientation_ = SCROLL_ORIENTATION_UNSET; + + if (event.x() <= screen.x() + overlap_area) { + start_location_ = BEZEL_START_LEFT; + } else if (event.x() >= screen.right() - overlap_area) { + start_location_ = BEZEL_START_RIGHT; + } else if (event.y() >= screen.bottom()) { + start_location_ = BEZEL_START_BOTTOM; + } +} + +bool BezelGestureHandler::DetermineGestureOrientation( + const ui::GestureEvent& event) { + if (orientation_ == SCROLL_ORIENTATION_UNSET) { + if (!event.details().scroll_x() && !event.details().scroll_y()) + return false; + + // For left and right the scroll angle needs to be much steeper to + // be accepted for a 'device configuration' gesture. + if (start_location_ == BEZEL_START_LEFT || + start_location_ == BEZEL_START_RIGHT) { + orientation_ = abs(event.details().scroll_y()) > + abs(event.details().scroll_x()) * 3 ? + SCROLL_ORIENTATION_VERTICAL : SCROLL_ORIENTATION_HORIZONTAL; + } else { + orientation_ = abs(event.details().scroll_y()) > + abs(event.details().scroll_x()) ? + SCROLL_ORIENTATION_VERTICAL : SCROLL_ORIENTATION_HORIZONTAL; + } + + // Reset the delay counter for noise event filtering. + initiation_delay_events_ = 0; + } + return true; +} + +void BezelGestureHandler::HandleBezelGestureUpdate( + aura::Window* target, + const ui::GestureEvent& event) { + if (orientation_ == SCROLL_ORIENTATION_HORIZONTAL) { + if (HandleApplicationControl(event)) + start_location_ = BEZEL_START_UNSET; + } else { + if (start_location_ == BEZEL_START_BOTTOM) { + if (HandleLauncherControl(event)) + start_location_ = BEZEL_START_UNSET; + } else { + // Check if device gestures should be performed or not. + if (CommandLine::ForCurrentProcess()->HasSwitch( + ::switches::kDisableBezelTouch)) { + start_location_ = BEZEL_START_UNSET; + return; + } + gfx::Rect screen = gfx::Screen::GetDisplayNearestWindow(target).bounds(); + // Limit the user gesture "mostly" to the off screen area and check for + // noise invocation. + if (!GestureInBezelArea(screen, event) || + BezelGestureMightBeNoise(screen, event)) + return; + if (HandleDeviceControl(screen, event)) + start_location_ = BEZEL_START_UNSET; + } + } +} + +void BezelGestureHandler::HandleBezelGestureEnd() { + // All which is needed is to set the gesture start location to undefined. + start_location_ = BEZEL_START_UNSET; +} + +bool BezelGestureHandler::GestureInBezelArea( + const gfx::Rect& screen, + const ui::GestureEvent& event) { + // Limit the gesture mostly to the off screen. + double allowable_offset = + screen.width() * kAllowableScreenOverlapForDeviceCommand; + if ((start_location_ == BEZEL_START_LEFT && + event.x() > allowable_offset) || + (start_location_ == BEZEL_START_RIGHT && + event.x() < screen.width() - allowable_offset)) { + start_location_ = BEZEL_START_UNSET; + return false; + } + return true; +} + +bool BezelGestureHandler::BezelGestureMightBeNoise( + const gfx::Rect& screen, + const ui::GestureEvent& event) { + // The first events will not trigger an action. + if (initiation_delay_events_++ < kIgnoreFirstBezelDeviceEvents) { + // When the values are too far apart we ignore it since it might + // be random noise. + double delta_y = event.details().scroll_y(); + double span_y = screen.height(); + if (abs(delta_y / span_y) > kBezelNoiseDeltaFilter) + start_location_ = BEZEL_START_UNSET; + return true; + } + return false; +} + +bool BezelGestureHandler::DeNoiseBezelSliderPosition(double* percent) { + // The range gets passed as 0..100% and is extended to the range of + // (-kMinMaxInsetPercent) .. (100 + kMinMaxInsetPercent). This way we can + // cut off the extreme upper and lower values which are prone to noise. + // It additionally adds a "security buffer" which can then be clamped to the + // extremes to empower the user to get to these values (0% and 100%). + *percent = *percent * (100.0 + 2 * kMinMaxInsetPercent) / 100 - + kMinMaxInsetPercent; + // Values which fall outside of the acceptable inner range area get ignored. + if (*percent < -kMinMaxCutOffPercent || + *percent > 100.0 + kMinMaxCutOffPercent) + return false; + // Excessive values get then clamped to the 0..100% range. + *percent = std::max(std::min(*percent, 100.0), 0.0); + return true; +} + +double BezelGestureHandler::LimitBezelBrightnessFromSlider(double percent) { + // Turning off the display makes no sense, so we map the accessible range to + // kMinimumBrightness .. 100%. + percent = (percent + kMinBrightnessPercent) * 100.0 / + (100.0 + kMinBrightnessPercent); + // Clamp to avoid rounding issues. + return std::min(percent, 100.0); +} + +} // namespace internal +} // namespace ash diff --git a/ash/wm/gestures/bezel_gesture_handler.h b/ash/wm/gestures/bezel_gesture_handler.h new file mode 100644 index 0000000..ac007a5 --- /dev/null +++ b/ash/wm/gestures/bezel_gesture_handler.h @@ -0,0 +1,120 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_WM_GESTURES_BEZEL_GESTURE_HANDLER_H_ +#define ASH_WM_GESTURES_BEZEL_GESTURE_HANDLER_H_ + +#include "base/basictypes.h" + +namespace aura { +class Window; +} + +namespace gfx { +class Point; +class Rect; +} + +namespace ui { +class GestureEvent; +} + +namespace ash { +namespace internal { + +enum BezelStart { + BEZEL_START_UNSET = 0, + BEZEL_START_TOP, + BEZEL_START_LEFT, + BEZEL_START_RIGHT, + BEZEL_START_BOTTOM +}; + +enum ScrollOrientation { + SCROLL_ORIENTATION_UNSET = 0, + SCROLL_ORIENTATION_HORIZONTAL, + SCROLL_ORIENTATION_VERTICAL +}; + +class BezelGestureHandler { + public: + BezelGestureHandler(); + virtual ~BezelGestureHandler(); + + void ProcessGestureEvent(aura::Window* target, const ui::GestureEvent& event); + + private: + // Handle events meant for volume / brightness. Returns true when no further + // events from this gesture should be sent. + bool HandleDeviceControl(const gfx::Rect& screen, + const ui::GestureEvent& event); + + // Handle events meant for showing the launcher. Returns true when no further + // events from this gesture should be sent. + bool HandleLauncherControl(const ui::GestureEvent& event); + + // Handle events meant to switch through applications. Returns true when no + // further events from this gesture should be sent. + bool HandleApplicationControl(const ui::GestureEvent& event); + + // Handle a gesture begin event. + void HandleBezelGestureStart(aura::Window* target, + const ui::GestureEvent& event); + + // Determine the gesture orientation (if not yet done). + // Returns true when the orientation has been successfully determined. + bool DetermineGestureOrientation(const ui::GestureEvent& event); + + // Handles a gesture update once the orientation has been found. + void HandleBezelGestureUpdate(aura::Window* target, + const ui::GestureEvent& event); + + // End a bezel gesture. + void HandleBezelGestureEnd(); + + // Check if a bezel slider gesture is still within the bezel area. + // If it is not, it will abort the gesture, otherwise it will return true. + bool GestureInBezelArea(const gfx::Rect& screen, + const ui::GestureEvent& event); + + // Check if the bezel gesture for noise artifacts. If it is no noise + // it will return false. If noise is detected it will abort the gesture. + bool BezelGestureMightBeNoise(const gfx::Rect& screen, + const ui::GestureEvent& event); + + // Most noise events are showing up at the upper and lower extremes. To + // filter them out, a few percent get cut off at the top and at the bottom. + // A return value of false indicates possible noise and no further action + // should be performed with the event. + // The returned |percent| value is between 0.0 and 100.0. + bool DeNoiseBezelSliderPosition(double* percent); + + // Determine the brightness value from the slider position. + double LimitBezelBrightnessFromSlider(double percent); + + // The percentage of the screen to the left and right which belongs to + // device gestures. + const int overlap_percent_; + + // Which bezel corner are we on. + BezelStart start_location_; + + // Which orientation are we moving. + ScrollOrientation orientation_; + + // A device swipe gesture is in progress. + bool is_scrubbing_; + + // To suppress random noise in the bezel area, the stroke needs to have at + // least a certain amount of events in close proximity before it gets used. + // This is the counter which keeps track of the number of events passed. + int initiation_delay_events_; + + DISALLOW_COPY_AND_ASSIGN(BezelGestureHandler); +}; + +} // namespace internal +} // namespace ash + +#endif // ASH_WM_GESTURES_BEZEL_GESTURE_HANDLER_H_ |