Home | History | Annotate | Download | only in caption_buttons
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "ash/frame/caption_buttons/frame_size_button.h"
      6 
      7 #include "ash/frame/caption_buttons/frame_caption_button.h"
      8 #include "ash/frame/caption_buttons/frame_caption_button_container_view.h"
      9 #include "ash/shell.h"
     10 #include "ash/test/ash_test_base.h"
     11 #include "ash/wm/window_state.h"
     12 #include "base/i18n/rtl.h"
     13 #include "grit/ash_resources.h"
     14 #include "ui/aura/test/event_generator.h"
     15 #include "ui/aura/window.h"
     16 #include "ui/base/l10n/l10n_util.h"
     17 #include "ui/events/gestures/gesture_configuration.h"
     18 #include "ui/gfx/display.h"
     19 #include "ui/gfx/screen.h"
     20 #include "ui/views/widget/widget.h"
     21 #include "ui/views/widget/widget_delegate.h"
     22 
     23 namespace ash {
     24 namespace test {
     25 
     26 namespace {
     27 
     28 class TestWidgetDelegate : public views::WidgetDelegateView {
     29  public:
     30   TestWidgetDelegate() {}
     31   virtual ~TestWidgetDelegate() {}
     32 
     33   // Overridden from views::WidgetDelegate:
     34   virtual views::View* GetContentsView() OVERRIDE {
     35     return this;
     36   }
     37   virtual bool CanResize() const OVERRIDE {
     38     return true;
     39   }
     40   virtual bool CanMaximize() const OVERRIDE {
     41     return true;
     42   }
     43 
     44   ash::FrameCaptionButtonContainerView* caption_button_container() {
     45     return caption_button_container_;
     46   }
     47 
     48  private:
     49   // Overridden from views::View:
     50   virtual void Layout() OVERRIDE {
     51     caption_button_container_->Layout();
     52 
     53     // Right align the caption button container.
     54     gfx::Size preferred_size = caption_button_container_->GetPreferredSize();
     55     caption_button_container_->SetBounds(width() - preferred_size.width(), 0,
     56         preferred_size.width(), preferred_size.height());
     57   }
     58 
     59   virtual void ViewHierarchyChanged(
     60       const ViewHierarchyChangedDetails& details) OVERRIDE {
     61     if (details.is_add && details.child == this) {
     62       caption_button_container_ = new FrameCaptionButtonContainerView(
     63           GetWidget(), FrameCaptionButtonContainerView::MINIMIZE_ALLOWED);
     64 
     65       // Set arbitrary images for the container's buttons so that the buttons
     66       // have non-empty sizes.
     67       for (int icon = 0; icon < CAPTION_BUTTON_ICON_COUNT; ++icon) {
     68         caption_button_container_->SetButtonImages(
     69             static_cast<CaptionButtonIcon>(icon),
     70             IDR_AURA_WINDOW_CONTROL_ICON_CLOSE,
     71             IDR_AURA_WINDOW_CONTROL_ICON_CLOSE_I,
     72             IDR_AURA_WINDOW_CONTROL_BACKGROUND_H,
     73             IDR_AURA_WINDOW_CONTROL_BACKGROUND_P);
     74       }
     75 
     76       AddChildView(caption_button_container_);
     77     }
     78   }
     79 
     80   // Not owned.
     81   ash::FrameCaptionButtonContainerView* caption_button_container_;
     82 
     83   DISALLOW_COPY_AND_ASSIGN(TestWidgetDelegate);
     84 };
     85 
     86 }  // namespace
     87 
     88 class FrameSizeButtonTest : public AshTestBase {
     89  public:
     90   FrameSizeButtonTest() {}
     91   virtual ~FrameSizeButtonTest() {}
     92 
     93   // Returns the center point of |view| in screen coordinates.
     94   gfx::Point CenterPointInScreen(views::View* view) {
     95     return view->GetBoundsInScreen().CenterPoint();
     96   }
     97 
     98   // Returns true if the window has |state_type|.
     99   bool HasStateType(wm::WindowStateType state_type) const {
    100     return window_state()->GetStateType() == state_type;
    101   }
    102 
    103   // Returns true if all three buttons are in the normal state.
    104   bool AllButtonsInNormalState() const {
    105     return minimize_button_->state() == views::Button::STATE_NORMAL &&
    106         size_button_->state() == views::Button::STATE_NORMAL &&
    107         close_button_->state() == views::Button::STATE_NORMAL;
    108   }
    109 
    110   // Creates a widget with |delegate|. The returned widget takes ownership of
    111   // |delegate|.
    112   views::Widget* CreateWidget(views::WidgetDelegate* delegate) {
    113     views::Widget* widget = new views::Widget;
    114     views::Widget::InitParams params(
    115         views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
    116     params.context = CurrentContext();
    117     params.delegate = delegate;
    118     params.bounds = gfx::Rect(10, 10, 100, 100);
    119     widget->Init(params);
    120     widget->Show();
    121     return widget;
    122   }
    123 
    124   // AshTestBase overrides:
    125   virtual void SetUp() OVERRIDE {
    126     AshTestBase::SetUp();
    127 
    128     TestWidgetDelegate* delegate = new TestWidgetDelegate();
    129     window_state_ = ash::wm::GetWindowState(
    130         CreateWidget(delegate)->GetNativeWindow());
    131 
    132     FrameCaptionButtonContainerView::TestApi test(
    133         delegate->caption_button_container());
    134 
    135     minimize_button_ = test.minimize_button();
    136     size_button_ = test.size_button();
    137     static_cast<FrameSizeButton*>(
    138         size_button_)->set_delay_to_set_buttons_to_snap_mode(0);
    139     close_button_ = test.close_button();
    140   }
    141 
    142   ash::wm::WindowState* window_state() { return window_state_; }
    143   const ash::wm::WindowState* window_state() const { return window_state_; }
    144 
    145   FrameCaptionButton* minimize_button() { return minimize_button_; }
    146   FrameCaptionButton* size_button() { return size_button_; }
    147   FrameCaptionButton* close_button() { return close_button_; }
    148 
    149  private:
    150   // Not owned.
    151   ash::wm::WindowState* window_state_;
    152   FrameCaptionButton* minimize_button_;
    153   FrameCaptionButton* size_button_;
    154   FrameCaptionButton* close_button_;
    155 
    156   DISALLOW_COPY_AND_ASSIGN(FrameSizeButtonTest);
    157 };
    158 
    159 // Tests that pressing the left mouse button or tapping down on the size button
    160 // puts the button into the pressed state.
    161 TEST_F(FrameSizeButtonTest, PressedState) {
    162   aura::test::EventGenerator& generator = GetEventGenerator();
    163   generator.MoveMouseTo(CenterPointInScreen(size_button()));
    164   generator.PressLeftButton();
    165   EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
    166   generator.ReleaseLeftButton();
    167   RunAllPendingInMessageLoop();
    168   EXPECT_EQ(views::Button::STATE_NORMAL, size_button()->state());
    169 
    170   generator.MoveMouseTo(CenterPointInScreen(size_button()));
    171   generator.PressTouchId(3);
    172   EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
    173   generator.ReleaseTouchId(3);
    174   RunAllPendingInMessageLoop();
    175   EXPECT_EQ(views::Button::STATE_NORMAL, size_button()->state());
    176 }
    177 
    178 // Tests that clicking on the size button toggles between the maximized and
    179 // normal state.
    180 TEST_F(FrameSizeButtonTest, ClickSizeButtonTogglesMaximize) {
    181   EXPECT_FALSE(window_state()->IsMaximized());
    182 
    183   aura::test::EventGenerator& generator = GetEventGenerator();
    184   generator.MoveMouseTo(CenterPointInScreen(size_button()));
    185   generator.ClickLeftButton();
    186   RunAllPendingInMessageLoop();
    187   EXPECT_TRUE(window_state()->IsMaximized());
    188 
    189   generator.MoveMouseTo(CenterPointInScreen(size_button()));
    190   generator.ClickLeftButton();
    191   RunAllPendingInMessageLoop();
    192   EXPECT_FALSE(window_state()->IsMaximized());
    193 
    194   generator.GestureTapAt(CenterPointInScreen(size_button()));
    195   RunAllPendingInMessageLoop();
    196   EXPECT_TRUE(window_state()->IsMaximized());
    197 
    198   generator.GestureTapAt(CenterPointInScreen(size_button()));
    199   RunAllPendingInMessageLoop();
    200   EXPECT_FALSE(window_state()->IsMaximized());
    201 }
    202 
    203 // Test that clicking + dragging to a button adjacent to the size button snaps
    204 // the window left or right.
    205 TEST_F(FrameSizeButtonTest, ButtonDrag) {
    206   EXPECT_TRUE(window_state()->IsNormalStateType());
    207 
    208   // 1) Test by dragging the mouse.
    209   // Snap right.
    210   aura::test::EventGenerator& generator = GetEventGenerator();
    211   generator.MoveMouseTo(CenterPointInScreen(size_button()));
    212   generator.PressLeftButton();
    213   generator.MoveMouseTo(CenterPointInScreen(close_button()));
    214   generator.ReleaseLeftButton();
    215   RunAllPendingInMessageLoop();
    216   EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED));
    217 
    218   // Snap left.
    219   generator.MoveMouseTo(CenterPointInScreen(size_button()));
    220   generator.PressLeftButton();
    221   generator.MoveMouseTo(CenterPointInScreen(minimize_button()));
    222   generator.ReleaseLeftButton();
    223   RunAllPendingInMessageLoop();
    224   EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED));
    225 
    226   // 2) Test with scroll gestures.
    227   // Snap right.
    228   generator.GestureScrollSequence(
    229       CenterPointInScreen(size_button()),
    230       CenterPointInScreen(close_button()),
    231       base::TimeDelta::FromMilliseconds(100),
    232       3);
    233   RunAllPendingInMessageLoop();
    234   EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED));
    235 
    236   // Snap left.
    237   generator.GestureScrollSequence(
    238       CenterPointInScreen(size_button()),
    239       CenterPointInScreen(minimize_button()),
    240       base::TimeDelta::FromMilliseconds(100),
    241       3);
    242   RunAllPendingInMessageLoop();
    243   EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED));
    244 
    245   // 3) Test with tap gestures.
    246   const int touch_default_radius =
    247       ui::GestureConfiguration::default_radius();
    248   ui::GestureConfiguration::set_default_radius(0);
    249   // Snap right.
    250   generator.MoveMouseTo(CenterPointInScreen(size_button()));
    251   generator.PressMoveAndReleaseTouchTo(CenterPointInScreen(close_button()));
    252   RunAllPendingInMessageLoop();
    253   EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED));
    254   // Snap left.
    255   generator.MoveMouseTo(CenterPointInScreen(size_button()));
    256   generator.PressMoveAndReleaseTouchTo(CenterPointInScreen(minimize_button()));
    257   RunAllPendingInMessageLoop();
    258   EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED));
    259   ui::GestureConfiguration::set_default_radius(touch_default_radius);
    260 }
    261 
    262 // Test that clicking, dragging, and overshooting the minimize button a bit
    263 // horizontally still snaps the window left.
    264 TEST_F(FrameSizeButtonTest, SnapLeftOvershootMinimize) {
    265   EXPECT_TRUE(window_state()->IsNormalStateType());
    266 
    267   aura::test::EventGenerator& generator = GetEventGenerator();
    268   generator.MoveMouseTo(CenterPointInScreen(size_button()));
    269 
    270   generator.PressLeftButton();
    271   // Move to the minimize button.
    272   generator.MoveMouseTo(CenterPointInScreen(minimize_button()));
    273   // Overshoot the minimize button.
    274   generator.MoveMouseBy(-minimize_button()->width(), 0);
    275   generator.ReleaseLeftButton();
    276   RunAllPendingInMessageLoop();
    277   EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED));
    278 }
    279 
    280 // Test that right clicking the size button has no effect.
    281 TEST_F(FrameSizeButtonTest, RightMouseButton) {
    282   EXPECT_TRUE(window_state()->IsNormalStateType());
    283 
    284   aura::test::EventGenerator& generator = GetEventGenerator();
    285   generator.MoveMouseTo(CenterPointInScreen(size_button()));
    286   generator.PressRightButton();
    287   generator.ReleaseRightButton();
    288   RunAllPendingInMessageLoop();
    289   EXPECT_TRUE(window_state()->IsNormalStateType());
    290 }
    291 
    292 // Test that upon releasing the mouse button after having pressed the size
    293 // button
    294 // - The state of all the caption buttons is reset.
    295 // - The icon displayed by all of the caption buttons is reset.
    296 TEST_F(FrameSizeButtonTest, ResetButtonsAfterClick) {
    297   EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->icon());
    298   EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE, close_button()->icon());
    299   EXPECT_TRUE(AllButtonsInNormalState());
    300 
    301   // Pressing the size button should result in the size button being pressed and
    302   // the minimize and close button icons changing.
    303   aura::test::EventGenerator& generator = GetEventGenerator();
    304   generator.MoveMouseTo(CenterPointInScreen(size_button()));
    305   generator.PressLeftButton();
    306   EXPECT_EQ(views::Button::STATE_NORMAL, minimize_button()->state());
    307   EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
    308   EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state());
    309   EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon());
    310   EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon());
    311 
    312   // Dragging the mouse over the minimize button should hover the minimize
    313   // button and the minimize and close button icons should stay changed.
    314   generator.MoveMouseTo(CenterPointInScreen(minimize_button()));
    315   EXPECT_EQ(views::Button::STATE_HOVERED, minimize_button()->state());
    316   EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
    317   EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state());
    318   EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon());
    319   EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon());
    320 
    321   // Release the mouse, snapping the window left.
    322   generator.ReleaseLeftButton();
    323   RunAllPendingInMessageLoop();
    324   EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED));
    325 
    326   // None of the buttons should stay pressed and the buttons should have their
    327   // regular icons.
    328   EXPECT_TRUE(AllButtonsInNormalState());
    329   EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->icon());
    330   EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE, close_button()->icon());
    331 
    332   // Repeat test but release button where it does not affect the window's state
    333   // because the code path is different.
    334   generator.MoveMouseTo(CenterPointInScreen(size_button()));
    335   generator.PressLeftButton();
    336   EXPECT_EQ(views::Button::STATE_NORMAL, minimize_button()->state());
    337   EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
    338   EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state());
    339   EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon());
    340   EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon());
    341 
    342   const gfx::Rect& kWorkAreaBoundsInScreen =
    343       ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
    344   generator.MoveMouseTo(kWorkAreaBoundsInScreen.bottom_left());
    345 
    346   // None of the buttons should be pressed because we are really far away from
    347   // any of the caption buttons. The minimize and close button icons should
    348   // be changed because the mouse is pressed.
    349   EXPECT_TRUE(AllButtonsInNormalState());
    350   EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon());
    351   EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon());
    352 
    353   // Release the mouse. The window should stay snapped left.
    354   generator.ReleaseLeftButton();
    355   RunAllPendingInMessageLoop();
    356   EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_LEFT_SNAPPED));
    357 
    358   // The buttons should stay unpressed and the buttons should now have their
    359   // regular icons.
    360   EXPECT_TRUE(AllButtonsInNormalState());
    361   EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->icon());
    362   EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE, close_button()->icon());
    363 }
    364 
    365 // Test that the size button is pressed whenever the snap left/right buttons
    366 // are hovered.
    367 TEST_F(FrameSizeButtonTest, SizeButtonPressedWhenSnapButtonHovered) {
    368   EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->icon());
    369   EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE, close_button()->icon());
    370   EXPECT_TRUE(AllButtonsInNormalState());
    371 
    372   // Pressing the size button should result in the size button being pressed and
    373   // the minimize and close button icons changing.
    374   aura::test::EventGenerator& generator = GetEventGenerator();
    375   generator.MoveMouseTo(CenterPointInScreen(size_button()));
    376   generator.PressLeftButton();
    377   EXPECT_EQ(views::Button::STATE_NORMAL, minimize_button()->state());
    378   EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
    379   EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state());
    380   EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon());
    381   EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon());
    382 
    383   // Dragging the mouse over the minimize button (snap left button) should hover
    384   // the minimize button and keep the size button pressed.
    385   generator.MoveMouseTo(CenterPointInScreen(minimize_button()));
    386   EXPECT_EQ(views::Button::STATE_HOVERED, minimize_button()->state());
    387   EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
    388   EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state());
    389 
    390   // Moving the mouse far away from the caption buttons and then moving it over
    391   // the close button (snap right button) should hover the close button and
    392   // keep the size button pressed.
    393   const gfx::Rect& kWorkAreaBoundsInScreen =
    394       ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
    395   generator.MoveMouseTo(kWorkAreaBoundsInScreen.bottom_left());
    396   EXPECT_TRUE(AllButtonsInNormalState());
    397   generator.MoveMouseTo(CenterPointInScreen(close_button()));
    398   EXPECT_EQ(views::Button::STATE_NORMAL, minimize_button()->state());
    399   EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
    400   EXPECT_EQ(views::Button::STATE_HOVERED, close_button()->state());
    401 }
    402 
    403 class FrameSizeButtonTestRTL : public FrameSizeButtonTest {
    404  public:
    405   FrameSizeButtonTestRTL() {}
    406   virtual ~FrameSizeButtonTestRTL() {}
    407 
    408   virtual void SetUp() OVERRIDE {
    409     original_locale_ = l10n_util::GetApplicationLocale(std::string());
    410     base::i18n::SetICUDefaultLocale("he");
    411 
    412     FrameSizeButtonTest::SetUp();
    413   }
    414 
    415   virtual void TearDown() OVERRIDE {
    416     FrameSizeButtonTest::TearDown();
    417     base::i18n::SetICUDefaultLocale(original_locale_);
    418   }
    419 
    420  private:
    421   std::string original_locale_;
    422 
    423   DISALLOW_COPY_AND_ASSIGN(FrameSizeButtonTestRTL);
    424 };
    425 
    426 // Test that clicking + dragging to a button adjacent to the size button presses
    427 // the correct button and snaps the window to the correct side.
    428 TEST_F(FrameSizeButtonTestRTL, ButtonDrag) {
    429   // In RTL the close button should be left of the size button and the minimize
    430   // button should be right of the size button.
    431   ASSERT_LT(close_button()->GetBoundsInScreen().x(),
    432             size_button()->GetBoundsInScreen().x());
    433   ASSERT_LT(size_button()->GetBoundsInScreen().x(),
    434             minimize_button()->GetBoundsInScreen().x());
    435 
    436   // Test initial state.
    437   EXPECT_TRUE(window_state()->IsNormalStateType());
    438   EXPECT_TRUE(AllButtonsInNormalState());
    439   EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->icon());
    440   EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE, close_button()->icon());
    441 
    442   // Pressing the size button should swap the icons of the minimize and close
    443   // buttons to icons for snapping right and for snapping left respectively.
    444   aura::test::EventGenerator& generator = GetEventGenerator();
    445   generator.MoveMouseTo(CenterPointInScreen(size_button()));
    446   generator.PressLeftButton();
    447   EXPECT_EQ(views::Button::STATE_NORMAL, minimize_button()->state());
    448   EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
    449   EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state());
    450   EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, minimize_button()->icon());
    451   EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, close_button()->icon());
    452 
    453   // Dragging over to the minimize button should press it.
    454   generator.MoveMouseTo(CenterPointInScreen(minimize_button()));
    455   EXPECT_EQ(views::Button::STATE_HOVERED, minimize_button()->state());
    456   EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
    457   EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state());
    458 
    459   // Releasing should snap the window right.
    460   generator.ReleaseLeftButton();
    461   RunAllPendingInMessageLoop();
    462   EXPECT_TRUE(HasStateType(wm::WINDOW_STATE_TYPE_RIGHT_SNAPPED));
    463 
    464   // None of the buttons should stay pressed and the buttons should have their
    465   // regular icons.
    466   EXPECT_TRUE(AllButtonsInNormalState());
    467   EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->icon());
    468   EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE, close_button()->icon());
    469 }
    470 
    471 }  // namespace test
    472 }  // namespace ash
    473