Home | History | Annotate | Download | only in wm
      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/wm/window_state.h"
      6 
      7 #include "ash/screen_util.h"
      8 #include "ash/shell.h"
      9 #include "ash/test/ash_test_base.h"
     10 #include "ash/wm/window_state.h"
     11 #include "ash/wm/wm_event.h"
     12 #include "ui/aura/client/aura_constants.h"
     13 #include "ui/aura/test/test_window_delegate.h"
     14 #include "ui/aura/window.h"
     15 
     16 namespace ash {
     17 namespace wm {
     18 namespace {
     19 
     20 class AlwaysMaximizeTestState : public WindowState::State {
     21  public:
     22   explicit AlwaysMaximizeTestState(WindowStateType initial_state_type)
     23       : state_type_(initial_state_type) {}
     24   virtual ~AlwaysMaximizeTestState() {}
     25 
     26   // WindowState::State overrides:
     27   virtual void OnWMEvent(WindowState* window_state,
     28                          const WMEvent* event) OVERRIDE {
     29     // We don't do anything here.
     30   }
     31   virtual WindowStateType GetType() const OVERRIDE {
     32     return state_type_;
     33   }
     34   virtual void AttachState(
     35       WindowState* window_state,
     36       WindowState::State* previous_state) OVERRIDE {
     37     // We always maximize.
     38     if (state_type_ != WINDOW_STATE_TYPE_MAXIMIZED) {
     39       window_state->Maximize();
     40       state_type_ = WINDOW_STATE_TYPE_MAXIMIZED;
     41     }
     42   }
     43   virtual void DetachState(WindowState* window_state) OVERRIDE {}
     44 
     45  private:
     46   WindowStateType state_type_;
     47 
     48   DISALLOW_COPY_AND_ASSIGN(AlwaysMaximizeTestState);
     49 };
     50 
     51 }  // namespace
     52 
     53 typedef test::AshTestBase WindowStateTest;
     54 
     55 // Test that a window gets properly snapped to the display's edges in a
     56 // multi monitor environment.
     57 TEST_F(WindowStateTest, SnapWindowBasic) {
     58   if (!SupportsMultipleDisplays())
     59     return;
     60 
     61   UpdateDisplay("0+0-500x400, 0+500-600x400");
     62   const gfx::Rect kPrimaryDisplayWorkAreaBounds =
     63       ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
     64   const gfx::Rect kSecondaryDisplayWorkAreaBounds =
     65       ScreenUtil::GetSecondaryDisplay().work_area();
     66 
     67   scoped_ptr<aura::Window> window(
     68       CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
     69   WindowState* window_state = GetWindowState(window.get());
     70   const WMEvent snap_left(WM_EVENT_SNAP_LEFT);
     71   window_state->OnWMEvent(&snap_left);
     72   gfx::Rect expected = gfx::Rect(
     73       kPrimaryDisplayWorkAreaBounds.x(),
     74       kPrimaryDisplayWorkAreaBounds.y(),
     75       kPrimaryDisplayWorkAreaBounds.width() / 2,
     76       kPrimaryDisplayWorkAreaBounds.height());
     77   EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
     78 
     79   const WMEvent snap_right(WM_EVENT_SNAP_RIGHT);
     80   window_state->OnWMEvent(&snap_right);
     81   expected.set_x(kPrimaryDisplayWorkAreaBounds.right() - expected.width());
     82   EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
     83 
     84   // Move the window to the secondary display.
     85   window->SetBoundsInScreen(gfx::Rect(600, 0, 100, 100),
     86                             ScreenUtil::GetSecondaryDisplay());
     87 
     88   window_state->OnWMEvent(&snap_right);
     89   expected = gfx::Rect(
     90       kSecondaryDisplayWorkAreaBounds.x() +
     91           kSecondaryDisplayWorkAreaBounds.width() / 2,
     92       kSecondaryDisplayWorkAreaBounds.y(),
     93       kSecondaryDisplayWorkAreaBounds.width() / 2,
     94       kSecondaryDisplayWorkAreaBounds.height());
     95   EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
     96 
     97   window_state->OnWMEvent(&snap_left);
     98   expected.set_x(kSecondaryDisplayWorkAreaBounds.x());
     99   EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
    100 }
    101 
    102 // Test how the minimum and maximum size specified by the aura::WindowDelegate
    103 // affect snapping.
    104 TEST_F(WindowStateTest, SnapWindowMinimumSize) {
    105   if (!SupportsHostWindowResize())
    106     return;
    107 
    108   UpdateDisplay("0+0-600x900");
    109   const gfx::Rect kWorkAreaBounds =
    110       ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
    111 
    112   aura::test::TestWindowDelegate delegate;
    113   scoped_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate(
    114       &delegate, -1, gfx::Rect(0, 100, kWorkAreaBounds.width() - 1, 100)));
    115 
    116   // It should be possible to snap a window with a minimum size.
    117   delegate.set_minimum_size(gfx::Size(kWorkAreaBounds.width() - 1, 0));
    118   WindowState* window_state = GetWindowState(window.get());
    119   EXPECT_TRUE(window_state->CanSnap());
    120   const WMEvent snap_right(WM_EVENT_SNAP_RIGHT);
    121   window_state->OnWMEvent(&snap_right);
    122   gfx::Rect expected = gfx::Rect(kWorkAreaBounds.x() + 1,
    123                                  kWorkAreaBounds.y(),
    124                                  kWorkAreaBounds.width() - 1,
    125                                  kWorkAreaBounds.height());
    126   EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
    127 
    128   // It should not be possible to snap a window with a maximum size, or if it
    129   // cannot be maximized.
    130   delegate.set_maximum_size(gfx::Size(kWorkAreaBounds.width() - 1, 0));
    131   EXPECT_FALSE(window_state->CanSnap());
    132   delegate.set_maximum_size(gfx::Size(0, kWorkAreaBounds.height() - 1));
    133   EXPECT_FALSE(window_state->CanSnap());
    134   delegate.set_maximum_size(gfx::Size());
    135   window->SetProperty(aura::client::kCanMaximizeKey, false);
    136   EXPECT_FALSE(window_state->CanSnap());
    137 }
    138 
    139 // Test that the minimum size specified by aura::WindowDelegate gets respected.
    140 TEST_F(WindowStateTest, TestRespectMinimumSize) {
    141   if (!SupportsHostWindowResize())
    142     return;
    143 
    144   UpdateDisplay("0+0-1024x768");
    145 
    146   aura::test::TestWindowDelegate delegate;
    147   const gfx::Size minimum_size(gfx::Size(500, 300));
    148   delegate.set_minimum_size(minimum_size);
    149 
    150   scoped_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate(
    151       &delegate, -1, gfx::Rect(0, 100, 100, 100)));
    152 
    153   // Check that the window has the correct minimum size.
    154   EXPECT_EQ(minimum_size.ToString(), window->bounds().size().ToString());
    155 
    156   // Set the size to something bigger - that should work.
    157   gfx::Rect bigger_bounds(700, 500, 700, 500);
    158   window->SetBounds(bigger_bounds);
    159   EXPECT_EQ(bigger_bounds.ToString(), window->bounds().ToString());
    160 
    161   // Set the size to something smaller - that should only resize to the smallest
    162   // possible size.
    163   gfx::Rect smaller_bounds(700, 500, 100, 100);
    164   window->SetBounds(smaller_bounds);
    165   EXPECT_EQ(minimum_size.ToString(), window->bounds().size().ToString());
    166 }
    167 
    168 // Test that the minimum window size specified by aura::WindowDelegate does not
    169 // exceed the screen size.
    170 TEST_F(WindowStateTest, TestIgnoreTooBigMinimumSize) {
    171   if (!SupportsHostWindowResize())
    172     return;
    173 
    174   UpdateDisplay("0+0-1024x768");
    175   const gfx::Size work_area_size =
    176       ash::Shell::GetScreen()->GetPrimaryDisplay().work_area().size();
    177   const gfx::Size illegal_size(1280, 960);
    178   const gfx::Rect illegal_bounds(gfx::Point(0, 0), illegal_size);
    179 
    180   aura::test::TestWindowDelegate delegate;
    181   const gfx::Size minimum_size(illegal_size);
    182   delegate.set_minimum_size(minimum_size);
    183 
    184   // The creation should force the window to respect the screen size.
    185   scoped_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate(
    186       &delegate, -1, illegal_bounds));
    187   EXPECT_EQ(work_area_size.ToString(), window->bounds().size().ToString());
    188 
    189   // Trying to set the size to something bigger then the screen size should be
    190   // ignored.
    191   window->SetBounds(illegal_bounds);
    192   EXPECT_EQ(work_area_size.ToString(), window->bounds().size().ToString());
    193 
    194   // Maximizing the window should not allow it to go bigger than that either.
    195   WindowState* window_state = GetWindowState(window.get());
    196   window_state->Maximize();
    197   EXPECT_EQ(work_area_size.ToString(), window->bounds().size().ToString());
    198 }
    199 
    200 // Test that setting the bounds of a snapped window keeps its snapped.
    201 TEST_F(WindowStateTest, SnapWindowSetBounds) {
    202   if (!SupportsHostWindowResize())
    203     return;
    204 
    205   UpdateDisplay("0+0-900x600");
    206   const gfx::Rect kWorkAreaBounds =
    207       ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
    208 
    209   scoped_ptr<aura::Window> window(
    210       CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
    211   WindowState* window_state = GetWindowState(window.get());
    212   const WMEvent snap_left(WM_EVENT_SNAP_LEFT);
    213   window_state->OnWMEvent(&snap_left);
    214   EXPECT_EQ(WINDOW_STATE_TYPE_LEFT_SNAPPED, window_state->GetStateType());
    215   gfx::Rect expected = gfx::Rect(kWorkAreaBounds.x(),
    216                                  kWorkAreaBounds.y(),
    217                                  kWorkAreaBounds.width() / 2,
    218                                  kWorkAreaBounds.height());
    219   EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
    220 
    221   // Snapped windows can have any width.
    222   expected.set_width(500);
    223   window->SetBounds(gfx::Rect(10, 10, 500, 300));
    224   EXPECT_EQ(expected.ToString(), window->GetBoundsInScreen().ToString());
    225   EXPECT_EQ(WINDOW_STATE_TYPE_LEFT_SNAPPED, window_state->GetStateType());
    226 }
    227 
    228 // Test that snapping left/right preserves the restore bounds.
    229 TEST_F(WindowStateTest, RestoreBounds) {
    230   scoped_ptr<aura::Window> window(
    231       CreateTestWindowInShellWithBounds(gfx::Rect(100, 100, 100, 100)));
    232   WindowState* window_state = GetWindowState(window.get());
    233 
    234   EXPECT_TRUE(window_state->IsNormalStateType());
    235 
    236   // 1) Start with restored window with restore bounds set.
    237   gfx::Rect restore_bounds = window->GetBoundsInScreen();
    238   restore_bounds.set_width(restore_bounds.width() + 1);
    239   window_state->SetRestoreBoundsInScreen(restore_bounds);
    240   const WMEvent snap_left(WM_EVENT_SNAP_LEFT);
    241   window_state->OnWMEvent(&snap_left);
    242   const WMEvent snap_right(WM_EVENT_SNAP_RIGHT);
    243   window_state->OnWMEvent(&snap_right);
    244   EXPECT_NE(restore_bounds.ToString(), window->GetBoundsInScreen().ToString());
    245   EXPECT_EQ(restore_bounds.ToString(),
    246             window_state->GetRestoreBoundsInScreen().ToString());
    247   window_state->Restore();
    248   EXPECT_EQ(restore_bounds.ToString(), window->GetBoundsInScreen().ToString());
    249 
    250   // 2) Start with restored bounds set as a result of maximizing the window.
    251   window_state->Maximize();
    252   gfx::Rect maximized_bounds = window->GetBoundsInScreen();
    253   EXPECT_NE(maximized_bounds.ToString(), restore_bounds.ToString());
    254   EXPECT_EQ(restore_bounds.ToString(),
    255             window_state->GetRestoreBoundsInScreen().ToString());
    256 
    257   window_state->OnWMEvent(&snap_left);
    258   EXPECT_NE(restore_bounds.ToString(), window->GetBoundsInScreen().ToString());
    259   EXPECT_NE(maximized_bounds.ToString(),
    260             window->GetBoundsInScreen().ToString());
    261   EXPECT_EQ(restore_bounds.ToString(),
    262             window_state->GetRestoreBoundsInScreen().ToString());
    263 
    264   window_state->Restore();
    265   EXPECT_EQ(restore_bounds.ToString(), window->GetBoundsInScreen().ToString());
    266 }
    267 
    268 // Test that maximizing an auto managed window, then snapping it puts the window
    269 // at the snapped bounds and not at the auto-managed (centered) bounds.
    270 TEST_F(WindowStateTest, AutoManaged) {
    271   scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
    272   WindowState* window_state = GetWindowState(window.get());
    273   window_state->set_window_position_managed(true);
    274   window->Hide();
    275   window->SetBounds(gfx::Rect(100, 100, 100, 100));
    276   window->Show();
    277 
    278   window_state->Maximize();
    279   const WMEvent snap_right(WM_EVENT_SNAP_RIGHT);
    280   window_state->OnWMEvent(&snap_right);
    281 
    282   const gfx::Rect kWorkAreaBounds =
    283       ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
    284   gfx::Rect expected_snapped_bounds(
    285       kWorkAreaBounds.x() + kWorkAreaBounds.width() / 2,
    286       kWorkAreaBounds.y(),
    287       kWorkAreaBounds.width() / 2,
    288       kWorkAreaBounds.height());
    289   EXPECT_EQ(expected_snapped_bounds.ToString(),
    290             window->GetBoundsInScreen().ToString());
    291 
    292   // The window should still be auto managed despite being right maximized.
    293   EXPECT_TRUE(window_state->window_position_managed());
    294 }
    295 
    296 // Test that the replacement of a State object works as expected.
    297 TEST_F(WindowStateTest, SimpleStateSwap) {
    298   scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
    299   WindowState* window_state = GetWindowState(window.get());
    300   EXPECT_FALSE(window_state->IsMaximized());
    301   window_state->SetStateObject(
    302       scoped_ptr<WindowState::State> (new AlwaysMaximizeTestState(
    303           window_state->GetStateType())));
    304   EXPECT_TRUE(window_state->IsMaximized());
    305 }
    306 
    307 // Test that the replacement of a state object, following a restore with the
    308 // original one restores the window to its original state.
    309 TEST_F(WindowStateTest, StateSwapRestore) {
    310   scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
    311   WindowState* window_state = GetWindowState(window.get());
    312   EXPECT_FALSE(window_state->IsMaximized());
    313   scoped_ptr<WindowState::State> old(window_state->SetStateObject(
    314       scoped_ptr<WindowState::State> (new AlwaysMaximizeTestState(
    315           window_state->GetStateType()))).Pass());
    316   EXPECT_TRUE(window_state->IsMaximized());
    317   window_state->SetStateObject(old.Pass());
    318   EXPECT_FALSE(window_state->IsMaximized());
    319 }
    320 
    321 // Tests that a window that had same bounds as the work area shrinks after the
    322 // window is maximized and then restored.
    323 TEST_F(WindowStateTest, RestoredWindowBoundsShrink) {
    324   scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
    325   WindowState* window_state = GetWindowState(window.get());
    326   EXPECT_FALSE(window_state->IsMaximized());
    327   gfx::Rect work_area =
    328       ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
    329 
    330   window->SetBounds(work_area);
    331   window_state->Maximize();
    332   EXPECT_TRUE(window_state->IsMaximized());
    333   EXPECT_EQ(work_area.ToString(), window->bounds().ToString());
    334 
    335   window_state->Restore();
    336   EXPECT_FALSE(window_state->IsMaximized());
    337   EXPECT_NE(work_area.ToString(), window->bounds().ToString());
    338   EXPECT_TRUE(work_area.Contains(window->bounds()));
    339 }
    340 
    341 // TODO(skuhne): Add more unit test to verify the correctness for the restore
    342 // operation.
    343 
    344 }  // namespace wm
    345 }  // namespace ash
    346