Home | History | Annotate | Download | only in dock
      1 // Copyright (c) 2013 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/dock/docked_window_layout_manager.h"
      6 
      7 #include "ash/ash_switches.h"
      8 #include "ash/launcher/launcher.h"
      9 #include "ash/launcher/launcher_model.h"
     10 #include "ash/root_window_controller.h"
     11 #include "ash/screen_ash.h"
     12 #include "ash/shelf/shelf_layout_manager.h"
     13 #include "ash/shelf/shelf_types.h"
     14 #include "ash/shelf/shelf_widget.h"
     15 #include "ash/shell.h"
     16 #include "ash/shell_window_ids.h"
     17 #include "ash/test/ash_test_base.h"
     18 #include "ash/test/launcher_view_test_api.h"
     19 #include "ash/test/shell_test_api.h"
     20 #include "ash/test/test_launcher_delegate.h"
     21 #include "ash/wm/panels/panel_layout_manager.h"
     22 #include "ash/wm/window_properties.h"
     23 #include "ash/wm/window_resizer.h"
     24 #include "base/basictypes.h"
     25 #include "base/command_line.h"
     26 #include "ui/aura/client/aura_constants.h"
     27 #include "ui/aura/root_window.h"
     28 #include "ui/aura/window.h"
     29 #include "ui/base/hit_test.h"
     30 #include "ui/views/widget/widget.h"
     31 
     32 namespace ash {
     33 namespace internal {
     34 
     35 class DockedWindowLayoutManagerTest
     36     : public test::AshTestBase,
     37       public testing::WithParamInterface<aura::client::WindowType> {
     38  public:
     39   DockedWindowLayoutManagerTest() : window_type_(GetParam()) {}
     40   virtual ~DockedWindowLayoutManagerTest() {}
     41 
     42   virtual void SetUp() OVERRIDE {
     43     CommandLine::ForCurrentProcess()->AppendSwitch(
     44         ash::switches::kAshEnableStickyEdges);
     45     CommandLine::ForCurrentProcess()->AppendSwitch(
     46         ash::switches::kAshEnableDockedWindows);
     47     AshTestBase::SetUp();
     48     UpdateDisplay("600x600");
     49     ASSERT_TRUE(test::TestLauncherDelegate::instance());
     50 
     51     launcher_view_test_.reset(new test::LauncherViewTestAPI(
     52         Launcher::ForPrimaryDisplay()->GetLauncherViewForTest()));
     53     launcher_view_test_->SetAnimationDuration(1);
     54   }
     55 
     56  protected:
     57   enum DockedEdge {
     58     DOCKED_EDGE_NONE,
     59     DOCKED_EDGE_LEFT,
     60     DOCKED_EDGE_RIGHT,
     61   };
     62 
     63   enum DockedState {
     64     UNDOCKED,
     65     DOCKED,
     66   };
     67 
     68   aura::Window* CreateTestWindow(const gfx::Rect& bounds) {
     69     aura::Window* window = CreateTestWindowInShellWithDelegateAndType(
     70         NULL,
     71         window_type_,
     72         0,
     73         bounds);
     74     if (window_type_ == aura::client::WINDOW_TYPE_PANEL) {
     75       test::TestLauncherDelegate* launcher_delegate =
     76           test::TestLauncherDelegate::instance();
     77       launcher_delegate->AddLauncherItem(window);
     78       PanelLayoutManager* manager =
     79           static_cast<PanelLayoutManager*>(GetPanelContainer(window)->
     80               layout_manager());
     81       manager->Relayout();
     82     }
     83     return window;
     84   }
     85 
     86   aura::Window* GetPanelContainer(aura::Window* panel) {
     87     return Shell::GetContainer(panel->GetRootWindow(),
     88                                internal::kShellWindowId_PanelContainer);
     89   }
     90 
     91   static WindowResizer* CreateSomeWindowResizer(
     92       aura::Window* window,
     93       const gfx::Point& point_in_parent,
     94       int window_component) {
     95     return CreateWindowResizer(
     96         window,
     97         point_in_parent,
     98         window_component,
     99         aura::client::WINDOW_MOVE_SOURCE_MOUSE).release();
    100   }
    101 
    102   void DragStart(aura::Window* window) {
    103     initial_location_in_parent_ = window->bounds().origin();
    104     resizer_.reset(CreateSomeWindowResizer(window,
    105                                            initial_location_in_parent_,
    106                                            HTCAPTION));
    107     ASSERT_TRUE(resizer_.get());
    108   }
    109 
    110   void DragStartAtOffsetFromwindowOrigin(aura::Window* window,
    111                                          int dx,
    112                                          int dy) {
    113     initial_location_in_parent_ =
    114         window->bounds().origin() + gfx::Vector2d(dx, dy);
    115     resizer_.reset(CreateSomeWindowResizer(window,
    116                                            initial_location_in_parent_,
    117                                            HTCAPTION));
    118     ASSERT_TRUE(resizer_.get());
    119   }
    120 
    121   void DragMove(int dx, int dy) {
    122     resizer_->Drag(initial_location_in_parent_ + gfx::Vector2d(dx, dy), 0);
    123   }
    124 
    125   void DragEnd() {
    126     resizer_->CompleteDrag(0);
    127     resizer_.reset();
    128   }
    129 
    130   void DragRevert() {
    131     resizer_->RevertDrag();
    132     resizer_.reset();
    133   }
    134 
    135   // Panels are parented by panel container during drags.
    136   // Docked windows are parented by dock container during drags.
    137   // All other windows that we are testing here have default container as a
    138   // parent.
    139   int CorrectContainerIdDuringDrag(DockedState is_docked) {
    140     if (window_type_ == aura::client::WINDOW_TYPE_PANEL)
    141       return internal::kShellWindowId_PanelContainer;
    142     if (is_docked == DOCKED)
    143       return internal::kShellWindowId_DockedContainer;
    144     return internal::kShellWindowId_DefaultContainer;
    145   }
    146 
    147   // Test dragging the window vertically (to detach if it is a panel) and then
    148   // horizontally to the edge with an added offset from the edge of |dx|.
    149   void DragRelativeToEdge(DockedEdge edge,
    150                           aura::Window* window,
    151                           int dx) {
    152     DragVerticallyAndRelativeToEdge(
    153         edge,
    154         window,
    155         dx,
    156         window_type_ == aura::client::WINDOW_TYPE_PANEL ? -100 : 20);
    157   }
    158 
    159   void DragToVerticalPositionAndToEdge(DockedEdge edge,
    160                                        aura::Window* window,
    161                                        int y) {
    162     DragToVerticalPositionRelativeToEdge(edge, window, 0, y);
    163   }
    164 
    165   void DragToVerticalPositionRelativeToEdge(DockedEdge edge,
    166                                             aura::Window* window,
    167                                             int dx,
    168                                             int y) {
    169     gfx::Rect initial_bounds = window->GetBoundsInScreen();
    170     DragVerticallyAndRelativeToEdge(edge, window, dx, y - initial_bounds.y());
    171   }
    172 
    173   // Detach if our window is a panel, then drag it vertically by |dy| and
    174   // horizontally to the edge with an added offset from the edge of |dx|.
    175   void DragVerticallyAndRelativeToEdge(DockedEdge edge,
    176                                        aura::Window* window,
    177                                        int dx,
    178                                        int dy) {
    179     aura::RootWindow* root_window = window->GetRootWindow();
    180     gfx::Rect initial_bounds = window->GetBoundsInScreen();
    181 
    182     if (window_type_ == aura::client::WINDOW_TYPE_PANEL) {
    183       ASSERT_NO_FATAL_FAILURE(DragStart(window));
    184       EXPECT_TRUE(window->GetProperty(kPanelAttachedKey));
    185 
    186       // Drag enough to detach since our tests assume panels to be initially
    187       // detached.
    188       DragMove(0, dy);
    189       EXPECT_EQ(CorrectContainerIdDuringDrag(UNDOCKED), window->parent()->id());
    190       EXPECT_EQ(initial_bounds.x(), window->GetBoundsInScreen().x());
    191       EXPECT_EQ(initial_bounds.y() + dy, window->GetBoundsInScreen().y());
    192 
    193       // The panel should be detached when the drag completes.
    194       DragEnd();
    195 
    196       EXPECT_FALSE(window->GetProperty(kPanelAttachedKey));
    197       EXPECT_EQ(internal::kShellWindowId_DefaultContainer,
    198                 window->parent()->id());
    199       EXPECT_EQ(root_window, window->GetRootWindow());
    200     }
    201 
    202     // avoid snap by clicking away from the border
    203     ASSERT_NO_FATAL_FAILURE(DragStartAtOffsetFromwindowOrigin(window, 25, 5));
    204 
    205     // Drag the window left or right to the edge (or almost to it).
    206     if (edge == DOCKED_EDGE_LEFT)
    207       dx += window->GetRootWindow()->bounds().x() - initial_bounds.x();
    208     else if (edge == DOCKED_EDGE_RIGHT)
    209       dx += window->GetRootWindow()->bounds().right() - initial_bounds.right();
    210     DragMove(dx, window_type_ == aura::client::WINDOW_TYPE_PANEL ? 0 : dy);
    211     EXPECT_EQ(CorrectContainerIdDuringDrag(UNDOCKED), window->parent()->id());
    212     // Release the mouse and the panel should be attached to the dock.
    213     DragEnd();
    214 
    215     // x-coordinate can get adjusted by snapping or sticking.
    216     // y-coordinate could be changed by possible automatic layout if docked.
    217     if (window->parent()->id() != internal::kShellWindowId_DockedContainer)
    218       EXPECT_EQ(initial_bounds.y() + dy, window->GetBoundsInScreen().y());
    219   }
    220 
    221  private:
    222   scoped_ptr<WindowResizer> resizer_;
    223   scoped_ptr<test::LauncherViewTestAPI> launcher_view_test_;
    224   aura::client::WindowType window_type_;
    225 
    226   // Location at start of the drag in |window->parent()|'s coordinates.
    227   gfx::Point initial_location_in_parent_;
    228 
    229   DISALLOW_COPY_AND_ASSIGN(DockedWindowLayoutManagerTest);
    230 };
    231 
    232 // Tests that a created window is successfully added to the dock
    233 // layout manager.
    234 TEST_P(DockedWindowLayoutManagerTest, AddOneWindow) {
    235   if (!SupportsHostWindowResize())
    236     return;
    237 
    238   gfx::Rect bounds(0, 0, 201, 201);
    239   scoped_ptr<aura::Window> window(CreateTestWindow(bounds));
    240   DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
    241 
    242   // The window should be attached and snapped to the right side of the screen.
    243   EXPECT_EQ(window->GetRootWindow()->bounds().right(),
    244             window->GetBoundsInScreen().right());
    245   EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id());
    246 }
    247 
    248 // Adds two windows and tests that the gaps are evenly distributed.
    249 TEST_P(DockedWindowLayoutManagerTest, AddTwoWindows) {
    250   if (!SupportsHostWindowResize())
    251     return;
    252 
    253   scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
    254   scoped_ptr<aura::Window> w2(CreateTestWindow(gfx::Rect(0, 0, 210, 202)));
    255   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
    256   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 300);
    257 
    258   // The windows should be attached and snapped to the right side of the screen.
    259   EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
    260             w1->GetBoundsInScreen().right());
    261   EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
    262   EXPECT_EQ(w2->GetRootWindow()->bounds().right(),
    263             w2->GetBoundsInScreen().right());
    264   EXPECT_EQ(internal::kShellWindowId_DockedContainer, w2->parent()->id());
    265 
    266   // Test that the gaps differ at most by a single pixel.
    267   int gap1 = w1->GetBoundsInScreen().y();
    268   int gap2 = w2->GetBoundsInScreen().y() - w1->GetBoundsInScreen().bottom();
    269   int gap3 = ScreenAsh::GetDisplayWorkAreaBoundsInParent(w1.get()).bottom() -
    270       w2->GetBoundsInScreen().bottom();
    271   EXPECT_LE(abs(gap1 - gap2), 1);
    272   EXPECT_LE(abs(gap2 - gap3), 1);
    273   EXPECT_LE(abs(gap3 - gap1), 1);
    274 }
    275 
    276 // Adds two non-overlapping windows and tests layout after a drag.
    277 TEST_P(DockedWindowLayoutManagerTest, TwoWindowsDragging) {
    278   if (!SupportsHostWindowResize())
    279     return;
    280 
    281   scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
    282   scoped_ptr<aura::Window> w2(CreateTestWindow(gfx::Rect(0, 0, 210, 202)));
    283   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
    284   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 300);
    285 
    286   // The windows should be attached and snapped to the right side of the screen.
    287   EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
    288             w1->GetBoundsInScreen().right());
    289   EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
    290   EXPECT_EQ(w2->GetRootWindow()->bounds().right(),
    291             w2->GetBoundsInScreen().right());
    292   EXPECT_EQ(internal::kShellWindowId_DockedContainer, w2->parent()->id());
    293 
    294   // Drag w2 above w1.
    295   ASSERT_NO_FATAL_FAILURE(DragStartAtOffsetFromwindowOrigin(w2.get(), 0, 20));
    296   DragMove(0, w1->bounds().y() - w2->bounds().y() - 20);
    297   DragEnd();
    298 
    299   // Test the new windows order and that the gaps differ at most by a pixel.
    300   int gap1 = w2->GetBoundsInScreen().y();
    301   int gap2 = w1->GetBoundsInScreen().y() - w2->GetBoundsInScreen().bottom();
    302   int gap3 = ScreenAsh::GetDisplayWorkAreaBoundsInParent(w1.get()).bottom() -
    303       w1->GetBoundsInScreen().bottom();
    304   EXPECT_LE(abs(gap1 - gap2), 1);
    305   EXPECT_LE(abs(gap2 - gap3), 1);
    306   EXPECT_LE(abs(gap3 - gap1), 1);
    307 }
    308 
    309 // Adds three overlapping windows and tests layout after a drag.
    310 TEST_P(DockedWindowLayoutManagerTest, ThreeWindowsDragging) {
    311   if (!SupportsHostWindowResize())
    312     return;
    313 
    314   scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
    315   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
    316   scoped_ptr<aura::Window> w2(CreateTestWindow(gfx::Rect(0, 0, 210, 202)));
    317   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 100);
    318   scoped_ptr<aura::Window> w3(CreateTestWindow(gfx::Rect(0, 0, 220, 204)));
    319   DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w3.get(), 300);
    320 
    321   // All windows should be attached and snapped to the right side of the screen.
    322   EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
    323             w1->GetBoundsInScreen().right());
    324   EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
    325   EXPECT_EQ(w2->GetRootWindow()->bounds().right(),
    326             w2->GetBoundsInScreen().right());
    327   EXPECT_EQ(internal::kShellWindowId_DockedContainer, w2->parent()->id());
    328   EXPECT_EQ(w3->GetRootWindow()->bounds().right(),
    329             w3->GetBoundsInScreen().right());
    330   EXPECT_EQ(internal::kShellWindowId_DockedContainer, w3->parent()->id());
    331 
    332   // Test that the top and bottom windows are clamped in work area and
    333   // that the overlaps between the windows differ at most by a pixel.
    334   int overlap1 = w1->GetBoundsInScreen().y();
    335   int overlap2 = w1->GetBoundsInScreen().bottom() - w2->GetBoundsInScreen().y();
    336   int overlap3 = w2->GetBoundsInScreen().bottom() - w3->GetBoundsInScreen().y();
    337   int overlap4 =
    338       ScreenAsh::GetDisplayWorkAreaBoundsInParent(w3.get()).bottom() -
    339       w3->GetBoundsInScreen().bottom();
    340   EXPECT_EQ(0, overlap1);
    341   EXPECT_LE(abs(overlap2 - overlap3), 1);
    342   EXPECT_EQ(0, overlap4);
    343 
    344   // Drag w1 below w2.
    345   ASSERT_NO_FATAL_FAILURE(DragStartAtOffsetFromwindowOrigin(w1.get(), 0, 20));
    346   DragMove(0, w2->bounds().y() - w1->bounds().y() + 20);
    347 
    348   // During the drag the windows get rearranged and the top and the bottom
    349   // should be clamped by the work area.
    350   EXPECT_EQ(0, w2->GetBoundsInScreen().y());
    351   EXPECT_GT(w1->GetBoundsInScreen().y(), w2->GetBoundsInScreen().y());
    352   EXPECT_EQ(ScreenAsh::GetDisplayWorkAreaBoundsInParent(w3.get()).bottom(),
    353             w3->GetBoundsInScreen().bottom());
    354   DragEnd();
    355 
    356   // Test the new windows order and that the overlaps differ at most by a pixel.
    357   overlap1 = w2->GetBoundsInScreen().y();
    358   overlap2 = w2->GetBoundsInScreen().bottom() - w1->GetBoundsInScreen().y();
    359   overlap3 = w1->GetBoundsInScreen().bottom() - w3->GetBoundsInScreen().y();
    360   overlap4 = ScreenAsh::GetDisplayWorkAreaBoundsInParent(w3.get()).bottom() -
    361       w3->GetBoundsInScreen().bottom();
    362   EXPECT_EQ(0, overlap1);
    363   EXPECT_LE(abs(overlap2 - overlap3), 1);
    364   EXPECT_EQ(0, overlap4);
    365 }
    366 
    367 // Tests run twice - on both panels and normal windows
    368 INSTANTIATE_TEST_CASE_P(NormalOrPanel,
    369                         DockedWindowLayoutManagerTest,
    370                         testing::Values(aura::client::WINDOW_TYPE_NORMAL,
    371                                         aura::client::WINDOW_TYPE_PANEL));
    372 }  // namespace internal
    373 }  // namespace ash
    374