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