diff options
author | bruthig <bruthig@chromium.org> | 2015-02-13 07:13:52 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-02-13 15:14:41 +0000 |
commit | 7ec5114efa5eada853c2ece5d6edb0fa4ce5611a (patch) | |
tree | a2ac273507cfec0d24575e2e233d8fd5b01f107c /ash | |
parent | da598af72344cb67f04108e80415372bed7cea66 (diff) | |
download | chromium_src-7ec5114efa5eada853c2ece5d6edb0fa4ce5611a.zip chromium_src-7ec5114efa5eada853c2ece5d6edb0fa4ce5611a.tar.gz chromium_src-7ec5114efa5eada853c2ece5d6edb0fa4ce5611a.tar.bz2 |
Implemented swipe to close in overview mode.
BUG=393668
Review URL: https://codereview.chromium.org/690103008
Cr-Commit-Position: refs/heads/master@{#316217}
Diffstat (limited to 'ash')
-rw-r--r-- | ash/ash_switches.cc | 4 | ||||
-rw-r--r-- | ash/ash_switches.h | 1 | ||||
-rw-r--r-- | ash/wm/overview/overview_animation_type.h | 11 | ||||
-rw-r--r-- | ash/wm/overview/scoped_overview_animation_settings.cc | 28 | ||||
-rw-r--r-- | ash/wm/overview/scoped_overview_animation_settings.h | 5 | ||||
-rw-r--r-- | ash/wm/overview/window_selector.cc | 1 | ||||
-rw-r--r-- | ash/wm/overview/window_selector_controller.cc | 6 | ||||
-rw-r--r-- | ash/wm/overview/window_selector_controller.h | 5 | ||||
-rw-r--r-- | ash/wm/overview/window_selector_item.cc | 138 | ||||
-rw-r--r-- | ash/wm/overview/window_selector_item.h | 24 | ||||
-rw-r--r-- | ash/wm/overview/window_selector_unittest.cc | 300 |
11 files changed, 463 insertions, 60 deletions
diff --git a/ash/ash_switches.cc b/ash/ash_switches.cc index f11743dd..05fabb2 100644 --- a/ash/ash_switches.cc +++ b/ash/ash_switches.cc @@ -39,6 +39,10 @@ const char kAshDisableScreenOrientationLock[] = "ash-disable-screen-orientation-lock"; #endif +// Disables gesture swipe to close windows while in Overview mode. +const char kAshDisableSwipeToCloseInOverviewMode[] = + "ash-disable-swipe-to-close-in-overview-mode"; + // Disable the Touch Exploration Mode. Touch Exploration Mode will no longer be // turned on automatically when spoken feedback is enabled when this flag is // set. diff --git a/ash/ash_switches.h b/ash/ash_switches.h index 4edb61e..50c8aca 100644 --- a/ash/ash_switches.h +++ b/ash/ash_switches.h @@ -22,6 +22,7 @@ ASH_EXPORT extern const char kAshConstrainPointerToRoot[]; ASH_EXPORT extern const char kAshCopyHostBackgroundAtBoot[]; ASH_EXPORT extern const char kAshDebugShortcuts[]; ASH_EXPORT extern const char kAshDisableLockLayoutManager[]; +ASH_EXPORT extern const char kAshDisableSwipeToCloseInOverviewMode[]; #if defined(OS_CHROMEOS) ASH_EXPORT extern const char kAshDisableScreenOrientationLock[]; #endif diff --git a/ash/wm/overview/overview_animation_type.h b/ash/wm/overview/overview_animation_type.h index 391ce8e..5df51cb 100644 --- a/ash/wm/overview/overview_animation_type.h +++ b/ash/wm/overview/overview_animation_type.h @@ -12,8 +12,10 @@ enum OverviewAnimationType { // TODO(bruthig): Remove OVERVIEW_ANIMATION_NONE value and replace it with // correct animation type actions. OVERVIEW_ANIMATION_NONE, - // Used to fade in the close button and label when entering overview mode. + // Used to fade in the close button and label. OVERVIEW_ANIMATION_ENTER_OVERVIEW_MODE_FADE_IN, + // Used to fade out the close button. + OVERVIEW_ANIMATION_ENTER_OVERVIEW_MODE_FADE_OUT, // Used to position windows when entering/exiting overview mode and when a // window is closed while overview mode is active. OVERVIEW_ANIMATION_LAY_OUT_SELECTOR_ITEMS, @@ -21,7 +23,12 @@ enum OverviewAnimationType { OVERVIEW_ANIMATION_HIDE_WINDOW, // Used to restore windows to their original position when exiting overview // mode. - OVERVIEW_ANIMATION_RESTORE_WINDOW + OVERVIEW_ANIMATION_RESTORE_WINDOW, + // Used to animate windows when a user performs a touch drag gesture. + OVERVIEW_ANIMATION_SCROLL_SELECTOR_ITEM, + // Used to animate windows back in to position when a touch drag gesture is + // cancelled. + OVERVIEW_ANIMATION_CANCEL_SELECTOR_ITEM_SCROLL }; } // namespace ash diff --git a/ash/wm/overview/scoped_overview_animation_settings.cc b/ash/wm/overview/scoped_overview_animation_settings.cc index 9dc172d..3cb6bcb 100644 --- a/ash/wm/overview/scoped_overview_animation_settings.cc +++ b/ash/wm/overview/scoped_overview_animation_settings.cc @@ -20,15 +20,22 @@ const int kTransitionMilliseconds = 200; // The time duration for widgets to fade in. const int kFadeInMilliseconds = 80; +// The time duration for widgets to fade out. +const int kFadeOutMilliseconds = 100; + base::TimeDelta GetAnimationDuration(OverviewAnimationType animation_type) { switch (animation_type) { case OVERVIEW_ANIMATION_NONE: + case OVERVIEW_ANIMATION_SCROLL_SELECTOR_ITEM: return base::TimeDelta(); case OVERVIEW_ANIMATION_ENTER_OVERVIEW_MODE_FADE_IN: return base::TimeDelta::FromMilliseconds(kFadeInMilliseconds); + case OVERVIEW_ANIMATION_ENTER_OVERVIEW_MODE_FADE_OUT: + return base::TimeDelta::FromMilliseconds(kFadeOutMilliseconds); case OVERVIEW_ANIMATION_LAY_OUT_SELECTOR_ITEMS: case OVERVIEW_ANIMATION_RESTORE_WINDOW: case OVERVIEW_ANIMATION_HIDE_WINDOW: + case OVERVIEW_ANIMATION_CANCEL_SELECTOR_ITEM_SCROLL: return base::TimeDelta::FromMilliseconds(kTransitionMilliseconds); } NOTREACHED(); @@ -44,6 +51,7 @@ ScopedOverviewAnimationSettings::ScopedOverviewAnimationSettings( switch (animation_type) { case OVERVIEW_ANIMATION_NONE: + case OVERVIEW_ANIMATION_SCROLL_SELECTOR_ITEM: animation_settings_.SetPreemptionStrategy( ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS); break; @@ -55,6 +63,10 @@ ScopedOverviewAnimationSettings::ScopedOverviewAnimationSettings( animation_settings_.SetPreemptionStrategy( ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS); break; + case OVERVIEW_ANIMATION_ENTER_OVERVIEW_MODE_FADE_OUT: + animation_settings_.SetPreemptionStrategy( + ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS); + break; case OVERVIEW_ANIMATION_LAY_OUT_SELECTOR_ITEMS: case OVERVIEW_ANIMATION_RESTORE_WINDOW: animation_settings_.SetPreemptionStrategy( @@ -65,6 +77,11 @@ ScopedOverviewAnimationSettings::ScopedOverviewAnimationSettings( animation_settings_.SetPreemptionStrategy( ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); break; + case OVERVIEW_ANIMATION_CANCEL_SELECTOR_ITEM_SCROLL: + animation_settings_.SetPreemptionStrategy( + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); + animation_settings_.SetTweenType(gfx::Tween::EASE_IN_OUT); + break; } animation_settings_.SetTransitionDuration( GetAnimationDuration(animation_type)); @@ -73,15 +90,4 @@ ScopedOverviewAnimationSettings::ScopedOverviewAnimationSettings( ScopedOverviewAnimationSettings::~ScopedOverviewAnimationSettings() { } -// static: -void ScopedOverviewAnimationSettings::SetupFadeInAfterLayout( - aura::Window* window) { - ui::Layer* layer = window->layer(); - layer->SetOpacity(0.0f); - ScopedOverviewAnimationSettings animation_settings( - OverviewAnimationType::OVERVIEW_ANIMATION_ENTER_OVERVIEW_MODE_FADE_IN, - window); - layer->SetOpacity(1.0f); -} - } // namespace ash diff --git a/ash/wm/overview/scoped_overview_animation_settings.h b/ash/wm/overview/scoped_overview_animation_settings.h index 4ea09e5..a558c7e 100644 --- a/ash/wm/overview/scoped_overview_animation_settings.h +++ b/ash/wm/overview/scoped_overview_animation_settings.h @@ -24,11 +24,6 @@ class ScopedOverviewAnimationSettings { aura::Window* window); virtual ~ScopedOverviewAnimationSettings(); - // Convenvience method to fade in a Window with predefined animation settings. - // Note: The fade in animation will occur after a delay where the delay is how - // long the lay out animations take. - static void SetupFadeInAfterLayout(aura::Window* window); - private: // The managed animation settings. ui::ScopedLayerAnimationSettings animation_settings_; diff --git a/ash/wm/overview/window_selector.cc b/ash/wm/overview/window_selector.cc index de86abe..6230b9b 100644 --- a/ash/wm/overview/window_selector.cc +++ b/ash/wm/overview/window_selector.cc @@ -5,6 +5,7 @@ #include "ash/wm/overview/window_selector.h" #include <algorithm> +#include <functional> #include <set> #include <vector> diff --git a/ash/wm/overview/window_selector_controller.cc b/ash/wm/overview/window_selector_controller.cc index df14d27..69ad590 100644 --- a/ash/wm/overview/window_selector_controller.cc +++ b/ash/wm/overview/window_selector_controller.cc @@ -6,6 +6,7 @@ #include <vector> +#include "ash/ash_switches.h" #include "ash/metrics/user_metrics_recorder.h" #include "ash/root_window_controller.h" #include "ash/session/session_state_delegate.h" @@ -15,12 +16,15 @@ #include "ash/wm/overview/window_selector.h" #include "ash/wm/window_state.h" #include "ash/wm/window_util.h" +#include "base/command_line.h" #include "base/metrics/histogram.h" #include "ui/aura/window.h" namespace ash { -WindowSelectorController::WindowSelectorController() { +WindowSelectorController::WindowSelectorController() + : swipe_to_close_disabled_(base::CommandLine::ForCurrentProcess()-> + HasSwitch(switches::kAshDisableSwipeToCloseInOverviewMode)) { } WindowSelectorController::~WindowSelectorController() { diff --git a/ash/wm/overview/window_selector_controller.h b/ash/wm/overview/window_selector_controller.h index 8d4a625..3a87904 100644 --- a/ash/wm/overview/window_selector_controller.h +++ b/ash/wm/overview/window_selector_controller.h @@ -48,6 +48,8 @@ class ASH_EXPORT WindowSelectorController // are visible during overview mode. bool IsRestoringMinimizedWindows() const; + bool swipe_to_close_disabled() const { return swipe_to_close_disabled_; } + // WindowSelectorDelegate: void OnSelectionEnded() override; @@ -60,6 +62,9 @@ class ASH_EXPORT WindowSelectorController scoped_ptr<WindowSelector> window_selector_; base::Time last_selection_time_; + // Tracks whether the "Swipe-to-close" feature is disabled. + bool swipe_to_close_disabled_; + DISALLOW_COPY_AND_ASSIGN(WindowSelectorController); }; diff --git a/ash/wm/overview/window_selector_item.cc b/ash/wm/overview/window_selector_item.cc index 13b52b7..2559362 100644 --- a/ash/wm/overview/window_selector_item.cc +++ b/ash/wm/overview/window_selector_item.cc @@ -37,6 +37,13 @@ namespace ash { namespace { +// The minimum fling velocity which will cause a window to be closed. Unit is +// pixels per second. +const float kMinimumFlingVelocity = 4000.0f; + +// The minimum opacity used during touch scroll gestures. +const float kMinimumOpacity = 0.2f; + // In the conceptual overview table, the window margin is the space reserved // around the window within the cell. This margin does not overlap so the // closest distance between adjacent windows will be twice this amount. @@ -72,6 +79,36 @@ gfx::Rect GetTransformedBounds(aura::Window* window) { return ToEnclosingRect(bounds); } +// Convenvience method to fade in a Window with predefined animation settings. +// Note: The fade in animation will occur after a delay where the delay is how +// long the lay out animations take. +void SetupFadeInAfterLayout(aura::Window* window) { + ui::Layer* layer = window->layer(); + layer->SetOpacity(0.0f); + ScopedOverviewAnimationSettings animation_settings( + OverviewAnimationType::OVERVIEW_ANIMATION_ENTER_OVERVIEW_MODE_FADE_IN, + window); + layer->SetOpacity(1.0f); +} + +// Convenience method to fade out a window using the animation settings defined +// by OverviewAnimationType::OVERVIEW_ANIMATION_ENTER_OVERVIEW_MODE_FADE_OUT. +void SetupFadeOut(aura::Window* window) { + ScopedOverviewAnimationSettings animation_settings( + OverviewAnimationType::OVERVIEW_ANIMATION_ENTER_OVERVIEW_MODE_FADE_OUT, + window); + window->layer()->SetOpacity(0.0f); +} + +// Calculates the window opacity from the given scroll |distance| and the +// |min opacity_distance|. +float CalculateOpacityFromScrollDistance(int distance, + int min_opacity_distance) { + float opacity = + 1.0f - static_cast<float>(abs(distance)) / min_opacity_distance; + return std::min(1.0f, std::max(kMinimumOpacity, opacity)); +} + // An image button with a close window icon. class OverviewCloseButton : public views::ImageButton { public: @@ -99,9 +136,10 @@ OverviewCloseButton::~OverviewCloseButton() { } // namespace WindowSelectorItem::OverviewLabelButton::OverviewLabelButton( - views::ButtonListener* listener, + WindowSelectorItem* selector_item, const base::string16& text) - : LabelButton(listener, text), + : LabelButton(selector_item, text), + selector_item_(selector_item), top_padding_(0) { } @@ -114,6 +152,12 @@ gfx::Rect WindowSelectorItem::OverviewLabelButton::GetChildAreaBounds() { return bounds; } +void WindowSelectorItem::OverviewLabelButton::OnGestureEvent( + ui::GestureEvent* event) { + selector_item_->OnGestureEvent(event); + views::LabelButton::OnGestureEvent(event); +} + WindowSelectorItem::WindowSelectorItem(aura::Window* window) : dimmed_(false), root_window_(window->GetRootWindow()), @@ -216,6 +260,72 @@ void WindowSelectorItem::ButtonPressed(views::Button* sender, wm::GetWindowState(transform_window_.window())->Activate(); } +void WindowSelectorItem::OnGestureEvent(ui::GestureEvent* event) { + if (Shell::GetInstance()->window_selector_controller()-> + swipe_to_close_disabled()) + return; + + int delta_x = 0; + if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN) + scroll_x_origin_ = event->x(); + else + delta_x = event->x() - scroll_x_origin_; + + switch (event->type()) { + case ui::ET_GESTURE_SCROLL_BEGIN: { + // We need to call SetHandled() for the ET_GESTURE_SCROLL_BEGIN event so + // that future ET_GESTURE_SCROLL_* events are sent here. + event->SetHandled(); + close_button_->SetEnabled(false); + SetupFadeOut(close_button_widget_.GetNativeWindow()); + break; + } + case ui::ET_GESTURE_SCROLL_UPDATE: { + event->SetHandled(); + ScopedTransformOverviewWindow::ScopedAnimationSettings + animation_settings; + transform_window_.BeginScopedAnimation( + OverviewAnimationType::OVERVIEW_ANIMATION_SCROLL_SELECTOR_ITEM, + &animation_settings); + + gfx::Transform new_transform; + new_transform.Translate(delta_x, 0); + new_transform.PreconcatTransform( + transform_window_.get_overview_transform()); + transform_window_.SetTransform(root_window(), new_transform); + + const float opacity = CalculateOpacityFromScrollDistance(delta_x, + GetMinimumCloseDistance()); + transform_window_.SetOpacity(opacity); + break; + } + case ui::ET_GESTURE_SCROLL_END: { + event->SetHandled(); + if (abs(delta_x) > GetMinimumCloseDistance()) { + transform_window_.Close(); + break; + } + ResetScrolledWindow(); + break; + } + case ui::ET_SCROLL_FLING_START: { + event->SetHandled(); + if (abs(delta_x) > GetMinimumCloseDistance() || + fabs(event->details().velocity_x()) > kMinimumFlingVelocity) { + transform_window_.Close(); + break; + } + ResetScrolledWindow(); + break; + } + case ui::ET_GESTURE_END: + scroll_x_origin_ = 0; + break; + default: + break; + } +} + void WindowSelectorItem::OnWindowDestroying(aura::Window* window) { window->RemoveObserver(this); transform_window_.OnWindowDestroyed(); @@ -228,6 +338,20 @@ void WindowSelectorItem::OnWindowTitleChanged(aura::Window* window) { UpdateCloseButtonAccessibilityName(); } +void WindowSelectorItem::ResetScrolledWindow() { + ScopedTransformOverviewWindow::ScopedAnimationSettings animation_settings; + transform_window_.BeginScopedAnimation( + OverviewAnimationType::OVERVIEW_ANIMATION_CANCEL_SELECTOR_ITEM_SCROLL, + &animation_settings); + + transform_window_.SetTransform(root_window(), + transform_window_.get_overview_transform()); + transform_window_.SetOpacity(1.0); + + SetupFadeInAfterLayout(close_button_widget_.GetNativeWindow()); + close_button_->SetEnabled(true); +} + void WindowSelectorItem::SetItemBounds(const gfx::Rect& target_bounds, OverviewAnimationType animation_type) { DCHECK(root_window_ == GetWindow()->GetRootWindow()); @@ -261,8 +385,7 @@ void WindowSelectorItem::UpdateWindowLabel( if (!window_label_->IsVisible()) { window_label_->Show(); - ScopedOverviewAnimationSettings::SetupFadeInAfterLayout( - window_label_->GetNativeWindow()); + SetupFadeInAfterLayout(window_label_->GetNativeWindow()); } gfx::Rect converted_bounds = @@ -311,8 +434,7 @@ void WindowSelectorItem::UpdateCloseButtonLayout( OverviewAnimationType animation_type) { if (!close_button_->visible()) { close_button_->SetVisible(true); - ScopedOverviewAnimationSettings::SetupFadeInAfterLayout( - close_button_widget_.GetNativeWindow()); + SetupFadeInAfterLayout(close_button_widget_.GetNativeWindow()); } ScopedOverviewAnimationSettings animation_settings(animation_type, close_button_widget_.GetNativeWindow()); @@ -334,4 +456,8 @@ void WindowSelectorItem::UpdateCloseButtonAccessibilityName() { GetWindow()->title())); } +int WindowSelectorItem::GetMinimumCloseDistance() const { + return target_bounds_.size().width() / 2; +} + } // namespace ash diff --git a/ash/wm/overview/window_selector_item.h b/ash/wm/overview/window_selector_item.h index 063b141..0e4aa8f 100644 --- a/ash/wm/overview/window_selector_item.h +++ b/ash/wm/overview/window_selector_item.h @@ -33,18 +33,25 @@ class ASH_EXPORT WindowSelectorItem : public views::ButtonListener, public: class OverviewLabelButton : public views::LabelButton { public: - OverviewLabelButton(views::ButtonListener* listener, + OverviewLabelButton(WindowSelectorItem* selector_item, const base::string16& text); ~OverviewLabelButton() override; void set_top_padding(int top_padding) { top_padding_ = top_padding; } + // views::LabelButton: + void OnGestureEvent(ui::GestureEvent* event) override; + protected: // views::LabelButton: gfx::Rect GetChildAreaBounds() override; private: + // The WindowSelectorItem that the touch gestures are delegated to. + // Not owned. + WindowSelectorItem* selector_item_; + // Padding on top of the button. int top_padding_; @@ -94,6 +101,9 @@ class ASH_EXPORT WindowSelectorItem : public views::ButtonListener, const gfx::Rect& target_bounds() const { return target_bounds_; } + // Handles the gestures on the Window + void OnGestureEvent(ui::GestureEvent* event); + // views::ButtonListener: void ButtonPressed(views::Button* sender, const ui::Event& event) override; @@ -127,6 +137,14 @@ class ASH_EXPORT WindowSelectorItem : public views::ButtonListener, // Updates the close buttons accessibility name. void UpdateCloseButtonAccessibilityName(); + // Animates the |transform_window_| back to it's original overview mode + // position. + void ResetScrolledWindow(); + + // Returns the minimum distance at which a scroll gesture will cause this + // selector item to be closed. + int GetMinimumCloseDistance() const; + // True if the item is being shown in the overview, false if it's being // filtered. bool dimmed_; @@ -158,6 +176,10 @@ class ASH_EXPORT WindowSelectorItem : public views::ButtonListener, // close_button_widget_. views::ImageButton* close_button_; + // The original X location for a scroll begin event. |original_x_| is in the + // local coordinate space of |window_label_button_view_|. + float scroll_x_origin_; + DISALLOW_COPY_AND_ASSIGN(WindowSelectorItem); }; diff --git a/ash/wm/overview/window_selector_unittest.cc b/ash/wm/overview/window_selector_unittest.cc index fef9b75..a663b5c 100644 --- a/ash/wm/overview/window_selector_unittest.cc +++ b/ash/wm/overview/window_selector_unittest.cc @@ -3,8 +3,11 @@ // found in the LICENSE file. #include <algorithm> +#include <map> +#include <vector> #include "ash/accessibility_delegate.h" +#include "ash/ash_switches.h" #include "ash/drag_drop/drag_drop_controller.h" #include "ash/root_window_controller.h" #include "ash/screen_util.h" @@ -28,6 +31,7 @@ #include "ash/wm/window_util.h" #include "ash/wm/wm_event.h" #include "base/basictypes.h" +#include "base/command_line.h" #include "base/compiler_specific.h" #include "base/memory/scoped_vector.h" #include "base/run_loop.h" @@ -68,8 +72,22 @@ void CancelDrag(DragDropController* controller, bool* canceled) { } } +// A short drag distance that will not cause an overview item to close. +const int kShortDragDistance = 10; + +// A far drag distance that will cause an overview item to close. +const int kFarDragDistance = 200; + +// A slow fling velocity that should not cause selctor items to close. +const int kSlowFlingVelocity = 2000; + +// A fast fling velocity that should cause selector items to close. +const int kFastFlingVelocity = 5000; + } // namespace +// TODO(bruthig): Move all non-simple method definitions out of class +// declaration. class WindowSelectorTest : public test::AshTestBase { public: WindowSelectorTest() {} @@ -99,24 +117,28 @@ class WindowSelectorTest : public test::AshTestBase { return window; } - aura::Window* CreatePanelWindow(const gfx::Rect& bounds) { - aura::Window* window = CreateTestWindowInShellWithDelegateAndType( - nullptr, ui::wm::WINDOW_TYPE_PANEL, 0, bounds); - test::TestShelfDelegate::instance()->AddShelfItem(window); - shelf_view_test()->RunMessageLoopUntilAnimationsDone(); - return window; - } - - views::Widget* CreatePanelWindowWidget(const gfx::Rect& bounds) { - views::Widget* widget = new views::Widget; + // Creates a Widget containing a Window with the given |bounds|. This should + // be used when the test requires a Widget. For example any test that will + // cause a window to be closed via + // views::Widget::GetWidgetForNativeView(window)->Close(). + scoped_ptr<views::Widget> CreateWindowWidget(const gfx::Rect& bounds) { + scoped_ptr<views::Widget> widget(new views::Widget); views::Widget::InitParams params; params.bounds = bounds; - params.type = views::Widget::InitParams::TYPE_PANEL; + params.type = views::Widget::InitParams::TYPE_WINDOW; params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; widget->Init(params); widget->Show(); ParentWindowInPrimaryRootWindow(widget->GetNativeWindow()); - return widget; + return widget.Pass(); + } + + aura::Window* CreatePanelWindow(const gfx::Rect& bounds) { + aura::Window* window = CreateTestWindowInShellWithDelegateAndType( + nullptr, ui::wm::WINDOW_TYPE_PANEL, 0, bounds); + test::TestShelfDelegate::instance()->AddShelfItem(window); + shelf_view_test()->RunMessageLoopUntilAnimationsDone(); + return window; } bool WindowsOverlapping(aura::Window* window1, aura::Window* window2) { @@ -256,6 +278,24 @@ class WindowSelectorTest : public test::AshTestBase { DISALLOW_COPY_AND_ASSIGN(WindowSelectorTest); }; +class WindowSelectorSwipeToCloseDisabledTest : public WindowSelectorTest { + public: + WindowSelectorSwipeToCloseDisabledTest() {} + ~WindowSelectorSwipeToCloseDisabledTest() override {} + + // WindowSelectorTest: + void SetUp() override; + + private: + DISALLOW_COPY_AND_ASSIGN(WindowSelectorSwipeToCloseDisabledTest); +}; + +void WindowSelectorSwipeToCloseDisabledTest::SetUp() { + base::CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kAshDisableSwipeToCloseInOverviewMode); + WindowSelectorTest::SetUp(); +} + // Tests that an a11y alert is sent on entering overview mode. TEST_F(WindowSelectorTest, A11yAlertOnOverviewMode) { gfx::Rect bounds(0, 0, 400, 400); @@ -325,15 +365,6 @@ TEST_F(WindowSelectorTest, BasicGesture) { TEST_F(WindowSelectorTest, NoCrashWithDesktopTap) { scoped_ptr<aura::Window> window(CreateWindow(gfx::Rect(200, 300, 250, 450))); - // We need a widget for the close button to work, a bare window will crash. - scoped_ptr<views::Widget> widget(new views::Widget); - views::Widget::InitParams params; - params.bounds = gfx::Rect(0, 0, 400, 400); - params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - params.parent = window->parent(); - widget->Init(params); - widget->Show(); - ToggleOverview(); gfx::Rect bounds = @@ -428,22 +459,15 @@ TEST_F(WindowSelectorTest, WindowDoesNotReceiveEvents) { // Tests that clicking on the close button effectively closes the window. TEST_F(WindowSelectorTest, CloseButton) { - scoped_ptr<aura::Window> window1(CreateWindow(gfx::Rect(200, 300, 250, 450))); + scoped_ptr<views::Widget> widget = + CreateWindowWidget(gfx::Rect(0, 0, 400, 400)); - // We need a widget for the close button to work, a bare window will crash. - scoped_ptr<views::Widget> widget(new views::Widget); - views::Widget::InitParams params; - params.bounds = gfx::Rect(0, 0, 400, 400); - params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - params.parent = window1->parent(); - widget->Init(params); - widget->Show(); ToggleOverview(); - aura::Window* window2 = widget->GetNativeWindow(); - gfx::RectF bounds = GetTransformedBoundsInRootWindow(window2); + aura::Window* window = widget->GetNativeWindow(); + gfx::RectF bounds = GetTransformedBoundsInRootWindow(window); gfx::Point point(bounds.top_right().x() - 1, bounds.top_right().y() - 1); - ui::test::EventGenerator event_generator(window2->GetRootWindow(), point); + ui::test::EventGenerator event_generator(window->GetRootWindow(), point); EXPECT_FALSE(widget->IsClosed()); event_generator.ClickLeftButton(); @@ -461,7 +485,10 @@ TEST_F(WindowSelectorTest, CloseButtonOnMultipleDisplay) { scoped_ptr<aura::Window> window1(CreateWindow(gfx::Rect(650, 300, 250, 450))); - // We need a widget for the close button to work, a bare window will crash. + // We need a widget for the close button to work because windows are closed + // via the widget. We also use the widget to determine if the window has been + // closed or not. We explicity create the widget so that the window can be + // parented to a non-primary root window. scoped_ptr<views::Widget> widget(new views::Widget); views::Widget::InitParams params; params.bounds = gfx::Rect(650, 0, 400, 400); @@ -1246,4 +1273,209 @@ TEST_F(WindowSelectorTest, CancelOverviewOnTap) { EXPECT_FALSE(IsSelecting()); } +// Verify swipe to close doesn't work when swipe to close is disabled. +TEST_F(WindowSelectorSwipeToCloseDisabledTest, WindowTapDragFarDistance) { + scoped_ptr<views::Widget> widget = + CreateWindowWidget(gfx::Rect(0, 0, 400, 400)); + + ToggleOverview(); + ASSERT_TRUE(IsSelecting()); + + aura::Window* window = widget->GetNativeWindow(); + gfx::Rect bounds = ToNearestRect(GetTransformedBoundsInRootWindow(window)); + ui::test::EventGenerator event_generator(window->GetRootWindow()); + + ASSERT_FALSE(widget->IsClosed()); + + gfx::Point start(bounds.CenterPoint()); + gfx::Point end(start.x() - kFarDragDistance, start.y()); + event_generator.GestureScrollSequence( + start, end, base::TimeDelta::FromMilliseconds(10), 5); + + EXPECT_FALSE(widget->IsClosed()); + + RunAllPendingInMessageLoop(); + EXPECT_TRUE(IsSelecting()); +} + +// Verify the window moves and fades as it is dragged. +TEST_F(WindowSelectorTest, VerifyWindowBehaviourDuringTapDrag) { + scoped_ptr<aura::Window> window(CreateWindow(gfx::Rect(0, 0, 400, 400))); + + ToggleOverview(); + + gfx::Rect bounds = ToNearestRect(GetTransformedBoundsInRootWindow( + window.get())); + ui::test::EventGenerator event_generator(window->GetRootWindow()); + + const gfx::Point drag_start_point(bounds.CenterPoint()); + const gfx::Point drag_left_point(drag_start_point.x() - kFarDragDistance, + drag_start_point.y()); + const gfx::Point drag_right_point(drag_start_point.x() + kFarDragDistance, + drag_start_point.y()); + + const int drag_left_delta_x = drag_start_point.x() - drag_left_point.x(); + const int drag_right_delta_x = drag_start_point.x() - drag_right_point.x(); + + const gfx::Rect original_bounds = window->GetBoundsInScreen(); + + ASSERT_EQ(1.0f, window->layer()->opacity()); + + event_generator.set_current_location(drag_start_point); + event_generator.PressTouch(); + + EXPECT_EQ(1.0f, window->layer()->opacity()); + + event_generator.MoveTouch(drag_left_point); + + EXPECT_EQ(original_bounds.x() - drag_left_delta_x, + window->GetBoundsInScreen().x()); + EXPECT_EQ(original_bounds.y(), window->GetBoundsInScreen().y()); + + EXPECT_LT(window->layer()->opacity(), 0.5f); + + event_generator.MoveTouch(drag_start_point); + + EXPECT_EQ(original_bounds.x(), window->GetBoundsInScreen().x()); + EXPECT_EQ(original_bounds.y(), window->GetBoundsInScreen().y()); + EXPECT_EQ(1.0f, window->layer()->opacity()); + + event_generator.MoveTouch(drag_right_point); + + EXPECT_EQ(original_bounds.x() - drag_right_delta_x, + window->GetBoundsInScreen().x()); + EXPECT_EQ(original_bounds.y(), window->GetBoundsInScreen().y()); + + EXPECT_LT(window->layer()->opacity(), 0.5f); +} + +// Test dragging a window a short distance. +TEST_F(WindowSelectorTest, WindowTapDragShortDistance) { + scoped_ptr<views::Widget> widget = + CreateWindowWidget(gfx::Rect(0, 0, 400, 400)); + + ToggleOverview(); + + aura::Window* window = widget->GetNativeWindow(); + gfx::Rect bounds = ToNearestRect(GetTransformedBoundsInRootWindow(window)); + ui::test::EventGenerator event_generator(window->GetRootWindow()); + + ASSERT_FALSE(widget->IsClosed()); + + gfx::Point start(bounds.CenterPoint()); + gfx::Point end(start.x() - kShortDragDistance, start.y()); + event_generator.GestureScrollSequence( + start, end, base::TimeDelta::FromMilliseconds(10), 5); + + EXPECT_FALSE(widget->IsClosed()); + + RunAllPendingInMessageLoop(); + EXPECT_TRUE(IsSelecting()); +} + +// Test dragging a window a far distance. +TEST_F(WindowSelectorTest, WindowTapDragFarDistance) { + scoped_ptr<views::Widget> widget = + CreateWindowWidget(gfx::Rect(0, 0, 400, 400)); + + ToggleOverview(); + ASSERT_TRUE(IsSelecting()); + + aura::Window* window = widget->GetNativeWindow(); + gfx::Rect bounds = ToNearestRect(GetTransformedBoundsInRootWindow(window)); + ui::test::EventGenerator event_generator(window->GetRootWindow()); + + ASSERT_FALSE(widget->IsClosed()); + + gfx::Point start(bounds.CenterPoint()); + gfx::Point end(start.x() - kFarDragDistance, start.y()); + event_generator.GestureScrollSequence( + start, end, base::TimeDelta::FromMilliseconds(10), 5); + + EXPECT_TRUE(widget->IsClosed()); + + RunAllPendingInMessageLoop(); + EXPECT_FALSE(IsSelecting()); +} + +// Test a slow velocity fling. +TEST_F(WindowSelectorTest, SlowVelocityFling) { + scoped_ptr<views::Widget> widget = + CreateWindowWidget(gfx::Rect(0, 0, 400, 400)); + + ToggleOverview(); + + aura::Window* window = widget->GetNativeWindow(); + gfx::RectF bounds = GetTransformedBoundsInRootWindow(window); + ui::test::EventGenerator event_generator(window->GetRootWindow()); + + ASSERT_FALSE(widget->IsClosed()); + + gfx::Point start(bounds.CenterPoint().x(), bounds.CenterPoint().y()); + gfx::Point end(start.x() - kShortDragDistance, start.y()); + const base::TimeDelta kScrollDuration = + event_generator.CalculateScrollDurationForFlingVelocity( + start, end, kSlowFlingVelocity, 10); + event_generator.GestureScrollSequence(start, end, kScrollDuration, 10); + + EXPECT_FALSE(widget->IsClosed()); + + RunAllPendingInMessageLoop(); + EXPECT_TRUE(IsSelecting()); +} + +// Test a fast velocity fling. +TEST_F(WindowSelectorTest, FastVelocityFling) { + scoped_ptr<views::Widget> widget = + CreateWindowWidget(gfx::Rect(0, 0, 400, 400)); + + ToggleOverview(); + ASSERT_TRUE(IsSelecting()); + + aura::Window* window = widget->GetNativeWindow(); + gfx::RectF bounds = GetTransformedBoundsInRootWindow(window); + ui::test::EventGenerator event_generator(window->GetRootWindow()); + + ASSERT_FALSE(widget->IsClosed()); + + gfx::Point start(bounds.CenterPoint().x(), bounds.CenterPoint().y()); + gfx::Point end(start.x() - kShortDragDistance, start.y()); + const base::TimeDelta kScrollDuration = + event_generator.CalculateScrollDurationForFlingVelocity( + start, end, kFastFlingVelocity, 10); + event_generator.GestureScrollSequence(start, end, kScrollDuration, 10); + + EXPECT_TRUE(widget->IsClosed()); + + RunAllPendingInMessageLoop(); + EXPECT_FALSE(IsSelecting()); +} + +// Test a fast velocity fling. +TEST_F(WindowSelectorTest, SlowVelocityFlingAtAFarDistance) { + scoped_ptr<views::Widget> widget = + CreateWindowWidget(gfx::Rect(0, 0, 400, 400)); + + ToggleOverview(); + ASSERT_TRUE(IsSelecting()); + + aura::Window* window = widget->GetNativeWindow(); + gfx::RectF bounds = GetTransformedBoundsInRootWindow(window); + ui::test::EventGenerator event_generator(window->GetRootWindow()); + + ASSERT_FALSE(widget->IsClosed()); + + gfx::Point start(bounds.CenterPoint().x(), bounds.CenterPoint().y()); + gfx::Point end(start.x() - kFarDragDistance, start.y()); + const base::TimeDelta kScrollDuration = + event_generator.CalculateScrollDurationForFlingVelocity( + start, end, kSlowFlingVelocity, 10); + event_generator.GestureScrollSequence(start, end, kScrollDuration, 10); + + EXPECT_TRUE(widget->IsClosed()); + + RunAllPendingInMessageLoop(); + EXPECT_FALSE(IsSelecting()); +} + } // namespace ash |