diff options
author | varunjain@chromium.org <varunjain@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-26 22:14:37 +0000 |
---|---|---|
committer | varunjain@chromium.org <varunjain@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-26 22:14:37 +0000 |
commit | 62531e1316efc119fd1342b367b8f6e7b6007c7a (patch) | |
tree | 0ff5d2ffcd7800172e241cfce7067716552dedfb /ash | |
parent | a1ea5a9a54154af76ff112ccc363018aa8d985f7 (diff) | |
download | chromium_src-62531e1316efc119fd1342b367b8f6e7b6007c7a.zip chromium_src-62531e1316efc119fd1342b367b8f6e7b6007c7a.tar.gz chromium_src-62531e1316efc119fd1342b367b8f6e7b6007c7a.tar.bz2 |
GestureRecognizer: Long press should be cancelled on prevented-defaulted moves.
BUG=133375
TEST=added new test
Review URL: https://chromiumcodereview.appspot.com/10626009
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@144295 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ash')
-rw-r--r-- | ash/wm/system_gesture_event_filter.cc | 166 | ||||
-rw-r--r-- | ash/wm/system_gesture_event_filter.h | 50 | ||||
-rw-r--r-- | ash/wm/system_gesture_event_filter_unittest.cc | 81 |
3 files changed, 209 insertions, 88 deletions
diff --git a/ash/wm/system_gesture_event_filter.cc b/ash/wm/system_gesture_event_filter.cc index 4af722d..2be1782 100644 --- a/ash/wm/system_gesture_event_filter.cc +++ b/ash/wm/system_gesture_event_filter.cc @@ -18,17 +18,14 @@ #include "ash/wm/window_util.h" #include "ash/wm/workspace/phantom_window_controller.h" #include "ash/wm/workspace/snap_sizer.h" -#include "base/timer.h" #include "third_party/skia/include/core/SkColor.h" #include "third_party/skia/include/core/SkPaint.h" #include "third_party/skia/include/core/SkPath.h" #include "third_party/skia/include/core/SkRect.h" #include "ui/aura/event.h" #include "ui/aura/root_window.h" -#include "ui/base/animation/animation.h" -#include "ui/base/animation/animation_delegate.h" -#include "ui/base/animation/linear_animation.h" #include "ui/base/gestures/gesture_configuration.h" +#include "ui/base/gestures/gesture_util.h" #include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/gfx/canvas.h" #include "ui/gfx/point.h" @@ -87,13 +84,19 @@ Widget* CreateAffordanceWidget() { return widget; } +} // namespace + +namespace ash { +namespace internal { + // View of the LongPressAffordanceAnimation. Draws the actual contents and // updates as the animation proceeds. It also maintains the views::Widget that // the animation is shown in. // Currently the affordance is to simply show an empty circle and fill it up as // the animation proceeds. // TODO(varunjain): Change the look of this affordance when we get official UX. -class LongPressAffordanceView : public views::View { +class LongPressAffordanceAnimation::LongPressAffordanceView + : public views::View { public: explicit LongPressAffordanceView(const gfx::Point& event_location) : views::View(), @@ -112,7 +115,6 @@ class LongPressAffordanceView : public views::View { } virtual ~LongPressAffordanceView() { - widget_->Hide(); } void UpdateWithAnimation(ui::Animation* animation) { @@ -148,100 +150,94 @@ class LongPressAffordanceView : public views::View { canvas->DrawPath(path, paint); } - scoped_ptr<Widget> widget_; + scoped_ptr<views::Widget> widget_; int current_angle_; DISALLOW_COPY_AND_ASSIGN(LongPressAffordanceView); }; -} // namespace - -namespace ash { -namespace internal { +LongPressAffordanceAnimation::LongPressAffordanceAnimation() + : ui::LinearAnimation(kAffordanceFrameRateHz, this), + view_(NULL), + tap_down_target_(NULL) { + int duration = + ui::GestureConfiguration::long_press_time_in_seconds() * 1000 - + ui::GestureConfiguration::semi_long_press_time_in_seconds() * 1000; + SetDuration(duration); +} -// LongPressAffordanceAnimation displays an animated affordance that is shown -// on a TAP_DOWN gesture. The animation completes on a LONG_PRESS gesture, or is -// canceled and hidden if any other event is received before that. -class SystemGestureEventFilter::LongPressAffordanceAnimation - : public ui::AnimationDelegate, - public ui::LinearAnimation { - public: - LongPressAffordanceAnimation() - : ui::LinearAnimation(kAffordanceFrameRateHz, this), - view_(NULL) { - int duration = - ui::GestureConfiguration::long_press_time_in_seconds() * 1000 - - ui::GestureConfiguration::semi_long_press_time_in_seconds() * 1000; - SetDuration(duration); - } +LongPressAffordanceAnimation::~LongPressAffordanceAnimation() {} - void ProcessEvent(aura::Window* target, aura::LocatedEvent* event) { - gfx::Point event_location; - int64 timer_start_time_ms = - ui::GestureConfiguration::semi_long_press_time_in_seconds() * 1000; - switch (event->type()) { - case ui::ET_GESTURE_TAP_DOWN: - // Start animation. - tap_down_location_ = event->root_location(); - timer_.Start(FROM_HERE, - base::TimeDelta::FromMilliseconds(timer_start_time_ms), - this, - &LongPressAffordanceAnimation::StartAnimation); - break; - case ui::ET_TOUCH_MOVED: - // We do not want to stop the animation on every TOUCH_MOVED. Instead, - // we will rely on SCROLL_BEGIN to break the animation when the user - // moves their finger. - break; - case ui::ET_GESTURE_LONG_PRESS: - if (is_animating()) - End(); - // fall through to default to reset the view. - default: - // On all other touch and gesture events, we hide the animation. +void LongPressAffordanceAnimation::ProcessEvent(aura::Window* target, + aura::LocatedEvent* event) { + // Once we have a target, we are only interested in events on that target. + if (tap_down_target_ && tap_down_target_ != target) + return; + int64 timer_start_time_ms = + ui::GestureConfiguration::semi_long_press_time_in_seconds() * 1000; + switch (event->type()) { + case ui::ET_GESTURE_TAP_DOWN: + // Start animation. + tap_down_location_ = event->root_location(); + tap_down_target_ = target; + timer_.Start(FROM_HERE, + base::TimeDelta::FromMilliseconds(timer_start_time_ms), + this, + &LongPressAffordanceAnimation::StartAnimation); + break; + case ui::ET_TOUCH_MOVED: + // If animation is running, We want it to be robust to small finger + // movements. So we stop the animation only when the finger moves a + // certain distance. + if (is_animating() && !ui::gestures::IsInsideManhattanSquare( + event->root_location(), tap_down_location_)) StopAnimation(); - break; - } - } - - private: - void StartAnimation() { - view_.reset(new LongPressAffordanceView(tap_down_location_)); - Start(); - } - - void StopAnimation() { - if (timer_.IsRunning()) - timer_.Stop(); - if (is_animating()) - Stop(); - view_.reset(); + break; + case ui::ET_GESTURE_LONG_PRESS: + if (is_animating()) + End(); + // fall through to default to reset the view and tap down target. + default: + // On all other touch and gesture events, we hide the animation. + StopAnimation(); + break; } +} - // Overridden from ui::LinearAnimation. - virtual void AnimateToState(double state) OVERRIDE { - DCHECK(view_.get()); - view_->UpdateWithAnimation(this); - } +void LongPressAffordanceAnimation::StartAnimation() { + view_.reset(new LongPressAffordanceView(tap_down_location_)); + Start(); +} - // Overridden from ui::AnimationDelegate. - virtual void AnimationEnded(const ui::Animation* animation) OVERRIDE { - view_.reset(); - } +void LongPressAffordanceAnimation::StopAnimation() { + if (timer_.IsRunning()) + timer_.Stop(); + if (is_animating()) + Stop(); + view_.reset(); + tap_down_target_ = NULL; +} - virtual void AnimationProgressed(const ui::Animation* animation) OVERRIDE { - } +void LongPressAffordanceAnimation::AnimateToState(double state) { + DCHECK(view_.get()); + view_->UpdateWithAnimation(this); +} - virtual void AnimationCanceled(const ui::Animation* animation) OVERRIDE { - view_.reset(); - } +void LongPressAffordanceAnimation::AnimationEnded( + const ui::Animation* animation) { + view_.reset(); + tap_down_target_ = NULL; +} - scoped_ptr<LongPressAffordanceView> view_; - gfx::Point tap_down_location_; - base::OneShotTimer<LongPressAffordanceAnimation> timer_; +void LongPressAffordanceAnimation::AnimationProgressed( + const ui::Animation* animation) { +} - DISALLOW_COPY_AND_ASSIGN(LongPressAffordanceAnimation); -}; +void LongPressAffordanceAnimation::AnimationCanceled( + const ui::Animation* animation) { + view_.reset(); + tap_down_target_ = NULL; +} class SystemPinchHandler { public: diff --git a/ash/wm/system_gesture_event_filter.h b/ash/wm/system_gesture_event_filter.h index 3c258d7..9389c2f 100644 --- a/ash/wm/system_gesture_event_filter.h +++ b/ash/wm/system_gesture_event_filter.h @@ -8,18 +8,28 @@ #include "ash/shell.h" #include "ash/touch/touch_uma.h" +#include "base/timer.h" #include "ui/aura/event_filter.h" #include "ui/aura/window_observer.h" +#include "ui/base/animation/animation_delegate.h" +#include "ui/base/animation/linear_animation.h" +#include "ui/gfx/point.h" #include <map> namespace aura { -class MouseEvent; class KeyEvent; +class LocatedEvent; +class MouseEvent; class Window; } namespace ash { + +namespace test { +class SystemGestureEventFilterTest; +} // namespace test + namespace internal { class SystemPinchHandler; @@ -39,6 +49,41 @@ enum ScrollOrientation { SCROLL_ORIENTATION_VERTICAL }; +// LongPressAffordanceAnimation displays an animated affordance that is shown +// on a TAP_DOWN gesture. The animation completes on a LONG_PRESS gesture, or is +// canceled and hidden if any other event is received before that. +class LongPressAffordanceAnimation : public ui::AnimationDelegate, + public ui::LinearAnimation { + public: + LongPressAffordanceAnimation(); + virtual ~LongPressAffordanceAnimation(); + + // Display or removes long press affordance according to the |event|. + void ProcessEvent(aura::Window* target, aura::LocatedEvent* event); + + private: + friend class ash::test::SystemGestureEventFilterTest; + + void StartAnimation(); + void StopAnimation(); + + // Overridden from ui::LinearAnimation. + virtual void AnimateToState(double state) OVERRIDE; + + // Overridden from ui::AnimationDelegate. + virtual void AnimationEnded(const ui::Animation* animation) OVERRIDE; + virtual void AnimationProgressed(const ui::Animation* animation) OVERRIDE; + virtual void AnimationCanceled(const ui::Animation* animation) OVERRIDE; + + class LongPressAffordanceView; + scoped_ptr<LongPressAffordanceView> view_; + gfx::Point tap_down_location_; + aura::Window* tap_down_target_; + base::OneShotTimer<LongPressAffordanceAnimation> timer_; + + DISALLOW_COPY_AND_ASSIGN(LongPressAffordanceAnimation); +}; + // An event filter which handles system level gesture events. class SystemGestureEventFilter : public aura::EventFilter, public aura::WindowObserver { @@ -63,6 +108,8 @@ class SystemGestureEventFilter : public aura::EventFilter, virtual void OnWindowDestroying(aura::Window* window) OVERRIDE; private: + friend class ash::test::SystemGestureEventFilterTest; + // Removes system-gesture handlers for a window. void ClearGestureHandlerForWindow(aura::Window* window); @@ -96,7 +143,6 @@ class SystemGestureEventFilter : public aura::EventFilter, // A device swipe gesture is in progress. bool is_scrubbing_; - class LongPressAffordanceAnimation; scoped_ptr<LongPressAffordanceAnimation> long_press_affordance_; TouchUMA touch_uma_; diff --git a/ash/wm/system_gesture_event_filter_unittest.cc b/ash/wm/system_gesture_event_filter_unittest.cc index 2ca46bf..8f0fceb 100644 --- a/ash/wm/system_gesture_event_filter_unittest.cc +++ b/ash/wm/system_gesture_event_filter_unittest.cc @@ -4,6 +4,7 @@ #include "ash/wm/system_gesture_event_filter.h" +#include "base/timer.h" #include "ash/accelerators/accelerator_controller.h" #include "ash/launcher/launcher.h" #include "ash/launcher/launcher_model.h" @@ -95,7 +96,34 @@ class DummyBrightnessControlDelegate : public BrightnessControlDelegate, } // namespace -typedef test::AshTestBase SystemGestureEventFilterTest; +class SystemGestureEventFilterTest : public AshTestBase { + public: + SystemGestureEventFilterTest() : AshTestBase() {} + virtual ~SystemGestureEventFilterTest() {} + + internal::LongPressAffordanceAnimation* GetLongPressAffordance() { + Shell::TestApi shell_test(Shell::GetInstance()); + return shell_test.system_gesture_event_filter()-> + long_press_affordance_.get(); + } + + base::OneShotTimer<internal::LongPressAffordanceAnimation>* + GetLongPressAffordanceTimer() { + return &GetLongPressAffordance()->timer_; + } + + aura::Window* GetLongPressAffordanceTarget() { + return GetLongPressAffordance()->tap_down_target_; + } + + views::View* GetLongPressAffordanceView() { + return reinterpret_cast<views::View*>( + GetLongPressAffordance()->view_.get()); + } + + private: + DISALLOW_COPY_AND_ASSIGN(SystemGestureEventFilterTest); +}; // Ensure that events targeted at the root window are consumed by the // system event handler. @@ -341,5 +369,56 @@ TEST_F(SystemGestureEventFilterTest, ApplicationControl) { } } +TEST_F(SystemGestureEventFilterTest, LongPressAffordanceStateOnCaptureLoss) { + aura::RootWindow* root_window = Shell::GetPrimaryRootWindow(); + + aura::test::TestWindowDelegate delegate; + scoped_ptr<aura::Window> window0( + aura::test::CreateTestWindowWithDelegate( + &delegate, 9, gfx::Rect(0, 0, 100, 100), root_window)); + scoped_ptr<aura::Window> window1( + aura::test::CreateTestWindowWithDelegate( + &delegate, 10, gfx::Rect(0, 0, 100, 50), window0.get())); + scoped_ptr<aura::Window> window2( + aura::test::CreateTestWindowWithDelegate( + &delegate, 11, gfx::Rect(0, 50, 100, 50), window0.get())); + + const int kTouchId = 5; + + // Capture first window. + window1->SetCapture(); + EXPECT_TRUE(window1->HasCapture()); + + // Send touch event to first window. + aura::TouchEvent press(ui::ET_TOUCH_PRESSED, gfx::Point(10, 10), kTouchId, + base::Time::NowFromSystemTime() - base::Time()); + root_window->DispatchTouchEvent(&press); + EXPECT_TRUE(window1->HasCapture()); + + base::OneShotTimer<internal::LongPressAffordanceAnimation>* timer = + GetLongPressAffordanceTimer(); + EXPECT_TRUE(timer->IsRunning()); + EXPECT_EQ(window1.get(), GetLongPressAffordanceTarget()); + + // Force timeout so that the affordance animation can start. + timer->user_task().Run(); + timer->Stop(); + EXPECT_TRUE(GetLongPressAffordance()->is_animating()); + + // Change capture. + window2->SetCapture(); + EXPECT_TRUE(window2->HasCapture()); + + EXPECT_TRUE(GetLongPressAffordance()->is_animating()); + EXPECT_EQ(window1.get(), GetLongPressAffordanceTarget()); + + // Animate to completion. + GetLongPressAffordance()->End(); + + // Check if state has reset. + EXPECT_EQ(NULL, GetLongPressAffordanceTarget()); + EXPECT_EQ(NULL, GetLongPressAffordanceView()); +} + } // namespace test } // namespace ash |