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