| // Copyright 2014 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/frame/caption_buttons/frame_size_button.h" |
| |
| #include "ash/frame/caption_buttons/frame_caption_button.h" |
| #include "ash/frame/caption_buttons/frame_caption_button_container_view.h" |
| #include "ash/shell.h" |
| #include "ash/test/ash_test_base.h" |
| #include "ash/wm/window_state.h" |
| #include "base/i18n/rtl.h" |
| #include "grit/ash_resources.h" |
| #include "ui/aura/test/event_generator.h" |
| #include "ui/aura/window.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/events/gestures/gesture_configuration.h" |
| #include "ui/gfx/display.h" |
| #include "ui/gfx/screen.h" |
| #include "ui/views/widget/widget.h" |
| #include "ui/views/widget/widget_delegate.h" |
| |
| namespace ash { |
| namespace test { |
| |
| namespace { |
| |
| class TestWidgetDelegate : public views::WidgetDelegateView { |
| public: |
| TestWidgetDelegate() {} |
| virtual ~TestWidgetDelegate() {} |
| |
| // Overridden from views::WidgetDelegate: |
| virtual views::View* GetContentsView() OVERRIDE { |
| return this; |
| } |
| virtual bool CanResize() const OVERRIDE { |
| return true; |
| } |
| virtual bool CanMaximize() const OVERRIDE { |
| return true; |
| } |
| |
| ash::FrameCaptionButtonContainerView* caption_button_container() { |
| return caption_button_container_; |
| } |
| |
| private: |
| // Overridden from views::View: |
| virtual void Layout() OVERRIDE { |
| caption_button_container_->Layout(); |
| |
| // Right align the caption button container. |
| gfx::Size preferred_size = caption_button_container_->GetPreferredSize(); |
| caption_button_container_->SetBounds(width() - preferred_size.width(), 0, |
| preferred_size.width(), preferred_size.height()); |
| } |
| |
| virtual void ViewHierarchyChanged( |
| const ViewHierarchyChangedDetails& details) OVERRIDE { |
| if (details.is_add && details.child == this) { |
| caption_button_container_ = new FrameCaptionButtonContainerView( |
| GetWidget(), FrameCaptionButtonContainerView::MINIMIZE_ALLOWED); |
| |
| // Set arbitrary images for the container's buttons so that the buttons |
| // have non-empty sizes. |
| for (int icon = 0; icon < CAPTION_BUTTON_ICON_COUNT; ++icon) { |
| caption_button_container_->SetButtonImages( |
| static_cast<CaptionButtonIcon>(icon), |
| IDR_AURA_WINDOW_CONTROL_ICON_CLOSE, |
| IDR_AURA_WINDOW_CONTROL_ICON_CLOSE_I, |
| IDR_AURA_WINDOW_CONTROL_BACKGROUND_H, |
| IDR_AURA_WINDOW_CONTROL_BACKGROUND_P); |
| } |
| |
| AddChildView(caption_button_container_); |
| } |
| } |
| |
| // Not owned. |
| ash::FrameCaptionButtonContainerView* caption_button_container_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TestWidgetDelegate); |
| }; |
| |
| } // namespace |
| |
| class FrameSizeButtonTest : public AshTestBase { |
| public: |
| FrameSizeButtonTest() {} |
| virtual ~FrameSizeButtonTest() {} |
| |
| // Returns the center point of |view| in screen coordinates. |
| gfx::Point CenterPointInScreen(views::View* view) { |
| return view->GetBoundsInScreen().CenterPoint(); |
| } |
| |
| // Returns true if the window has |state_type|. |
| bool HasStateType(wm::WindowStateType state_type) const { |
| return window_state()->GetStateType() == state_type; |
| } |
| |
| // Returns true if all three buttons are in the normal state. |
| bool AllButtonsInNormalState() const { |
| return minimize_button_->state() == views::Button::STATE_NORMAL && |
| size_button_->state() == views::Button::STATE_NORMAL && |
| close_button_->state() == views::Button::STATE_NORMAL; |
| } |
| |
| // Creates a widget with |delegate|. The returned widget takes ownership of |
| // |delegate|. |
| views::Widget* CreateWidget(views::WidgetDelegate* delegate) { |
| views::Widget* widget = new views::Widget; |
| views::Widget::InitParams params( |
| views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); |
| params.context = CurrentContext(); |
| params.delegate = delegate; |
| params.bounds = gfx::Rect(10, 10, 100, 100); |
| widget->Init(params); |
| widget->Show(); |
| return widget; |
| } |
| |
| // AshTestBase overrides: |
| virtual void SetUp() OVERRIDE { |
| AshTestBase::SetUp(); |
| |
| TestWidgetDelegate* delegate = new TestWidgetDelegate(); |
| window_state_ = ash::wm::GetWindowState( |
| CreateWidget(delegate)->GetNativeWindow()); |
| |
| FrameCaptionButtonContainerView::TestApi test( |
| delegate->caption_button_container()); |
| |
| minimize_button_ = test.minimize_button(); |
| size_button_ = test.size_button(); |
| static_cast<FrameSizeButton*>( |
| size_button_)->set_delay_to_set_buttons_to_snap_mode(0); |
| close_button_ = test.close_button(); |
| } |
| |
| ash::wm::WindowState* window_state() { return window_state_; } |
| const ash::wm::WindowState* window_state() const { return window_state_; } |
| |
| FrameCaptionButton* minimize_button() { return minimize_button_; } |
| FrameCaptionButton* size_button() { return size_button_; } |
| FrameCaptionButton* close_button() { return close_button_; } |
| |
| private: |
| // Not owned. |
| ash::wm::WindowState* window_state_; |
| FrameCaptionButton* minimize_button_; |
| FrameCaptionButton* size_button_; |
| FrameCaptionButton* close_button_; |
| |
| DISALLOW_COPY_AND_ASSIGN(FrameSizeButtonTest); |
| }; |
| |
| // Tests that pressing the left mouse button or tapping down on the size button |
| // puts the button into the pressed state. |
| TEST_F(FrameSizeButtonTest, PressedState) { |
| aura::test::EventGenerator& generator = GetEventGenerator(); |
| generator.MoveMouseTo(CenterPointInScreen(size_button())); |
| generator.PressLeftButton(); |
| EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state()); |
| generator.ReleaseLeftButton(); |
| RunAllPendingInMessageLoop(); |
| EXPECT_EQ(views::Button::STATE_NORMAL, size_button()->state()); |
| |
| generator.MoveMouseTo(CenterPointInScreen(size_button())); |
| generator.PressTouchId(3); |
| EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state()); |
| generator.ReleaseTouchId(3); |
| RunAllPendingInMessageLoop(); |
| EXPECT_EQ(views::Button::STATE_NORMAL, size_button()->state()); |
| } |
| |
| // Tests that clicking on the size button toggles between the maximized and |
| // normal state. |
| TEST_F(FrameSizeButtonTest, ClickSizeButtonTogglesMaximize) { |
| EXPECT_FALSE(window_state()->IsMaximized()); |
| |
| aura::test::EventGenerator& generator = GetEventGenerator(); |
| generator.MoveMouseTo(CenterPointInScreen(size_button())); |
| generator.ClickLeftButton(); |
| RunAllPendingInMessageLoop(); |
| EXPECT_TRUE(window_state()->IsMaximized()); |
| |
| generator.MoveMouseTo(CenterPointInScreen(size_button())); |
| generator.ClickLeftButton(); |
| RunAllPendingInMessageLoop(); |
| EXPECT_FALSE(window_state()->IsMaximized()); |
| |
| generator.GestureTapAt(CenterPointInScreen(size_button())); |
| RunAllPendingInMessageLoop(); |
| EXPECT_TRUE(window_state()->IsMaximized()); |
| |
| generator.GestureTapAt(CenterPointInScreen(size_button())); |
| RunAllPendingInMessageLoop(); |
| EXPECT_FALSE(window_state()->IsMaximized()); |
| } |
| |
| // Test that clicking + dragging to a button adjacent to the size button snaps |
| // the window left or right. |
| TEST_F(FrameSizeButtonTest, ButtonDrag) { |
| EXPECT_TRUE(window_state()->IsNormalStateType()); |
| |
| // 1) Test by dragging the mouse. |
| // Snap right. |
| aura::test::EventGenerator& generator = GetEventGenerator(); |
| generator.MoveMouseTo(CenterPointInScreen(size_button())); |
| generator.PressLeftButton(); |
| generator.MoveMouseTo(CenterPointInScreen(close_button())); |
| generator.ReleaseLeftButton(); |
| RunAllPendingInMessageLoop(); |
| EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED)); |
| |
| // Snap left. |
| generator.MoveMouseTo(CenterPointInScreen(size_button())); |
| generator.PressLeftButton(); |
| generator.MoveMouseTo(CenterPointInScreen(minimize_button())); |
| generator.ReleaseLeftButton(); |
| RunAllPendingInMessageLoop(); |
| EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED)); |
| |
| // 2) Test with scroll gestures. |
| // Snap right. |
| generator.GestureScrollSequence( |
| CenterPointInScreen(size_button()), |
| CenterPointInScreen(close_button()), |
| base::TimeDelta::FromMilliseconds(100), |
| 3); |
| RunAllPendingInMessageLoop(); |
| EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED)); |
| |
| // Snap left. |
| generator.GestureScrollSequence( |
| CenterPointInScreen(size_button()), |
| CenterPointInScreen(minimize_button()), |
| base::TimeDelta::FromMilliseconds(100), |
| 3); |
| RunAllPendingInMessageLoop(); |
| EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED)); |
| |
| // 3) Test with tap gestures. |
| const int touch_default_radius = |
| ui::GestureConfiguration::default_radius(); |
| ui::GestureConfiguration::set_default_radius(0); |
| // Snap right. |
| generator.MoveMouseTo(CenterPointInScreen(size_button())); |
| generator.PressMoveAndReleaseTouchTo(CenterPointInScreen(close_button())); |
| RunAllPendingInMessageLoop(); |
| EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED)); |
| // Snap left. |
| generator.MoveMouseTo(CenterPointInScreen(size_button())); |
| generator.PressMoveAndReleaseTouchTo(CenterPointInScreen(minimize_button())); |
| RunAllPendingInMessageLoop(); |
| EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED)); |
| ui::GestureConfiguration::set_default_radius(touch_default_radius); |
| } |
| |
| // Test that clicking, dragging, and overshooting the minimize button a bit |
| // horizontally still snaps the window left. |
| TEST_F(FrameSizeButtonTest, SnapLeftOvershootMinimize) { |
| EXPECT_TRUE(window_state()->IsNormalStateType()); |
| |
| aura::test::EventGenerator& generator = GetEventGenerator(); |
| generator.MoveMouseTo(CenterPointInScreen(size_button())); |
| |
| generator.PressLeftButton(); |
| // Move to the minimize button. |
| generator.MoveMouseTo(CenterPointInScreen(minimize_button())); |
| // Overshoot the minimize button. |
| generator.MoveMouseBy(-minimize_button()->width(), 0); |
| generator.ReleaseLeftButton(); |
| RunAllPendingInMessageLoop(); |
| EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED)); |
| } |
| |
| // Test that right clicking the size button has no effect. |
| TEST_F(FrameSizeButtonTest, RightMouseButton) { |
| EXPECT_TRUE(window_state()->IsNormalStateType()); |
| |
| aura::test::EventGenerator& generator = GetEventGenerator(); |
| generator.MoveMouseTo(CenterPointInScreen(size_button())); |
| generator.PressRightButton(); |
| generator.ReleaseRightButton(); |
| RunAllPendingInMessageLoop(); |
| EXPECT_TRUE(window_state()->IsNormalStateType()); |
| } |
| |
| // Test that upon releasing the mouse button after having pressed the size |
| // button |
| // - The state of all the caption buttons is reset. |
| // - The icon displayed by all of the caption buttons is reset. |
| TEST_F(FrameSizeButtonTest, ResetButtonsAfterClick) { |
| EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->icon()); |
| EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE, close_button()->icon()); |
| EXPECT_TRUE(AllButtonsInNormalState()); |
| |
| // Pressing the size button should result in the size button being pressed and |
| // the minimize and close button icons changing. |
| aura::test::EventGenerator& generator = GetEventGenerator(); |
| generator.MoveMouseTo(CenterPointInScreen(size_button())); |
| generator.PressLeftButton(); |
| EXPECT_EQ(views::Button::STATE_NORMAL, minimize_button()->state()); |
| EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state()); |
| EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state()); |
| EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon()); |
| EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon()); |
| |
| // Dragging the mouse over the minimize button should hover the minimize |
| // button and the minimize and close button icons should stay changed. |
| generator.MoveMouseTo(CenterPointInScreen(minimize_button())); |
| EXPECT_EQ(views::Button::STATE_HOVERED, minimize_button()->state()); |
| EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state()); |
| EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state()); |
| EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon()); |
| EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon()); |
| |
| // Release the mouse, snapping the window left. |
| generator.ReleaseLeftButton(); |
| RunAllPendingInMessageLoop(); |
| EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED)); |
| |
| // None of the buttons should stay pressed and the buttons should have their |
| // regular icons. |
| EXPECT_TRUE(AllButtonsInNormalState()); |
| EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->icon()); |
| EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE, close_button()->icon()); |
| |
| // Repeat test but release button where it does not affect the window's state |
| // because the code path is different. |
| generator.MoveMouseTo(CenterPointInScreen(size_button())); |
| generator.PressLeftButton(); |
| EXPECT_EQ(views::Button::STATE_NORMAL, minimize_button()->state()); |
| EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state()); |
| EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state()); |
| EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon()); |
| EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon()); |
| |
| const gfx::Rect& kWorkAreaBoundsInScreen = |
| ash::Shell::GetScreen()->GetPrimaryDisplay().work_area(); |
| generator.MoveMouseTo(kWorkAreaBoundsInScreen.bottom_left()); |
| |
| // None of the buttons should be pressed because we are really far away from |
| // any of the caption buttons. The minimize and close button icons should |
| // be changed because the mouse is pressed. |
| EXPECT_TRUE(AllButtonsInNormalState()); |
| EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon()); |
| EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon()); |
| |
| // Release the mouse. The window should stay snapped left. |
| generator.ReleaseLeftButton(); |
| RunAllPendingInMessageLoop(); |
| EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED)); |
| |
| // The buttons should stay unpressed and the buttons should now have their |
| // regular icons. |
| EXPECT_TRUE(AllButtonsInNormalState()); |
| EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->icon()); |
| EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE, close_button()->icon()); |
| } |
| |
| // Test that the size button is pressed whenever the snap left/right buttons |
| // are hovered. |
| TEST_F(FrameSizeButtonTest, SizeButtonPressedWhenSnapButtonHovered) { |
| EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->icon()); |
| EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE, close_button()->icon()); |
| EXPECT_TRUE(AllButtonsInNormalState()); |
| |
| // Pressing the size button should result in the size button being pressed and |
| // the minimize and close button icons changing. |
| aura::test::EventGenerator& generator = GetEventGenerator(); |
| generator.MoveMouseTo(CenterPointInScreen(size_button())); |
| generator.PressLeftButton(); |
| EXPECT_EQ(views::Button::STATE_NORMAL, minimize_button()->state()); |
| EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state()); |
| EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state()); |
| EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon()); |
| EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon()); |
| |
| // Dragging the mouse over the minimize button (snap left button) should hover |
| // the minimize button and keep the size button pressed. |
| generator.MoveMouseTo(CenterPointInScreen(minimize_button())); |
| EXPECT_EQ(views::Button::STATE_HOVERED, minimize_button()->state()); |
| EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state()); |
| EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state()); |
| |
| // Moving the mouse far away from the caption buttons and then moving it over |
| // the close button (snap right button) should hover the close button and |
| // keep the size button pressed. |
| const gfx::Rect& kWorkAreaBoundsInScreen = |
| ash::Shell::GetScreen()->GetPrimaryDisplay().work_area(); |
| generator.MoveMouseTo(kWorkAreaBoundsInScreen.bottom_left()); |
| EXPECT_TRUE(AllButtonsInNormalState()); |
| generator.MoveMouseTo(CenterPointInScreen(close_button())); |
| EXPECT_EQ(views::Button::STATE_NORMAL, minimize_button()->state()); |
| EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state()); |
| EXPECT_EQ(views::Button::STATE_HOVERED, close_button()->state()); |
| } |
| |
| class FrameSizeButtonTestRTL : public FrameSizeButtonTest { |
| public: |
| FrameSizeButtonTestRTL() {} |
| virtual ~FrameSizeButtonTestRTL() {} |
| |
| virtual void SetUp() OVERRIDE { |
| original_locale_ = l10n_util::GetApplicationLocale(std::string()); |
| base::i18n::SetICUDefaultLocale("he"); |
| |
| FrameSizeButtonTest::SetUp(); |
| } |
| |
| virtual void TearDown() OVERRIDE { |
| FrameSizeButtonTest::TearDown(); |
| base::i18n::SetICUDefaultLocale(original_locale_); |
| } |
| |
| private: |
| std::string original_locale_; |
| |
| DISALLOW_COPY_AND_ASSIGN(FrameSizeButtonTestRTL); |
| }; |
| |
| // Test that clicking + dragging to a button adjacent to the size button presses |
| // the correct button and snaps the window to the correct side. |
| TEST_F(FrameSizeButtonTestRTL, ButtonDrag) { |
| // In RTL the close button should be left of the size button and the minimize |
| // button should be right of the size button. |
| ASSERT_LT(close_button()->GetBoundsInScreen().x(), |
| size_button()->GetBoundsInScreen().x()); |
| ASSERT_LT(size_button()->GetBoundsInScreen().x(), |
| minimize_button()->GetBoundsInScreen().x()); |
| |
| // Test initial state. |
| EXPECT_TRUE(window_state()->IsNormalStateType()); |
| EXPECT_TRUE(AllButtonsInNormalState()); |
| EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->icon()); |
| EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE, close_button()->icon()); |
| |
| // Pressing the size button should swap the icons of the minimize and close |
| // buttons to icons for snapping right and for snapping left respectively. |
| aura::test::EventGenerator& generator = GetEventGenerator(); |
| generator.MoveMouseTo(CenterPointInScreen(size_button())); |
| generator.PressLeftButton(); |
| EXPECT_EQ(views::Button::STATE_NORMAL, minimize_button()->state()); |
| EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state()); |
| EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state()); |
| EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, minimize_button()->icon()); |
| EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, close_button()->icon()); |
| |
| // Dragging over to the minimize button should press it. |
| generator.MoveMouseTo(CenterPointInScreen(minimize_button())); |
| EXPECT_EQ(views::Button::STATE_HOVERED, minimize_button()->state()); |
| EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state()); |
| EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state()); |
| |
| // Releasing should snap the window right. |
| generator.ReleaseLeftButton(); |
| RunAllPendingInMessageLoop(); |
| EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED)); |
| |
| // None of the buttons should stay pressed and the buttons should have their |
| // regular icons. |
| EXPECT_TRUE(AllButtonsInNormalState()); |
| EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->icon()); |
| EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE, close_button()->icon()); |
| } |
| |
| } // namespace test |
| } // namespace ash |