1// Copyright (c) 2012 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/panels/panel_layout_manager.h"
6
7#include "ash/ash_switches.h"
8#include "ash/root_window_controller.h"
9#include "ash/screen_util.h"
10#include "ash/shelf/shelf.h"
11#include "ash/shelf/shelf_button.h"
12#include "ash/shelf/shelf_layout_manager.h"
13#include "ash/shelf/shelf_model.h"
14#include "ash/shelf/shelf_types.h"
15#include "ash/shelf/shelf_util.h"
16#include "ash/shelf/shelf_view.h"
17#include "ash/shelf/shelf_widget.h"
18#include "ash/shell.h"
19#include "ash/shell_window_ids.h"
20#include "ash/test/ash_test_base.h"
21#include "ash/test/shelf_test_api.h"
22#include "ash/test/shelf_view_test_api.h"
23#include "ash/test/shell_test_api.h"
24#include "ash/test/test_shelf_delegate.h"
25#include "ash/wm/mru_window_tracker.h"
26#include "ash/wm/window_state.h"
27#include "ash/wm/window_util.h"
28#include "base/basictypes.h"
29#include "base/command_line.h"
30#include "base/compiler_specific.h"
31#include "base/i18n/rtl.h"
32#include "base/run_loop.h"
33#include "ui/aura/client/aura_constants.h"
34#include "ui/aura/test/event_generator.h"
35#include "ui/aura/test/test_windows.h"
36#include "ui/aura/window.h"
37#include "ui/aura/window_event_dispatcher.h"
38#include "ui/base/l10n/l10n_util.h"
39#include "ui/events/event_utils.h"
40#include "ui/views/widget/widget.h"
41
42namespace ash {
43
44using aura::test::WindowIsAbove;
45
46class PanelLayoutManagerTest : public test::AshTestBase {
47 public:
48  PanelLayoutManagerTest() {}
49  virtual ~PanelLayoutManagerTest() {}
50
51  virtual void SetUp() OVERRIDE {
52    test::AshTestBase::SetUp();
53    ASSERT_TRUE(test::TestShelfDelegate::instance());
54
55    shelf_view_test_.reset(new test::ShelfViewTestAPI(
56        GetShelfView(Shelf::ForPrimaryDisplay())));
57    shelf_view_test_->SetAnimationDuration(1);
58  }
59
60  aura::Window* CreateNormalWindow(const gfx::Rect& bounds) {
61    return CreateTestWindowInShellWithBounds(bounds);
62  }
63
64  aura::Window* CreatePanelWindowWithDelegate(aura::WindowDelegate* delegate,
65                                              const gfx::Rect& bounds) {
66    aura::Window* window = CreateTestWindowInShellWithDelegateAndType(
67        delegate, ui::wm::WINDOW_TYPE_PANEL, 0, bounds);
68    test::TestShelfDelegate* shelf_delegate =
69        test::TestShelfDelegate::instance();
70    shelf_delegate->AddShelfItem(window);
71    shelf_view_test()->RunMessageLoopUntilAnimationsDone();
72    return window;
73  }
74
75  aura::Window* CreatePanelWindow(const gfx::Rect& bounds) {
76    return CreatePanelWindowWithDelegate(NULL, bounds);
77  }
78
79  aura::Window* GetPanelContainer(aura::Window* panel) {
80    return Shell::GetContainer(panel->GetRootWindow(),
81                               kShellWindowId_PanelContainer);
82  }
83
84  views::Widget* GetCalloutWidgetForPanel(aura::Window* panel) {
85    PanelLayoutManager* manager =
86        static_cast<PanelLayoutManager*>(GetPanelContainer(panel)->
87                                         layout_manager());
88    DCHECK(manager);
89    PanelLayoutManager::PanelList::iterator found = std::find(
90        manager->panel_windows_.begin(), manager->panel_windows_.end(),
91        panel);
92    DCHECK(found != manager->panel_windows_.end());
93    DCHECK(found->callout_widget);
94    return reinterpret_cast<views::Widget*>(found->callout_widget);
95  }
96
97  void PanelInScreen(aura::Window* panel) {
98    gfx::Rect panel_bounds = panel->GetBoundsInRootWindow();
99    gfx::Point root_point = gfx::Point(panel_bounds.x(), panel_bounds.y());
100    gfx::Display display = ScreenUtil::FindDisplayContainingPoint(root_point);
101
102    gfx::Rect panel_bounds_in_screen = panel->GetBoundsInScreen();
103    gfx::Point screen_bottom_right = gfx::Point(
104        panel_bounds_in_screen.right(),
105        panel_bounds_in_screen.bottom());
106    gfx::Rect display_bounds = display.bounds();
107    EXPECT_TRUE(screen_bottom_right.x() < display_bounds.width() &&
108                screen_bottom_right.y() < display_bounds.height());
109  }
110
111  void PanelsNotOverlapping(aura::Window* panel1, aura::Window* panel2) {
112    // Waits until all shelf view animations are done.
113    shelf_view_test()->RunMessageLoopUntilAnimationsDone();
114    gfx::Rect window1_bounds = panel1->GetBoundsInRootWindow();
115    gfx::Rect window2_bounds = panel2->GetBoundsInRootWindow();
116
117    EXPECT_FALSE(window1_bounds.Intersects(window2_bounds));
118  }
119
120  void IsPanelAboveLauncherIcon(const aura::Window* panel) {
121    // Waits until all shelf view animations are done.
122    shelf_view_test()->RunMessageLoopUntilAnimationsDone();
123
124    Shelf* shelf = RootWindowController::ForShelf(panel)->shelf()->shelf();
125    gfx::Rect icon_bounds = shelf->GetScreenBoundsOfItemIconForWindow(panel);
126    ASSERT_FALSE(icon_bounds.width() == 0 && icon_bounds.height() == 0);
127
128    gfx::Rect window_bounds = panel->GetBoundsInScreen();
129    ASSERT_LT(icon_bounds.width(), window_bounds.width());
130    ASSERT_LT(icon_bounds.height(), window_bounds.height());
131    gfx::Rect shelf_bounds = shelf->shelf_widget()->GetWindowBoundsInScreen();
132    ShelfAlignment alignment = GetAlignment(panel->GetRootWindow());
133
134    if (IsHorizontal(alignment)) {
135      // The horizontal bounds of the panel window should contain the bounds of
136      // the shelf icon.
137      EXPECT_LE(window_bounds.x(), icon_bounds.x());
138      EXPECT_GE(window_bounds.right(), icon_bounds.right());
139    } else {
140      // The vertical bounds of the panel window should contain the bounds of
141      // the shelf icon.
142      EXPECT_LE(window_bounds.y(), icon_bounds.y());
143      EXPECT_GE(window_bounds.bottom(), icon_bounds.bottom());
144    }
145
146    switch (alignment) {
147      case SHELF_ALIGNMENT_BOTTOM:
148        EXPECT_EQ(shelf_bounds.y(), window_bounds.bottom());
149        break;
150      case SHELF_ALIGNMENT_LEFT:
151        EXPECT_EQ(shelf_bounds.right(), window_bounds.x());
152        break;
153      case SHELF_ALIGNMENT_RIGHT:
154        EXPECT_EQ(shelf_bounds.x(), window_bounds.right());
155        break;
156      case SHELF_ALIGNMENT_TOP:
157        EXPECT_EQ(shelf_bounds.bottom(), window_bounds.y());
158        break;
159    }
160  }
161
162  void IsCalloutAboveLauncherIcon(aura::Window* panel) {
163    // Flush the message loop, since callout updates use a delayed task.
164    base::RunLoop().RunUntilIdle();
165    views::Widget* widget = GetCalloutWidgetForPanel(panel);
166
167    Shelf* shelf = RootWindowController::ForShelf(panel)->shelf()->shelf();
168    gfx::Rect icon_bounds = shelf->GetScreenBoundsOfItemIconForWindow(panel);
169    ASSERT_FALSE(icon_bounds.IsEmpty());
170
171    gfx::Rect panel_bounds = panel->GetBoundsInScreen();
172    gfx::Rect callout_bounds = widget->GetWindowBoundsInScreen();
173    ASSERT_FALSE(icon_bounds.IsEmpty());
174
175    EXPECT_TRUE(widget->IsVisible());
176
177    ShelfAlignment alignment = GetAlignment(panel->GetRootWindow());
178    switch (alignment) {
179      case SHELF_ALIGNMENT_BOTTOM:
180        EXPECT_EQ(panel_bounds.bottom(), callout_bounds.y());
181        break;
182      case SHELF_ALIGNMENT_LEFT:
183        EXPECT_EQ(panel_bounds.x(), callout_bounds.right());
184        break;
185      case SHELF_ALIGNMENT_RIGHT:
186        EXPECT_EQ(panel_bounds.right(), callout_bounds.x());
187        break;
188      case SHELF_ALIGNMENT_TOP:
189        EXPECT_EQ(panel_bounds.y(), callout_bounds.bottom());
190        break;
191    }
192
193    if (IsHorizontal(alignment)) {
194      EXPECT_NEAR(icon_bounds.CenterPoint().x(),
195                  widget->GetWindowBoundsInScreen().CenterPoint().x(),
196                  1);
197    } else {
198      EXPECT_NEAR(icon_bounds.CenterPoint().y(),
199                  widget->GetWindowBoundsInScreen().CenterPoint().y(),
200                  1);
201    }
202  }
203
204  bool IsPanelCalloutVisible(aura::Window* panel) {
205    views::Widget* widget = GetCalloutWidgetForPanel(panel);
206    return widget->IsVisible();
207  }
208
209  test::ShelfViewTestAPI* shelf_view_test() {
210    return shelf_view_test_.get();
211  }
212
213  // Clicks the shelf items on |shelf_view| that is associated with given
214  // |window|.
215  void ClickShelfItemForWindow(ShelfView* shelf_view, aura::Window* window) {
216    test::ShelfViewTestAPI test_api(shelf_view);
217    test_api.SetAnimationDuration(1);
218    test_api.RunMessageLoopUntilAnimationsDone();
219    ShelfModel* model = test::ShellTestApi(Shell::GetInstance()).shelf_model();
220    int index = model->ItemIndexByID(GetShelfIDForWindow(window));
221    gfx::Rect bounds = test_api.GetButton(index)->GetBoundsInScreen();
222
223    aura::test::EventGenerator& event_generator = GetEventGenerator();
224    event_generator.MoveMouseTo(bounds.CenterPoint());
225    event_generator.ClickLeftButton();
226
227    test_api.RunMessageLoopUntilAnimationsDone();
228  }
229
230  void SetAlignment(aura::Window* root_window, ShelfAlignment alignment) {
231    ash::Shell* shell = ash::Shell::GetInstance();
232    shell->SetShelfAlignment(alignment, root_window);
233  }
234
235  ShelfAlignment GetAlignment(const aura::Window* root_window) {
236    ash::Shell* shell = ash::Shell::GetInstance();
237    return shell->GetShelfAlignment(root_window);
238  }
239
240  void SetShelfAutoHideBehavior(aura::Window* window,
241                                ShelfAutoHideBehavior behavior) {
242    ShelfLayoutManager* shelf = RootWindowController::ForWindow(window)
243                                    ->shelf()
244                                    ->shelf_layout_manager();
245    shelf->SetAutoHideBehavior(behavior);
246    ShelfView* shelf_view = GetShelfView(Shelf::ForWindow(window));
247    test::ShelfViewTestAPI test_api(shelf_view);
248    test_api.RunMessageLoopUntilAnimationsDone();
249  }
250
251  void SetShelfVisibilityState(aura::Window* window,
252                               ShelfVisibilityState visibility_state) {
253    ShelfLayoutManager* shelf = RootWindowController::ForWindow(window)
254                                    ->shelf()
255                                    ->shelf_layout_manager();
256    shelf->SetState(visibility_state);
257  }
258
259  ShelfView* GetShelfView(Shelf* shelf) {
260    return test::ShelfTestAPI(shelf).shelf_view();
261  }
262
263 private:
264  scoped_ptr<test::ShelfViewTestAPI> shelf_view_test_;
265
266  bool IsHorizontal(ShelfAlignment alignment) {
267    return alignment == SHELF_ALIGNMENT_BOTTOM ||
268           alignment == SHELF_ALIGNMENT_TOP;
269  }
270
271  DISALLOW_COPY_AND_ASSIGN(PanelLayoutManagerTest);
272};
273
274class PanelLayoutManagerTextDirectionTest
275    : public PanelLayoutManagerTest,
276      public testing::WithParamInterface<bool> {
277 public:
278  PanelLayoutManagerTextDirectionTest() : is_rtl_(GetParam()) {}
279  virtual ~PanelLayoutManagerTextDirectionTest() {}
280
281  virtual void SetUp() OVERRIDE {
282    original_locale = l10n_util::GetApplicationLocale(std::string());
283    if (is_rtl_)
284      base::i18n::SetICUDefaultLocale("he");
285    PanelLayoutManagerTest::SetUp();
286    ASSERT_EQ(is_rtl_, base::i18n::IsRTL());
287  }
288
289  virtual void TearDown() OVERRIDE {
290    if (is_rtl_)
291      base::i18n::SetICUDefaultLocale(original_locale);
292    PanelLayoutManagerTest::TearDown();
293  }
294
295 private:
296  bool is_rtl_;
297  std::string original_locale;
298
299  DISALLOW_COPY_AND_ASSIGN(PanelLayoutManagerTextDirectionTest);
300};
301
302// Tests that a created panel window is above the shelf icon in LTR and RTL.
303TEST_P(PanelLayoutManagerTextDirectionTest, AddOnePanel) {
304  gfx::Rect bounds(0, 0, 201, 201);
305  scoped_ptr<aura::Window> window(CreatePanelWindow(bounds));
306  EXPECT_EQ(GetPanelContainer(window.get()), window->parent());
307  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(window.get()));
308  EXPECT_NO_FATAL_FAILURE(IsCalloutAboveLauncherIcon(window.get()));
309}
310
311// Tests that a created panel window is successfully aligned over a hidden
312// shelf icon.
313TEST_F(PanelLayoutManagerTest, PanelAlignsToHiddenLauncherIcon) {
314  gfx::Rect bounds(0, 0, 201, 201);
315  SetShelfAutoHideBehavior(Shell::GetPrimaryRootWindow(),
316                           SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
317  scoped_ptr<aura::Window> normal_window(CreateNormalWindow(bounds));
318  scoped_ptr<aura::Window> window(CreatePanelWindow(bounds));
319  EXPECT_EQ(GetPanelContainer(window.get()), window->parent());
320  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(window.get()));
321}
322
323TEST_F(PanelLayoutManagerTest, PanelAlignsToHiddenLauncherIconSecondDisplay) {
324  if (!SupportsMultipleDisplays())
325    return;
326
327  // Keep the displays wide so that shelves have enough space for shelves
328  // buttons.
329  UpdateDisplay("400x400,600x400");
330  aura::Window::Windows root_windows = Shell::GetAllRootWindows();
331
332  scoped_ptr<aura::Window> normal_window(
333      CreateNormalWindow(gfx::Rect(450, 0, 100, 100)));
334  scoped_ptr<aura::Window> panel(CreatePanelWindow(gfx::Rect(400, 0, 50, 50)));
335  EXPECT_EQ(root_windows[1], panel->GetRootWindow());
336  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(panel.get()));
337  gfx::Rect shelf_visible_position = panel->GetBoundsInScreen();
338
339  SetShelfAutoHideBehavior(root_windows[1],
340                           SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
341  // Expect the panel X position to remain the same after the shelf is hidden
342  // but the Y to move down.
343  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(panel.get()));
344  EXPECT_EQ(shelf_visible_position.x(), panel->GetBoundsInScreen().x());
345  EXPECT_GT(panel->GetBoundsInScreen().y(), shelf_visible_position.y());
346}
347
348// Tests interactions between multiple panels
349TEST_F(PanelLayoutManagerTest, MultiplePanelsAreAboveIcons) {
350  gfx::Rect odd_bounds(0, 0, 201, 201);
351  gfx::Rect even_bounds(0, 0, 200, 200);
352
353  scoped_ptr<aura::Window> w1(CreatePanelWindow(odd_bounds));
354  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w1.get()));
355
356  scoped_ptr<aura::Window> w2(CreatePanelWindow(even_bounds));
357  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w1.get()));
358  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w2.get()));
359
360  scoped_ptr<aura::Window> w3(CreatePanelWindow(odd_bounds));
361  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w1.get()));
362  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w2.get()));
363  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w3.get()));
364}
365
366TEST_F(PanelLayoutManagerTest, MultiplePanelStacking) {
367  gfx::Rect bounds(0, 0, 201, 201);
368  scoped_ptr<aura::Window> w1(CreatePanelWindow(bounds));
369  scoped_ptr<aura::Window> w2(CreatePanelWindow(bounds));
370  scoped_ptr<aura::Window> w3(CreatePanelWindow(bounds));
371
372  // Default stacking order.
373  EXPECT_TRUE(WindowIsAbove(w3.get(), w2.get()));
374  EXPECT_TRUE(WindowIsAbove(w2.get(), w1.get()));
375
376  // Changing the active window should update the stacking order.
377  wm::ActivateWindow(w1.get());
378  shelf_view_test()->RunMessageLoopUntilAnimationsDone();
379  EXPECT_TRUE(WindowIsAbove(w1.get(), w2.get()));
380  EXPECT_TRUE(WindowIsAbove(w2.get(), w3.get()));
381
382  wm::ActivateWindow(w2.get());
383  shelf_view_test()->RunMessageLoopUntilAnimationsDone();
384  EXPECT_TRUE(WindowIsAbove(w1.get(), w3.get()));
385  EXPECT_TRUE(WindowIsAbove(w2.get(), w3.get()));
386  EXPECT_TRUE(WindowIsAbove(w2.get(), w1.get()));
387
388  wm::ActivateWindow(w3.get());
389  EXPECT_TRUE(WindowIsAbove(w3.get(), w2.get()));
390  EXPECT_TRUE(WindowIsAbove(w2.get(), w1.get()));
391}
392
393TEST_F(PanelLayoutManagerTest, MultiplePanelStackingVertical) {
394  // Set shelf to be aligned on the right.
395  SetAlignment(Shell::GetPrimaryRootWindow(), SHELF_ALIGNMENT_RIGHT);
396
397  // Size panels in such a way that ordering them by X coordinate would cause
398  // stacking order to be incorrect. Test that stacking order is based on Y.
399  scoped_ptr<aura::Window> w1(CreatePanelWindow(gfx::Rect(0, 0, 210, 201)));
400  scoped_ptr<aura::Window> w2(CreatePanelWindow(gfx::Rect(0, 0, 220, 201)));
401  scoped_ptr<aura::Window> w3(CreatePanelWindow(gfx::Rect(0, 0, 200, 201)));
402
403  // Default stacking order.
404  EXPECT_TRUE(WindowIsAbove(w3.get(), w2.get()));
405  EXPECT_TRUE(WindowIsAbove(w2.get(), w1.get()));
406
407  // Changing the active window should update the stacking order.
408  wm::ActivateWindow(w1.get());
409  shelf_view_test()->RunMessageLoopUntilAnimationsDone();
410  EXPECT_TRUE(WindowIsAbove(w1.get(), w2.get()));
411  EXPECT_TRUE(WindowIsAbove(w2.get(), w3.get()));
412
413  wm::ActivateWindow(w2.get());
414  shelf_view_test()->RunMessageLoopUntilAnimationsDone();
415  EXPECT_TRUE(WindowIsAbove(w1.get(), w3.get()));
416  EXPECT_TRUE(WindowIsAbove(w2.get(), w3.get()));
417  EXPECT_TRUE(WindowIsAbove(w2.get(), w1.get()));
418
419  wm::ActivateWindow(w3.get());
420  EXPECT_TRUE(WindowIsAbove(w3.get(), w2.get()));
421  EXPECT_TRUE(WindowIsAbove(w2.get(), w1.get()));
422}
423
424TEST_F(PanelLayoutManagerTest, MultiplePanelCallout) {
425  gfx::Rect bounds(0, 0, 200, 200);
426  scoped_ptr<aura::Window> w1(CreatePanelWindow(bounds));
427  scoped_ptr<aura::Window> w2(CreatePanelWindow(bounds));
428  scoped_ptr<aura::Window> w3(CreatePanelWindow(bounds));
429  scoped_ptr<aura::Window> w4(CreateNormalWindow(gfx::Rect()));
430  shelf_view_test()->RunMessageLoopUntilAnimationsDone();
431  EXPECT_TRUE(IsPanelCalloutVisible(w1.get()));
432  EXPECT_TRUE(IsPanelCalloutVisible(w2.get()));
433  EXPECT_TRUE(IsPanelCalloutVisible(w3.get()));
434  wm::ActivateWindow(w1.get());
435  EXPECT_NO_FATAL_FAILURE(IsCalloutAboveLauncherIcon(w1.get()));
436  wm::ActivateWindow(w2.get());
437  EXPECT_NO_FATAL_FAILURE(IsCalloutAboveLauncherIcon(w2.get()));
438  wm::ActivateWindow(w3.get());
439  EXPECT_NO_FATAL_FAILURE(IsCalloutAboveLauncherIcon(w3.get()));
440  wm::ActivateWindow(w4.get());
441  wm::ActivateWindow(w3.get());
442  EXPECT_NO_FATAL_FAILURE(IsCalloutAboveLauncherIcon(w3.get()));
443  w3.reset();
444  EXPECT_NO_FATAL_FAILURE(IsCalloutAboveLauncherIcon(w2.get()));
445}
446
447// Tests removing panels.
448TEST_F(PanelLayoutManagerTest, RemoveLeftPanel) {
449  gfx::Rect bounds(0, 0, 201, 201);
450  scoped_ptr<aura::Window> w1(CreatePanelWindow(bounds));
451  scoped_ptr<aura::Window> w2(CreatePanelWindow(bounds));
452  scoped_ptr<aura::Window> w3(CreatePanelWindow(bounds));
453
454  // At this point, windows should be stacked with 1 < 2 < 3
455  wm::ActivateWindow(w1.get());
456  shelf_view_test()->RunMessageLoopUntilAnimationsDone();
457  // Now, windows should be stacked 1 > 2 > 3
458  w1.reset();
459  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w2.get()));
460  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w3.get()));
461  EXPECT_TRUE(WindowIsAbove(w2.get(), w3.get()));
462}
463
464TEST_F(PanelLayoutManagerTest, RemoveMiddlePanel) {
465  gfx::Rect bounds(0, 0, 201, 201);
466  scoped_ptr<aura::Window> w1(CreatePanelWindow(bounds));
467  scoped_ptr<aura::Window> w2(CreatePanelWindow(bounds));
468  scoped_ptr<aura::Window> w3(CreatePanelWindow(bounds));
469
470  // At this point, windows should be stacked with 1 < 2 < 3
471  wm::ActivateWindow(w2.get());
472  // Windows should be stacked 1 < 2 > 3
473  w2.reset();
474  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w1.get()));
475  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w3.get()));
476  EXPECT_TRUE(WindowIsAbove(w3.get(), w1.get()));
477}
478
479TEST_F(PanelLayoutManagerTest, RemoveRightPanel) {
480  gfx::Rect bounds(0, 0, 201, 201);
481  scoped_ptr<aura::Window> w1(CreatePanelWindow(bounds));
482  scoped_ptr<aura::Window> w2(CreatePanelWindow(bounds));
483  scoped_ptr<aura::Window> w3(CreatePanelWindow(bounds));
484
485  // At this point, windows should be stacked with 1 < 2 < 3
486  wm::ActivateWindow(w3.get());
487  // Order shouldn't change.
488  w3.reset();
489  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w1.get()));
490  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w2.get()));
491  EXPECT_TRUE(WindowIsAbove(w2.get(), w1.get()));
492}
493
494TEST_F(PanelLayoutManagerTest, RemoveNonActivePanel) {
495  gfx::Rect bounds(0, 0, 201, 201);
496  scoped_ptr<aura::Window> w1(CreatePanelWindow(bounds));
497  scoped_ptr<aura::Window> w2(CreatePanelWindow(bounds));
498  scoped_ptr<aura::Window> w3(CreatePanelWindow(bounds));
499
500  // At this point, windows should be stacked with 1 < 2 < 3
501  wm::ActivateWindow(w2.get());
502  // Windows should be stacked 1 < 2 > 3
503  w1.reset();
504  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w2.get()));
505  EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w3.get()));
506  EXPECT_TRUE(WindowIsAbove(w2.get(), w3.get()));
507}
508
509TEST_F(PanelLayoutManagerTest, SplitView) {
510  gfx::Rect bounds(0, 0, 90, 201);
511  scoped_ptr<aura::Window> w1(CreatePanelWindow(bounds));
512  scoped_ptr<aura::Window> w2(CreatePanelWindow(bounds));
513
514  EXPECT_NO_FATAL_FAILURE(PanelsNotOverlapping(w1.get(), w2.get()));
515}
516
517#if defined(OS_WIN)
518// RootWindow and Display can't resize on Windows Ash. http://crbug.com/165962
519#define MAYBE_SplitViewOverlapWhenLarge DISABLED_SplitViewOverlapWhenLarge
520#else
521#define MAYBE_SplitViewOverlapWhenLarge SplitViewOverlapWhenLarge
522#endif
523
524TEST_F(PanelLayoutManagerTest, MAYBE_SplitViewOverlapWhenLarge) {
525  gfx::Rect bounds(0, 0, 600, 201);
526  scoped_ptr<aura::Window> w1(CreatePanelWindow(bounds));
527  scoped_ptr<aura::Window> w2(CreatePanelWindow(bounds));
528
529  EXPECT_NO_FATAL_FAILURE(PanelInScreen(w1.get()));
530  EXPECT_NO_FATAL_FAILURE(PanelInScreen(w2.get()));
531}
532
533TEST_F(PanelLayoutManagerTest, FanWindows) {
534  gfx::Rect bounds(0, 0, 201, 201);
535  scoped_ptr<aura::Window> w1(CreatePanelWindow(bounds));
536  scoped_ptr<aura::Window> w2(CreatePanelWindow(bounds));
537  scoped_ptr<aura::Window> w3(CreatePanelWindow(bounds));
538
539  shelf_view_test()->RunMessageLoopUntilAnimationsDone();
540  int window_x1 = w1->GetBoundsInRootWindow().CenterPoint().x();
541  int window_x2 = w2->GetBoundsInRootWindow().CenterPoint().x();
542  int window_x3 = w3->GetBoundsInRootWindow().CenterPoint().x();
543  Shelf* shelf = Shelf::ForPrimaryDisplay();
544  int icon_x1 = shelf->GetScreenBoundsOfItemIconForWindow(w1.get()).x();
545  int icon_x2 = shelf->GetScreenBoundsOfItemIconForWindow(w2.get()).x();
546  EXPECT_EQ(window_x2 - window_x1, window_x3 - window_x2);
547  int spacing = window_x2 - window_x1;
548  EXPECT_GT(spacing, icon_x2 - icon_x1);
549}
550
551TEST_F(PanelLayoutManagerTest, FanLargeWindow) {
552  gfx::Rect small_bounds(0, 0, 201, 201);
553  gfx::Rect large_bounds(0, 0, 501, 201);
554  scoped_ptr<aura::Window> w1(CreatePanelWindow(small_bounds));
555  scoped_ptr<aura::Window> w2(CreatePanelWindow(large_bounds));
556  scoped_ptr<aura::Window> w3(CreatePanelWindow(small_bounds));
557
558  shelf_view_test()->RunMessageLoopUntilAnimationsDone();
559  int window_x1 = w1->GetBoundsInRootWindow().CenterPoint().x();
560  int window_x2 = w2->GetBoundsInRootWindow().CenterPoint().x();
561  int window_x3 = w3->GetBoundsInRootWindow().CenterPoint().x();
562  // The distances may not be equidistant with a large panel but the panels
563  // should be in the correct order with respect to their midpoints.
564  EXPECT_GT(window_x2, window_x1);
565  EXPECT_GT(window_x3, window_x2);
566}
567
568TEST_F(PanelLayoutManagerTest, MinimizeRestorePanel) {
569  gfx::Rect bounds(0, 0, 201, 201);
570  scoped_ptr<aura::Window> window(CreatePanelWindow(bounds));
571  // Activate the window, ensure callout is visible.
572  wm::ActivateWindow(window.get());
573  RunAllPendingInMessageLoop();
574  EXPECT_TRUE(IsPanelCalloutVisible(window.get()));
575  // Minimize the panel, callout should be hidden.
576  wm::GetWindowState(window.get())->Minimize();
577  RunAllPendingInMessageLoop();
578  EXPECT_FALSE(IsPanelCalloutVisible(window.get()));
579  // Restore the panel; panel should not be activated by default but callout
580  // should be visible.
581  wm::GetWindowState(window.get())->Unminimize();
582  RunAllPendingInMessageLoop();
583  EXPECT_TRUE(IsPanelCalloutVisible(window.get()));
584  // Activate the window, ensure callout is visible.
585  wm::ActivateWindow(window.get());
586  RunAllPendingInMessageLoop();
587  EXPECT_TRUE(IsPanelCalloutVisible(window.get()));
588}
589
590TEST_F(PanelLayoutManagerTest, PanelMoveBetweenMultipleDisplays) {
591  if (!SupportsMultipleDisplays())
592    return;
593
594  // Keep the displays wide so that shelves have enough space for launcher
595  // buttons.
596  UpdateDisplay("600x400,600x400");
597  aura::Window::Windows root_windows = Shell::GetAllRootWindows();
598
599  scoped_ptr<aura::Window> p1_d1(CreatePanelWindow(gfx::Rect(0, 0, 50, 50)));
600  scoped_ptr<aura::Window> p2_d1(CreatePanelWindow(gfx::Rect(0, 0, 50, 50)));
601  scoped_ptr<aura::Window> p1_d2(CreatePanelWindow(gfx::Rect(600, 0, 50, 50)));
602  scoped_ptr<aura::Window> p2_d2(CreatePanelWindow(gfx::Rect(600, 0, 50, 50)));
603
604  ShelfView* shelf_view_1st = GetShelfView(Shelf::ForPrimaryDisplay());
605  ShelfView* shelf_view_2nd =
606      GetShelfView(Shelf::ForWindow(root_windows[1]));
607
608  EXPECT_EQ(root_windows[0], p1_d1->GetRootWindow());
609  EXPECT_EQ(root_windows[0], p2_d1->GetRootWindow());
610  EXPECT_EQ(root_windows[1], p1_d2->GetRootWindow());
611  EXPECT_EQ(root_windows[1], p2_d2->GetRootWindow());
612
613  EXPECT_EQ(kShellWindowId_PanelContainer, p1_d1->parent()->id());
614  EXPECT_EQ(kShellWindowId_PanelContainer, p2_d1->parent()->id());
615  EXPECT_EQ(kShellWindowId_PanelContainer, p1_d2->parent()->id());
616  EXPECT_EQ(kShellWindowId_PanelContainer, p2_d2->parent()->id());
617
618  // Test a panel on 1st display.
619  // Clicking on the same display has no effect.
620  ClickShelfItemForWindow(shelf_view_1st, p1_d1.get());
621  EXPECT_EQ(root_windows[0], p1_d1->GetRootWindow());
622  EXPECT_EQ(root_windows[0], p2_d1->GetRootWindow());
623  EXPECT_EQ(root_windows[1], p1_d2->GetRootWindow());
624  EXPECT_EQ(root_windows[1], p1_d2->GetRootWindow());
625  EXPECT_FALSE(root_windows[1]->GetBoundsInScreen().Contains(
626      p1_d1->GetBoundsInScreen()));
627
628  // Test if clicking on another display moves the panel to
629  // that display.
630  ClickShelfItemForWindow(shelf_view_2nd, p1_d1.get());
631  EXPECT_EQ(root_windows[1], p1_d1->GetRootWindow());
632  EXPECT_EQ(root_windows[0], p2_d1->GetRootWindow());
633  EXPECT_EQ(root_windows[1], p1_d2->GetRootWindow());
634  EXPECT_EQ(root_windows[1], p2_d2->GetRootWindow());
635  EXPECT_TRUE(root_windows[1]->GetBoundsInScreen().Contains(
636      p1_d1->GetBoundsInScreen()));
637
638  // Test a panel on 2nd display.
639  // Clicking on the same display has no effect.
640  ClickShelfItemForWindow(shelf_view_2nd, p1_d2.get());
641  EXPECT_EQ(root_windows[1], p1_d1->GetRootWindow());
642  EXPECT_EQ(root_windows[0], p2_d1->GetRootWindow());
643  EXPECT_EQ(root_windows[1], p1_d2->GetRootWindow());
644  EXPECT_EQ(root_windows[1], p2_d2->GetRootWindow());
645  EXPECT_TRUE(root_windows[1]->GetBoundsInScreen().Contains(
646      p1_d2->GetBoundsInScreen()));
647
648  // Test if clicking on another display moves the panel to
649  // that display.
650  ClickShelfItemForWindow(shelf_view_1st, p1_d2.get());
651  EXPECT_EQ(root_windows[1], p1_d1->GetRootWindow());
652  EXPECT_EQ(root_windows[0], p2_d1->GetRootWindow());
653  EXPECT_EQ(root_windows[0], p1_d2->GetRootWindow());
654  EXPECT_EQ(root_windows[1], p2_d2->GetRootWindow());
655  EXPECT_TRUE(root_windows[0]->GetBoundsInScreen().Contains(
656      p1_d2->GetBoundsInScreen()));
657
658  // Test if clicking on a previously moved window moves the
659  // panel back to the original display.
660  ClickShelfItemForWindow(shelf_view_1st, p1_d1.get());
661  EXPECT_EQ(root_windows[0], p1_d1->GetRootWindow());
662  EXPECT_EQ(root_windows[0], p2_d1->GetRootWindow());
663  EXPECT_EQ(root_windows[0], p1_d2->GetRootWindow());
664  EXPECT_EQ(root_windows[1], p2_d2->GetRootWindow());
665  EXPECT_TRUE(root_windows[0]->GetBoundsInScreen().Contains(
666      p1_d1->GetBoundsInScreen()));
667}
668
669TEST_F(PanelLayoutManagerTest, PanelAttachPositionMultipleDisplays) {
670  if (!SupportsMultipleDisplays())
671    return;
672
673  // Keep the displays wide so that shelves have enough space for shelf buttons.
674  // Use differently sized displays so the shelf is in a different
675  // position on second display.
676  UpdateDisplay("600x400,600x600");
677  aura::Window::Windows root_windows = Shell::GetAllRootWindows();
678
679  scoped_ptr<aura::Window> p1_d1(CreatePanelWindow(gfx::Rect(0, 0, 50, 50)));
680  scoped_ptr<aura::Window> p1_d2(CreatePanelWindow(gfx::Rect(600, 0, 50, 50)));
681
682  EXPECT_EQ(root_windows[0], p1_d1->GetRootWindow());
683  EXPECT_EQ(root_windows[1], p1_d2->GetRootWindow());
684
685  IsPanelAboveLauncherIcon(p1_d1.get());
686  IsCalloutAboveLauncherIcon(p1_d1.get());
687  IsPanelAboveLauncherIcon(p1_d2.get());
688  IsCalloutAboveLauncherIcon(p1_d2.get());
689}
690
691TEST_F(PanelLayoutManagerTest, PanelAlignmentSecondDisplay) {
692  if (!SupportsMultipleDisplays())
693    return;
694
695  UpdateDisplay("600x400,600x400");
696  aura::Window::Windows root_windows = Shell::GetAllRootWindows();
697
698  scoped_ptr<aura::Window> p1_d2(CreatePanelWindow(gfx::Rect(600, 0, 50, 50)));
699  EXPECT_EQ(root_windows[1], p1_d2->GetRootWindow());
700
701  IsPanelAboveLauncherIcon(p1_d2.get());
702  IsCalloutAboveLauncherIcon(p1_d2.get());
703
704  SetAlignment(root_windows[1], SHELF_ALIGNMENT_RIGHT);
705  IsPanelAboveLauncherIcon(p1_d2.get());
706  IsCalloutAboveLauncherIcon(p1_d2.get());
707  SetAlignment(root_windows[1], SHELF_ALIGNMENT_LEFT);
708  IsPanelAboveLauncherIcon(p1_d2.get());
709  IsCalloutAboveLauncherIcon(p1_d2.get());
710  SetAlignment(root_windows[1], SHELF_ALIGNMENT_TOP);
711  IsPanelAboveLauncherIcon(p1_d2.get());
712  IsCalloutAboveLauncherIcon(p1_d2.get());
713}
714
715TEST_F(PanelLayoutManagerTest, AlignmentLeft) {
716  gfx::Rect bounds(0, 0, 201, 201);
717  scoped_ptr<aura::Window> w(CreatePanelWindow(bounds));
718  SetAlignment(Shell::GetPrimaryRootWindow(), SHELF_ALIGNMENT_LEFT);
719  IsPanelAboveLauncherIcon(w.get());
720  IsCalloutAboveLauncherIcon(w.get());
721}
722
723TEST_F(PanelLayoutManagerTest, AlignmentRight) {
724  gfx::Rect bounds(0, 0, 201, 201);
725  scoped_ptr<aura::Window> w(CreatePanelWindow(bounds));
726  SetAlignment(Shell::GetPrimaryRootWindow(), SHELF_ALIGNMENT_RIGHT);
727  IsPanelAboveLauncherIcon(w.get());
728  IsCalloutAboveLauncherIcon(w.get());
729}
730
731TEST_F(PanelLayoutManagerTest, AlignmentTop) {
732  gfx::Rect bounds(0, 0, 201, 201);
733  scoped_ptr<aura::Window> w(CreatePanelWindow(bounds));
734  SetAlignment(Shell::GetPrimaryRootWindow(), SHELF_ALIGNMENT_TOP);
735  IsPanelAboveLauncherIcon(w.get());
736  IsCalloutAboveLauncherIcon(w.get());
737}
738
739// Tests that panels will hide and restore their state with the shelf visibility
740// state. This ensures that entering full-screen mode will hide your panels
741// until you leave it.
742TEST_F(PanelLayoutManagerTest, PanelsHideAndRestoreWithShelf) {
743  gfx::Rect bounds(0, 0, 201, 201);
744
745  scoped_ptr<aura::Window> w1(CreatePanelWindow(bounds));
746  scoped_ptr<aura::Window> w2(CreatePanelWindow(bounds));
747  scoped_ptr<aura::Window> w3;
748  // Minimize w2.
749  wm::GetWindowState(w2.get())->Minimize();
750  RunAllPendingInMessageLoop();
751  EXPECT_TRUE(w1->IsVisible());
752  EXPECT_FALSE(w2->IsVisible());
753
754  SetShelfVisibilityState(Shell::GetPrimaryRootWindow(), SHELF_HIDDEN);
755  RunAllPendingInMessageLoop();
756
757  // w3 is created while in full-screen mode, should only become visible when
758  // we exit fullscreen mode.
759  w3.reset(CreatePanelWindow(bounds));
760
761  EXPECT_FALSE(w1->IsVisible());
762  EXPECT_FALSE(w2->IsVisible());
763  EXPECT_FALSE(w3->IsVisible());
764
765  // While in full-screen mode, the panel windows should still be in the
766  // switchable window list - http://crbug.com/313919.
767  MruWindowTracker::WindowList switchable_window_list =
768      Shell::GetInstance()->mru_window_tracker()->BuildMruWindowList();
769  EXPECT_EQ(3u, switchable_window_list.size());
770  EXPECT_NE(switchable_window_list.end(),
771      std::find(switchable_window_list.begin(), switchable_window_list.end(),
772          w1.get()));
773  EXPECT_NE(switchable_window_list.end(),
774      std::find(switchable_window_list.begin(), switchable_window_list.end(),
775          w2.get()));
776  EXPECT_NE(switchable_window_list.end(),
777      std::find(switchable_window_list.begin(), switchable_window_list.end(),
778          w3.get()));
779
780  SetShelfVisibilityState(Shell::GetPrimaryRootWindow(), SHELF_VISIBLE);
781  RunAllPendingInMessageLoop();
782
783  // Windows should be restored to their prior state.
784  EXPECT_TRUE(w1->IsVisible());
785  EXPECT_FALSE(w2->IsVisible());
786  EXPECT_TRUE(w3->IsVisible());
787}
788
789// Verifies that touches along the attached edge of a panel do not
790// target the panel itself.
791TEST_F(PanelLayoutManagerTest, TouchHitTestPanel) {
792  aura::test::TestWindowDelegate delegate;
793  scoped_ptr<aura::Window> w(
794      CreatePanelWindowWithDelegate(&delegate, gfx::Rect(0, 0, 200, 200)));
795  ui::EventTarget* root = w->GetRootWindow();
796  ui::EventTargeter* targeter = root->GetEventTargeter();
797
798  // Note that the constants used in the touch locations below are
799  // arbitrarily-selected small numbers which will ensure the point is
800  // within the default extended region surrounding the panel. This value
801  // is calculated as
802  // kResizeOutsideBoundsSize * kResizeOutsideBoundsScaleForTouch
803  // in src/ash/root_window_controller.cc.
804
805  // Hit test outside the right edge with a bottom-aligned shelf.
806  SetAlignment(Shell::GetPrimaryRootWindow(), SHELF_ALIGNMENT_BOTTOM);
807  gfx::Rect bounds(w->bounds());
808  ui::TouchEvent touch(ui::ET_TOUCH_PRESSED,
809                       gfx::Point(bounds.right() + 3, bounds.y() + 2),
810                       0, ui::EventTimeForNow());
811  ui::EventTarget* target = targeter->FindTargetForEvent(root, &touch);
812  EXPECT_EQ(w.get(), target);
813
814  // Hit test outside the bottom edge with a bottom-aligned shelf.
815  touch.set_location(gfx::Point(bounds.x() + 6, bounds.bottom() + 5));
816  target = targeter->FindTargetForEvent(root, &touch);
817  EXPECT_NE(w.get(), target);
818
819  // Hit test outside the bottom edge with a right-aligned shelf.
820  SetAlignment(Shell::GetPrimaryRootWindow(), SHELF_ALIGNMENT_RIGHT);
821  bounds = w->bounds();
822  touch.set_location(gfx::Point(bounds.x() + 6, bounds.bottom() + 5));
823  target = targeter->FindTargetForEvent(root, &touch);
824  EXPECT_EQ(w.get(), target);
825
826  // Hit test outside the right edge with a right-aligned shelf.
827  touch.set_location(gfx::Point(bounds.right() + 3, bounds.y() + 2));
828  target = targeter->FindTargetForEvent(root, &touch);
829  EXPECT_NE(w.get(), target);
830
831  // Hit test outside the top edge with a left-aligned shelf.
832  SetAlignment(Shell::GetPrimaryRootWindow(), SHELF_ALIGNMENT_LEFT);
833  bounds = w->bounds();
834  touch.set_location(gfx::Point(bounds.x() + 4, bounds.y() - 6));
835  target = targeter->FindTargetForEvent(root, &touch);
836  EXPECT_EQ(w.get(), target);
837
838  // Hit test outside the left edge with a left-aligned shelf.
839  touch.set_location(gfx::Point(bounds.x() - 1, bounds.y() + 5));
840  target = targeter->FindTargetForEvent(root, &touch);
841  EXPECT_NE(w.get(), target);
842
843  // Hit test outside the left edge with a top-aligned shelf.
844  SetAlignment(Shell::GetPrimaryRootWindow(), SHELF_ALIGNMENT_TOP);
845  bounds = w->bounds();
846  touch.set_location(gfx::Point(bounds.x() - 1, bounds.y() + 5));
847  target = targeter->FindTargetForEvent(root, &touch);
848  EXPECT_EQ(w.get(), target);
849
850  // Hit test outside the top edge with a top-aligned shelf.
851  touch.set_location(gfx::Point(bounds.x() + 4, bounds.y() - 6));
852  target = targeter->FindTargetForEvent(root, &touch);
853  EXPECT_NE(w.get(), target);
854}
855
856INSTANTIATE_TEST_CASE_P(LtrRtl, PanelLayoutManagerTextDirectionTest,
857                        testing::Bool());
858
859}  // namespace ash
860