summaryrefslogtreecommitdiffstats
path: root/ash/wm/gestures
diff options
context:
space:
mode:
authorsadrul@chromium.org <sadrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-08-29 16:05:18 +0000
committersadrul@chromium.org <sadrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-08-29 16:05:18 +0000
commitc57c18bd667a8d4a610244f094a98c3f46d7c0b2 (patch)
treec231974f80e6d8f40525598131a69cfe0a557291 /ash/wm/gestures
parentbc09dc9034ff4642c7d2a61947a8a9e4a7f70c9d (diff)
downloadchromium_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.cc296
-rw-r--r--ash/wm/gestures/bezel_gesture_handler.h120
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_