1 // Copyright 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.
7 #include "ash/accessibility_delegate.h"
8 #include "ash/drag_drop/drag_drop_controller.h"
9 #include "ash/root_window_controller.h"
10 #include "ash/screen_util.h"
11 #include "ash/shelf/shelf.h"
12 #include "ash/shelf/shelf_widget.h"
13 #include "ash/shell.h"
14 #include "ash/shell_window_ids.h"
15 #include "ash/test/ash_test_base.h"
16 #include "ash/test/shelf_test_api.h"
17 #include "ash/test/shelf_view_test_api.h"
18 #include "ash/test/shell_test_api.h"
19 #include "ash/test/test_shelf_delegate.h"
20 #include "ash/wm/maximize_mode/maximize_mode_controller.h"
21 #include "ash/wm/mru_window_tracker.h"
22 #include "ash/wm/overview/window_grid.h"
23 #include "ash/wm/overview/window_selector.h"
24 #include "ash/wm/overview/window_selector_controller.h"
25 #include "ash/wm/overview/window_selector_item.h"
26 #include "ash/wm/panels/panel_layout_manager.h"
27 #include "ash/wm/window_state.h"
28 #include "ash/wm/window_util.h"
29 #include "ash/wm/wm_event.h"
30 #include "base/basictypes.h"
31 #include "base/compiler_specific.h"
32 #include "base/memory/scoped_vector.h"
33 #include "base/run_loop.h"
34 #include "base/strings/string_piece.h"
35 #include "base/strings/utf_string_conversions.h"
36 #include "ui/aura/client/aura_constants.h"
37 #include "ui/aura/client/cursor_client.h"
38 #include "ui/aura/client/focus_client.h"
39 #include "ui/aura/test/test_window_delegate.h"
40 #include "ui/aura/test/test_windows.h"
41 #include "ui/aura/window.h"
42 #include "ui/aura/window_event_dispatcher.h"
43 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
44 #include "ui/events/test/event_generator.h"
45 #include "ui/gfx/geometry/point_conversions.h"
46 #include "ui/gfx/geometry/rect_conversions.h"
47 #include "ui/gfx/transform.h"
48 #include "ui/gfx/transform_util.h"
49 #include "ui/views/controls/label.h"
50 #include "ui/views/widget/native_widget_aura.h"
51 #include "ui/views/widget/widget_delegate.h"
52 #include "ui/wm/core/window_util.h"
53 #include "ui/wm/public/activation_delegate.h"
58 typedef std::vector
<aura::Window
*> WindowList
;
59 typedef std::map
<const aura::Window
*, int> WindowIndexMap
;
61 class NonActivatableActivationDelegate
62 : public aura::client::ActivationDelegate
{
64 bool ShouldActivate() const override
{ return false; }
67 void CancelDrag(DragDropController
* controller
, bool* canceled
) {
68 if (controller
->IsDragDropInProgress()) {
70 controller
->DragCancel();
74 // Fills the |index_map| with (window*, list index) entries based on each
75 // windows index in the |windows| list.
76 void GetWindowIndexMap(const WindowList
& windows
,
77 WindowIndexMap
* index_map
) {
79 for (auto window
: windows
) {
80 (*index_map
)[window
] = index
;
87 class WindowSelectorTest
: public test::AshTestBase
{
89 WindowSelectorTest() {}
90 ~WindowSelectorTest() override
{}
92 void SetUp() override
{
93 test::AshTestBase::SetUp();
94 ASSERT_TRUE(test::TestShelfDelegate::instance());
96 shelf_view_test_
.reset(new test::ShelfViewTestAPI(
97 test::ShelfTestAPI(Shelf::ForPrimaryDisplay()).shelf_view()));
98 shelf_view_test_
->SetAnimationDuration(1);
101 aura::Window
* CreateWindow(const gfx::Rect
& bounds
) {
102 return CreateTestWindowInShellWithDelegate(&delegate_
, -1, bounds
);
105 aura::Window
* CreateWindowWithId(const gfx::Rect
& bounds
, int id
) {
106 return CreateTestWindowInShellWithDelegate(&delegate_
, id
, bounds
);
108 aura::Window
* CreateNonActivatableWindow(const gfx::Rect
& bounds
) {
109 aura::Window
* window
= CreateWindow(bounds
);
110 aura::client::SetActivationDelegate(window
,
111 &non_activatable_activation_delegate_
);
112 EXPECT_FALSE(ash::wm::CanActivateWindow(window
));
116 aura::Window
* CreatePanelWindow(const gfx::Rect
& bounds
) {
117 aura::Window
* window
= CreateTestWindowInShellWithDelegateAndType(
118 nullptr, ui::wm::WINDOW_TYPE_PANEL
, 0, bounds
);
119 test::TestShelfDelegate::instance()->AddShelfItem(window
);
120 shelf_view_test()->RunMessageLoopUntilAnimationsDone();
124 views::Widget
* CreatePanelWindowWidget(const gfx::Rect
& bounds
) {
125 views::Widget
* widget
= new views::Widget
;
126 views::Widget::InitParams params
;
127 params
.bounds
= bounds
;
128 params
.type
= views::Widget::InitParams::TYPE_PANEL
;
129 params
.ownership
= views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
130 widget
->Init(params
);
132 ParentWindowInPrimaryRootWindow(widget
->GetNativeWindow());
136 bool WindowsOverlapping(aura::Window
* window1
, aura::Window
* window2
) {
137 gfx::RectF window1_bounds
= GetTransformedTargetBounds(window1
);
138 gfx::RectF window2_bounds
= GetTransformedTargetBounds(window2
);
139 return window1_bounds
.Intersects(window2_bounds
);
142 void ToggleOverview() {
143 ash::Shell::GetInstance()->window_selector_controller()->ToggleOverview();
146 gfx::RectF
GetTransformedBounds(aura::Window
* window
) {
147 gfx::RectF
bounds(ScreenUtil::ConvertRectToScreen(
148 window
->parent(), window
->layer()->bounds()));
149 gfx::Transform
transform(gfx::TransformAboutPivot(
150 gfx::ToFlooredPoint(bounds
.origin()),
151 window
->layer()->transform()));
152 transform
.TransformRect(&bounds
);
156 gfx::RectF
GetTransformedTargetBounds(aura::Window
* window
) {
157 gfx::RectF
bounds(ScreenUtil::ConvertRectToScreen(
158 window
->parent(), window
->layer()->GetTargetBounds()));
159 gfx::Transform
transform(gfx::TransformAboutPivot(
160 gfx::ToFlooredPoint(bounds
.origin()),
161 window
->layer()->GetTargetTransform()));
162 transform
.TransformRect(&bounds
);
166 gfx::RectF
GetTransformedBoundsInRootWindow(aura::Window
* window
) {
167 gfx::RectF bounds
= gfx::Rect(window
->bounds().size());
168 aura::Window
* root
= window
->GetRootWindow();
169 CHECK(window
->layer());
170 CHECK(root
->layer());
171 gfx::Transform transform
;
172 if (!window
->layer()->GetTargetTransformRelativeTo(root
->layer(),
176 transform
.TransformRect(&bounds
);
180 void ClickWindow(aura::Window
* window
) {
181 ui::test::EventGenerator
event_generator(window
->GetRootWindow(), window
);
182 gfx::RectF target
= GetTransformedBounds(window
);
183 event_generator
.ClickLeftButton();
186 void SendKey(ui::KeyboardCode key
) {
187 ui::test::EventGenerator
event_generator(Shell::GetPrimaryRootWindow());
188 event_generator
.PressKey(key
, 0);
189 event_generator
.ReleaseKey(key
, 0);
193 return ash::Shell::GetInstance()->window_selector_controller()->
197 aura::Window
* GetFocusedWindow() {
198 return aura::client::GetFocusClient(
199 Shell::GetPrimaryRootWindow())->GetFocusedWindow();
202 const std::vector
<WindowSelectorItem
*>& GetWindowItemsForRoot(int index
) {
203 return ash::Shell::GetInstance()->window_selector_controller()->
204 window_selector_
->grid_list_
[index
]->window_list_
.get();
207 const aura::Window
* GetSelectedWindow() {
208 WindowSelector
* ws
= ash::Shell::GetInstance()->
209 window_selector_controller()->window_selector_
.get();
210 return ws
->grid_list_
[ws
->selected_grid_index_
]->
211 SelectedWindow()->SelectionWindow();
214 bool selection_widget_active() {
215 WindowSelector
* ws
= ash::Shell::GetInstance()->
216 window_selector_controller()->window_selector_
.get();
217 return ws
->grid_list_
[ws
->selected_grid_index_
]->is_selecting();
220 bool showing_filter_widget() {
221 WindowSelector
* ws
= ash::Shell::GetInstance()->
222 window_selector_controller()->window_selector_
.get();
223 return ws
->text_filter_widget_
->GetNativeWindow()->layer()->
224 GetTargetTransform().IsIdentity();
227 views::Widget
* GetCloseButton(ash::WindowSelectorItem
* window
) {
228 return &(window
->close_button_widget_
);
231 views::Label
* GetLabelView(ash::WindowSelectorItem
* window
) {
232 return window
->window_label_view_
;
235 // Tests that a window is contained within a given WindowSelectorItem, and
236 // that both the window and its matching close button are within the same
238 void IsWindowAndCloseButtonInScreen(aura::Window
* window
,
239 WindowSelectorItem
* window_item
) {
240 aura::Window
* root_window
= window_item
->root_window();
241 EXPECT_TRUE(window_item
->Contains(window
));
242 EXPECT_TRUE(root_window
->GetBoundsInScreen().Contains(
243 ToEnclosingRect(GetTransformedTargetBounds(window
))));
244 EXPECT_TRUE(root_window
->GetBoundsInScreen().Contains(
245 ToEnclosingRect(GetTransformedTargetBounds(
246 GetCloseButton(window_item
)->GetNativeView()))));
249 void FilterItems(const base::StringPiece
& pattern
) {
250 ash::Shell::GetInstance()->
251 window_selector_controller()->window_selector_
.get()->
252 ContentsChanged(nullptr, base::UTF8ToUTF16(pattern
));
255 test::ShelfViewTestAPI
* shelf_view_test() {
256 return shelf_view_test_
.get();
259 views::Widget
* text_filter_widget() {
260 return ash::Shell::GetInstance()->
261 window_selector_controller()->window_selector_
.get()->
262 text_filter_widget_
.get();
266 aura::test::TestWindowDelegate delegate_
;
267 NonActivatableActivationDelegate non_activatable_activation_delegate_
;
268 scoped_ptr
<test::ShelfViewTestAPI
> shelf_view_test_
;
270 DISALLOW_COPY_AND_ASSIGN(WindowSelectorTest
);
273 // Tests that an a11y alert is sent on entering overview mode.
274 TEST_F(WindowSelectorTest
, A11yAlertOnOverviewMode
) {
275 gfx::Rect
bounds(0, 0, 400, 400);
276 AccessibilityDelegate
* delegate
=
277 ash::Shell::GetInstance()->accessibility_delegate();
278 scoped_ptr
<aura::Window
> window1(CreateWindow(bounds
));
279 EXPECT_NE(delegate
->GetLastAccessibilityAlert(),
280 ui::A11Y_ALERT_WINDOW_OVERVIEW_MODE_ENTERED
);
282 EXPECT_EQ(delegate
->GetLastAccessibilityAlert(),
283 ui::A11Y_ALERT_WINDOW_OVERVIEW_MODE_ENTERED
);
286 // Tests entering overview mode with two windows and selecting one by clicking.
287 TEST_F(WindowSelectorTest
, Basic
) {
288 gfx::Rect
bounds(0, 0, 400, 400);
289 aura::Window
* root_window
= Shell::GetPrimaryRootWindow();
290 scoped_ptr
<aura::Window
> window1(CreateWindow(bounds
));
291 scoped_ptr
<aura::Window
> window2(CreateWindow(bounds
));
292 scoped_ptr
<aura::Window
> panel1(CreatePanelWindow(bounds
));
293 scoped_ptr
<aura::Window
> panel2(CreatePanelWindow(bounds
));
294 EXPECT_TRUE(WindowsOverlapping(window1
.get(), window2
.get()));
295 EXPECT_TRUE(WindowsOverlapping(panel1
.get(), panel2
.get()));
296 wm::ActivateWindow(window2
.get());
297 EXPECT_FALSE(wm::IsActiveWindow(window1
.get()));
298 EXPECT_TRUE(wm::IsActiveWindow(window2
.get()));
299 EXPECT_EQ(window2
.get(), GetFocusedWindow());
300 // Hide the cursor before entering overview to test that it will be shown.
301 aura::client::GetCursorClient(root_window
)->HideCursor();
303 // In overview mode the windows should no longer overlap and the text filter
304 // widget should be focused.
306 EXPECT_EQ(text_filter_widget()->GetNativeWindow(), GetFocusedWindow());
307 EXPECT_FALSE(WindowsOverlapping(window1
.get(), window2
.get()));
308 EXPECT_FALSE(WindowsOverlapping(window1
.get(), panel1
.get()));
309 // Panels 1 and 2 should still be overlapping being in a single selector
311 EXPECT_TRUE(WindowsOverlapping(panel1
.get(), panel2
.get()));
313 // Clicking window 1 should activate it.
314 ClickWindow(window1
.get());
315 EXPECT_TRUE(wm::IsActiveWindow(window1
.get()));
316 EXPECT_FALSE(wm::IsActiveWindow(window2
.get()));
317 EXPECT_EQ(window1
.get(), GetFocusedWindow());
319 // Cursor should have been unlocked.
320 EXPECT_FALSE(aura::client::GetCursorClient(root_window
)->IsCursorLocked());
323 // Tests selecting a window by tapping on it.
324 TEST_F(WindowSelectorTest
, BasicGesture
) {
325 gfx::Rect
bounds(0, 0, 400, 400);
326 scoped_ptr
<aura::Window
> window1(CreateWindow(bounds
));
327 scoped_ptr
<aura::Window
> window2(CreateWindow(bounds
));
328 wm::ActivateWindow(window1
.get());
329 EXPECT_EQ(window1
.get(), GetFocusedWindow());
331 EXPECT_EQ(text_filter_widget()->GetNativeWindow(), GetFocusedWindow());
332 ui::test::EventGenerator
generator(Shell::GetPrimaryRootWindow(),
334 generator
.GestureTapAt(gfx::ToEnclosingRect(
335 GetTransformedTargetBounds(window2
.get())).CenterPoint());
336 EXPECT_EQ(window2
.get(), GetFocusedWindow());
339 // Tests that we do not crash and overview mode remains engaged if the desktop
340 // is tapped while a finger is already down over a window.
341 TEST_F(WindowSelectorTest
, NoCrashWithDesktopTap
) {
342 scoped_ptr
<aura::Window
> window(CreateWindow(gfx::Rect(200, 300, 250, 450)));
344 // We need a widget for the close button to work, a bare window will crash.
345 scoped_ptr
<views::Widget
> widget(new views::Widget
);
346 views::Widget::InitParams params
;
347 params
.bounds
= gfx::Rect(0, 0, 400, 400);
348 params
.ownership
= views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
349 params
.parent
= window
->parent();
350 widget
->Init(params
);
356 gfx::ToEnclosingRect(GetTransformedBoundsInRootWindow(window
.get()));
357 ui::test::EventGenerator
event_generator(window
->GetRootWindow(),
358 bounds
.CenterPoint());
360 // Press down on the window.
361 const int kTouchId
= 19;
362 event_generator
.PressTouchId(kTouchId
);
364 // Tap on the desktop, which should not cause a crash. Overview mode should
365 // remain engaged because the transparent widget over the window has capture.
366 event_generator
.GestureTapAt(gfx::Point(0, 0));
367 EXPECT_TRUE(IsSelecting());
369 event_generator
.ReleaseTouchId(kTouchId
);
372 // Tests that we do not crash and a window is selected when appropriate when
373 // we click on a window during touch.
374 TEST_F(WindowSelectorTest
, ClickOnWindowDuringTouch
) {
375 gfx::Rect
bounds(0, 0, 400, 400);
376 scoped_ptr
<aura::Window
> window1(CreateWindow(bounds
));
377 scoped_ptr
<aura::Window
> window2(CreateWindow(bounds
));
378 wm::ActivateWindow(window2
.get());
379 EXPECT_FALSE(wm::IsActiveWindow(window1
.get()));
380 EXPECT_TRUE(wm::IsActiveWindow(window2
.get()));
384 gfx::Rect window1_bounds
=
385 gfx::ToEnclosingRect(GetTransformedBoundsInRootWindow(window1
.get()));
386 ui::test::EventGenerator
event_generator(window1
->GetRootWindow(),
387 window1_bounds
.CenterPoint());
389 // Clicking on |window2| while touching on |window1| should not cause a
390 // crash, and overview mode should remain engaged because |window1|
392 const int kTouchId
= 19;
393 event_generator
.PressTouchId(kTouchId
);
394 event_generator
.MoveMouseToCenterOf(window2
.get());
395 event_generator
.ClickLeftButton();
396 EXPECT_TRUE(IsSelecting());
397 event_generator
.ReleaseTouchId(kTouchId
);
399 // Clicking on |window1| while touching on |window1| should not cause
400 // a crash, overview mode should be disengaged, and |window1| should
402 event_generator
.MoveMouseToCenterOf(window1
.get());
403 event_generator
.PressTouchId(kTouchId
);
404 event_generator
.ClickLeftButton();
405 EXPECT_FALSE(IsSelecting());
406 EXPECT_TRUE(wm::IsActiveWindow(window1
.get()));
407 EXPECT_FALSE(wm::IsActiveWindow(window2
.get()));
408 event_generator
.ReleaseTouchId(kTouchId
);
411 // Tests that a window does not receive located events when in overview mode.
412 TEST_F(WindowSelectorTest
, WindowDoesNotReceiveEvents
) {
413 gfx::Rect
window_bounds(20, 10, 200, 300);
414 aura::Window
* root_window
= Shell::GetPrimaryRootWindow();
415 scoped_ptr
<aura::Window
> window(CreateWindow(window_bounds
));
417 gfx::Point
point1(window_bounds
.x() + 10, window_bounds
.y() + 10);
419 ui::MouseEvent
event1(ui::ET_MOUSE_PRESSED
, point1
, point1
,
420 ui::EF_NONE
, ui::EF_NONE
);
422 ui::EventTarget
* root_target
= root_window
;
423 ui::EventTargeter
* targeter
= root_target
->GetEventTargeter();
425 // The event should target the window because we are still not in overview
427 EXPECT_EQ(window
, static_cast<aura::Window
*>(
428 targeter
->FindTargetForEvent(root_target
, &event1
)));
432 // The bounds have changed, take that into account.
433 gfx::RectF bounds
= GetTransformedBoundsInRootWindow(window
.get());
434 gfx::Point
point2(bounds
.x() + 10, bounds
.y() + 10);
435 ui::MouseEvent
event2(ui::ET_MOUSE_PRESSED
, point2
, point2
,
436 ui::EF_NONE
, ui::EF_NONE
);
438 // Now the transparent window should be intercepting this event.
439 EXPECT_NE(window
, static_cast<aura::Window
*>(
440 targeter
->FindTargetForEvent(root_target
, &event2
)));
443 // Tests that clicking on the close button effectively closes the window.
444 TEST_F(WindowSelectorTest
, CloseButton
) {
445 scoped_ptr
<aura::Window
> window1(CreateWindow(gfx::Rect(200, 300, 250, 450)));
447 // We need a widget for the close button to work, a bare window will crash.
448 scoped_ptr
<views::Widget
> widget(new views::Widget
);
449 views::Widget::InitParams params
;
450 params
.bounds
= gfx::Rect(0, 0, 400, 400);
451 params
.ownership
= views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
452 params
.parent
= window1
->parent();
453 widget
->Init(params
);
457 aura::Window
* window2
= widget
->GetNativeWindow();
458 gfx::RectF bounds
= GetTransformedBoundsInRootWindow(window2
);
459 gfx::Point
point(bounds
.top_right().x() - 1, bounds
.top_right().y() - 1);
460 ui::test::EventGenerator
event_generator(window2
->GetRootWindow(), point
);
462 EXPECT_FALSE(widget
->IsClosed());
463 event_generator
.ClickLeftButton();
464 EXPECT_TRUE(widget
->IsClosed());
467 // Tests that clicking on the close button on a secondary display effectively
468 // closes the window.
469 TEST_F(WindowSelectorTest
, CloseButtonOnMultipleDisplay
) {
470 if (!SupportsMultipleDisplays())
473 UpdateDisplay("600x400,600x400");
474 aura::Window::Windows root_windows
= Shell::GetAllRootWindows();
476 scoped_ptr
<aura::Window
> window1(CreateWindow(gfx::Rect(650, 300, 250, 450)));
478 // We need a widget for the close button to work, a bare window will crash.
479 scoped_ptr
<views::Widget
> widget(new views::Widget
);
480 views::Widget::InitParams params
;
481 params
.bounds
= gfx::Rect(650, 0, 400, 400);
482 params
.ownership
= views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
483 params
.parent
= window1
->parent();
484 widget
->Init(params
);
487 ASSERT_EQ(root_windows
[1], window1
->GetRootWindow());
491 aura::Window
* window2
= widget
->GetNativeWindow();
492 gfx::RectF bounds
= GetTransformedBoundsInRootWindow(window2
);
493 gfx::Point
point(bounds
.top_right().x() - 1, bounds
.top_right().y() - 1);
494 ui::test::EventGenerator
event_generator(window2
->GetRootWindow(), point
);
496 EXPECT_FALSE(widget
->IsClosed());
497 event_generator
.ClickLeftButton();
498 EXPECT_TRUE(widget
->IsClosed());
501 // Tests entering overview mode with two windows and selecting one.
502 TEST_F(WindowSelectorTest
, FullscreenWindow
) {
503 gfx::Rect
bounds(0, 0, 400, 400);
504 scoped_ptr
<aura::Window
> window1(CreateWindow(bounds
));
505 scoped_ptr
<aura::Window
> window2(CreateWindow(bounds
));
506 scoped_ptr
<aura::Window
> panel1(CreatePanelWindow(bounds
));
507 wm::ActivateWindow(window1
.get());
509 const wm::WMEvent
toggle_fullscreen_event(wm::WM_EVENT_TOGGLE_FULLSCREEN
);
510 wm::GetWindowState(window1
.get())->OnWMEvent(&toggle_fullscreen_event
);
511 // The panel is hidden in fullscreen mode.
512 EXPECT_FALSE(panel1
->IsVisible());
513 EXPECT_TRUE(wm::GetWindowState(window1
.get())->IsFullscreen());
515 // Enter overview and select the fullscreen window.
518 // The panel becomes temporarily visible for the overview.
519 EXPECT_TRUE(panel1
->IsVisible());
520 ClickWindow(window1
.get());
522 // The window is still fullscreen as it was selected. The panel should again
524 EXPECT_TRUE(wm::GetWindowState(window1
.get())->IsFullscreen());
525 EXPECT_FALSE(panel1
->IsVisible());
527 // Entering overview and selecting another window, the previous window remains
529 // TODO(flackr): Currently the panel remains hidden, but should become visible
532 ClickWindow(window2
.get());
533 EXPECT_TRUE(wm::GetWindowState(window1
.get())->IsFullscreen());
536 // Tests that the shelf dimming state is removed while in overview and restored
537 // on exiting overview.
538 TEST_F(WindowSelectorTest
, OverviewUndimsShelf
) {
539 gfx::Rect
bounds(0, 0, 400, 400);
540 scoped_ptr
<aura::Window
> window1(CreateWindow(bounds
));
541 wm::WindowState
* window_state
= wm::GetWindowState(window1
.get());
542 window_state
->Maximize();
543 ash::ShelfWidget
* shelf
= Shell::GetPrimaryRootWindowController()->shelf();
544 EXPECT_TRUE(shelf
->GetDimsShelf());
546 EXPECT_FALSE(shelf
->GetDimsShelf());
548 EXPECT_TRUE(shelf
->GetDimsShelf());
551 // Tests that entering overview when a fullscreen window is active in maximized
552 // mode correctly applies the transformations to the window and correctly
553 // updates the window bounds on exiting overview mode: http://crbug.com/401664.
554 TEST_F(WindowSelectorTest
, FullscreenWindowMaximizeMode
) {
555 gfx::Rect
bounds(0, 0, 400, 400);
556 scoped_ptr
<aura::Window
> window1(CreateWindow(bounds
));
557 scoped_ptr
<aura::Window
> window2(CreateWindow(bounds
));
558 Shell::GetInstance()->maximize_mode_controller()->
559 EnableMaximizeModeWindowManager(true);
560 wm::ActivateWindow(window2
.get());
561 wm::ActivateWindow(window1
.get());
562 gfx::Rect
normal_window_bounds(window1
->bounds());
563 const wm::WMEvent
toggle_fullscreen_event(wm::WM_EVENT_TOGGLE_FULLSCREEN
);
564 wm::GetWindowState(window1
.get())->OnWMEvent(&toggle_fullscreen_event
);
565 gfx::Rect
fullscreen_window_bounds(window1
->bounds());
566 EXPECT_NE(normal_window_bounds
.ToString(),
567 fullscreen_window_bounds
.ToString());
568 EXPECT_EQ(fullscreen_window_bounds
.ToString(),
569 window2
->GetTargetBounds().ToString());
571 // Window 2 would normally resize to normal window bounds on showing the shelf
572 // for overview but this is deferred until overview is exited.
573 EXPECT_EQ(fullscreen_window_bounds
.ToString(),
574 window2
->GetTargetBounds().ToString());
575 EXPECT_FALSE(WindowsOverlapping(window1
.get(), window2
.get()));
578 // Since the fullscreen window is still active, window2 will still have the
580 EXPECT_EQ(fullscreen_window_bounds
.ToString(),
581 window2
->GetTargetBounds().ToString());
583 // Enter overview again and select window 2. Selecting window 2 should show
584 // the shelf bringing window2 back to the normal bounds.
586 ClickWindow(window2
.get());
587 EXPECT_EQ(normal_window_bounds
.ToString(),
588 window2
->GetTargetBounds().ToString());
591 // Tests that beginning window selection hides the app list.
592 TEST_F(WindowSelectorTest
, SelectingHidesAppList
) {
593 gfx::Rect
bounds(0, 0, 400, 400);
594 scoped_ptr
<aura::Window
> window1(CreateWindow(bounds
));
595 scoped_ptr
<aura::Window
> window2(CreateWindow(bounds
));
596 Shell::GetInstance()->ShowAppList(nullptr);
597 EXPECT_TRUE(Shell::GetInstance()->GetAppListTargetVisibility());
599 EXPECT_FALSE(Shell::GetInstance()->GetAppListTargetVisibility());
603 // Tests that a minimized window's visibility and layer visibility is correctly
604 // changed when entering overview and restored when leaving overview mode.
605 // Crashes after the skia roll in http://crrev.com/274114.
606 // http://crbug.com/379570
607 TEST_F(WindowSelectorTest
, DISABLED_MinimizedWindowVisibility
) {
608 gfx::Rect
bounds(0, 0, 400, 400);
609 scoped_ptr
<aura::Window
> window1(CreateWindow(bounds
));
610 wm::WindowState
* window_state
= wm::GetWindowState(window1
.get());
611 window_state
->Minimize();
612 EXPECT_FALSE(window1
->IsVisible());
613 EXPECT_FALSE(window1
->layer()->GetTargetVisibility());
615 ui::ScopedAnimationDurationScaleMode
test_duration_mode(
616 ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION
);
618 EXPECT_TRUE(window1
->IsVisible());
619 EXPECT_TRUE(window1
->layer()->GetTargetVisibility());
622 ui::ScopedAnimationDurationScaleMode
test_duration_mode(
623 ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION
);
625 EXPECT_FALSE(window1
->IsVisible());
626 EXPECT_FALSE(window1
->layer()->GetTargetVisibility());
630 // Tests that a bounds change during overview is corrected for.
631 TEST_F(WindowSelectorTest
, BoundsChangeDuringOverview
) {
632 scoped_ptr
<aura::Window
> window(CreateWindow(gfx::Rect(0, 0, 400, 400)));
634 gfx::Rect overview_bounds
=
635 ToEnclosingRect(GetTransformedTargetBounds(window
.get()));
636 window
->SetBounds(gfx::Rect(200, 0, 200, 200));
637 gfx::Rect new_overview_bounds
=
638 ToEnclosingRect(GetTransformedTargetBounds(window
.get()));
639 EXPECT_EQ(overview_bounds
.x(), new_overview_bounds
.x());
640 EXPECT_EQ(overview_bounds
.y(), new_overview_bounds
.y());
641 EXPECT_EQ(overview_bounds
.width(), new_overview_bounds
.width());
642 EXPECT_EQ(overview_bounds
.height(), new_overview_bounds
.height());
646 // Tests that a newly created window aborts overview.
647 TEST_F(WindowSelectorTest
, NewWindowCancelsOveriew
) {
648 gfx::Rect
bounds(0, 0, 400, 400);
649 scoped_ptr
<aura::Window
> window1(CreateWindow(bounds
));
650 scoped_ptr
<aura::Window
> window2(CreateWindow(bounds
));
652 EXPECT_TRUE(IsSelecting());
654 // A window being created should exit overview mode.
655 scoped_ptr
<aura::Window
> window3(CreateWindow(bounds
));
656 EXPECT_FALSE(IsSelecting());
659 // Tests that a window activation exits overview mode.
660 TEST_F(WindowSelectorTest
, ActivationCancelsOveriew
) {
661 gfx::Rect
bounds(0, 0, 400, 400);
662 scoped_ptr
<aura::Window
> window1(CreateWindow(bounds
));
663 scoped_ptr
<aura::Window
> window2(CreateWindow(bounds
));
666 EXPECT_TRUE(IsSelecting());
668 // A window being activated should exit overview mode.
670 EXPECT_FALSE(IsSelecting());
672 // window1 should be focused after exiting even though window2 was focused on
673 // entering overview because we exited due to an activation.
674 EXPECT_EQ(window1
.get(), GetFocusedWindow());
677 // Tests that exiting overview mode without selecting a window restores focus
678 // to the previously focused window.
679 TEST_F(WindowSelectorTest
, CancelRestoresFocus
) {
680 gfx::Rect
bounds(0, 0, 400, 400);
681 scoped_ptr
<aura::Window
> window(CreateWindow(bounds
));
682 wm::ActivateWindow(window
.get());
683 EXPECT_EQ(window
.get(), GetFocusedWindow());
685 // In overview mode, the text filter widget should be focused.
687 EXPECT_EQ(text_filter_widget()->GetNativeWindow(), GetFocusedWindow());
689 // If canceling overview mode, focus should be restored.
691 EXPECT_EQ(window
.get(), GetFocusedWindow());
694 // Tests that overview mode is exited if the last remaining window is destroyed.
695 TEST_F(WindowSelectorTest
, LastWindowDestroyed
) {
696 gfx::Rect
bounds(0, 0, 400, 400);
697 scoped_ptr
<aura::Window
> window1(CreateWindow(bounds
));
698 scoped_ptr
<aura::Window
> window2(CreateWindow(bounds
));
703 EXPECT_FALSE(IsSelecting());
706 // Tests that entering overview mode restores a window to its original
708 TEST_F(WindowSelectorTest
, QuickReentryRestoresInitialTransform
) {
709 gfx::Rect
bounds(0, 0, 400, 400);
710 scoped_ptr
<aura::Window
> window(CreateWindow(bounds
));
711 gfx::Rect initial_bounds
= ToEnclosingRect(
712 GetTransformedBounds(window
.get()));
714 // Quickly exit and reenter overview mode. The window should still be
715 // animating when we reenter. We cannot short circuit animations for this but
716 // we also don't have to wait for them to complete.
718 ui::ScopedAnimationDurationScaleMode
test_duration_mode(
719 ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION
);
723 EXPECT_NE(initial_bounds
, ToEnclosingRect(
724 GetTransformedTargetBounds(window
.get())));
726 EXPECT_FALSE(IsSelecting());
727 EXPECT_EQ(initial_bounds
, ToEnclosingRect(
728 GetTransformedTargetBounds(window
.get())));
731 // Tests that non-activatable windows are hidden when entering overview mode.
732 TEST_F(WindowSelectorTest
, NonActivatableWindowsHidden
) {
733 gfx::Rect
bounds(0, 0, 400, 400);
734 scoped_ptr
<aura::Window
> window1(CreateWindow(bounds
));
735 scoped_ptr
<aura::Window
> window2(CreateWindow(bounds
));
736 scoped_ptr
<aura::Window
> non_activatable_window(
737 CreateNonActivatableWindow(Shell::GetPrimaryRootWindow()->bounds()));
738 EXPECT_TRUE(non_activatable_window
->IsVisible());
740 EXPECT_FALSE(non_activatable_window
->IsVisible());
742 EXPECT_TRUE(non_activatable_window
->IsVisible());
744 // Test that a window behind the fullscreen non-activatable window can be
746 non_activatable_window
->parent()->StackChildAtTop(
747 non_activatable_window
.get());
749 ClickWindow(window1
.get());
750 EXPECT_FALSE(IsSelecting());
751 EXPECT_TRUE(wm::IsActiveWindow(window1
.get()));
754 // Tests that windows with modal child windows are transformed with the modal
755 // child even though not activatable themselves.
756 TEST_F(WindowSelectorTest
, ModalChild
) {
757 gfx::Rect
bounds(0, 0, 400, 400);
758 scoped_ptr
<aura::Window
> window1(CreateWindow(bounds
));
759 scoped_ptr
<aura::Window
> child1(CreateWindow(bounds
));
760 child1
->SetProperty(aura::client::kModalKey
, ui::MODAL_TYPE_WINDOW
);
761 ::wm::AddTransientChild(window1
.get(), child1
.get());
762 EXPECT_EQ(window1
->parent(), child1
->parent());
764 EXPECT_TRUE(window1
->IsVisible());
765 EXPECT_TRUE(child1
->IsVisible());
766 EXPECT_EQ(ToEnclosingRect(GetTransformedTargetBounds(child1
.get())),
767 ToEnclosingRect(GetTransformedTargetBounds(window1
.get())));
771 // Tests that clicking a modal window's parent activates the modal window in
773 TEST_F(WindowSelectorTest
, ClickModalWindowParent
) {
774 scoped_ptr
<aura::Window
> window1(CreateWindow(gfx::Rect(0, 0, 180, 180)));
775 scoped_ptr
<aura::Window
> child1(CreateWindow(gfx::Rect(200, 0, 180, 180)));
776 child1
->SetProperty(aura::client::kModalKey
, ui::MODAL_TYPE_WINDOW
);
777 ::wm::AddTransientChild(window1
.get(), child1
.get());
778 EXPECT_FALSE(WindowsOverlapping(window1
.get(), child1
.get()));
779 EXPECT_EQ(window1
->parent(), child1
->parent());
781 // Given that their relative positions are preserved, the windows should still
783 EXPECT_FALSE(WindowsOverlapping(window1
.get(), child1
.get()));
784 ClickWindow(window1
.get());
785 EXPECT_FALSE(IsSelecting());
787 // Clicking on window1 should activate child1.
788 EXPECT_TRUE(wm::IsActiveWindow(child1
.get()));
791 // Tests that windows remain on the display they are currently on in overview
792 // mode, and that the close buttons are on matching displays.
793 TEST_F(WindowSelectorTest
, MultipleDisplays
) {
794 if (!SupportsMultipleDisplays())
797 UpdateDisplay("600x400,600x400");
798 aura::Window::Windows root_windows
= Shell::GetAllRootWindows();
799 gfx::Rect
bounds1(0, 0, 400, 400);
800 gfx::Rect
bounds2(650, 0, 400, 400);
802 scoped_ptr
<aura::Window
> window1(CreateWindow(bounds1
));
803 scoped_ptr
<aura::Window
> window2(CreateWindow(bounds1
));
804 scoped_ptr
<aura::Window
> window3(CreateWindow(bounds2
));
805 scoped_ptr
<aura::Window
> window4(CreateWindow(bounds2
));
806 scoped_ptr
<aura::Window
> panel1(CreatePanelWindow(bounds1
));
807 scoped_ptr
<aura::Window
> panel2(CreatePanelWindow(bounds1
));
808 scoped_ptr
<aura::Window
> panel3(CreatePanelWindow(bounds2
));
809 scoped_ptr
<aura::Window
> panel4(CreatePanelWindow(bounds2
));
810 EXPECT_EQ(root_windows
[0], window1
->GetRootWindow());
811 EXPECT_EQ(root_windows
[0], window2
->GetRootWindow());
812 EXPECT_EQ(root_windows
[1], window3
->GetRootWindow());
813 EXPECT_EQ(root_windows
[1], window4
->GetRootWindow());
815 EXPECT_EQ(root_windows
[0], panel1
->GetRootWindow());
816 EXPECT_EQ(root_windows
[0], panel2
->GetRootWindow());
817 EXPECT_EQ(root_windows
[1], panel3
->GetRootWindow());
818 EXPECT_EQ(root_windows
[1], panel4
->GetRootWindow());
820 // In overview mode, each window remains in the same root window.
822 EXPECT_EQ(root_windows
[0], window1
->GetRootWindow());
823 EXPECT_EQ(root_windows
[0], window2
->GetRootWindow());
824 EXPECT_EQ(root_windows
[1], window3
->GetRootWindow());
825 EXPECT_EQ(root_windows
[1], window4
->GetRootWindow());
826 EXPECT_EQ(root_windows
[0], panel1
->GetRootWindow());
827 EXPECT_EQ(root_windows
[0], panel2
->GetRootWindow());
828 EXPECT_EQ(root_windows
[1], panel3
->GetRootWindow());
829 EXPECT_EQ(root_windows
[1], panel4
->GetRootWindow());
831 const std::vector
<WindowSelectorItem
*>& primary_window_items
=
832 GetWindowItemsForRoot(0);
833 const std::vector
<WindowSelectorItem
*>& secondary_window_items
=
834 GetWindowItemsForRoot(1);
836 // Window indices are based on top-down order. The reverse of our creation.
837 IsWindowAndCloseButtonInScreen(window1
.get(), primary_window_items
[2]);
838 IsWindowAndCloseButtonInScreen(window2
.get(), primary_window_items
[1]);
839 IsWindowAndCloseButtonInScreen(window3
.get(), secondary_window_items
[2]);
840 IsWindowAndCloseButtonInScreen(window4
.get(), secondary_window_items
[1]);
842 IsWindowAndCloseButtonInScreen(panel1
.get(), primary_window_items
[0]);
843 IsWindowAndCloseButtonInScreen(panel2
.get(), primary_window_items
[0]);
844 IsWindowAndCloseButtonInScreen(panel3
.get(), secondary_window_items
[0]);
845 IsWindowAndCloseButtonInScreen(panel4
.get(), secondary_window_items
[0]);
847 EXPECT_TRUE(WindowsOverlapping(panel1
.get(), panel2
.get()));
848 EXPECT_TRUE(WindowsOverlapping(panel3
.get(), panel4
.get()));
849 EXPECT_FALSE(WindowsOverlapping(panel1
.get(), panel3
.get()));
852 // Tests shutting down during overview.
853 TEST_F(WindowSelectorTest
, Shutdown
) {
854 gfx::Rect
bounds(0, 0, 400, 400);
855 // These windows will be deleted when the test exits and the Shell instance
857 aura::Window
* window1(CreateWindow(bounds
));
858 aura::Window
* window2(CreateWindow(bounds
));
859 aura::Window
* window3(CreatePanelWindow(bounds
));
860 aura::Window
* window4(CreatePanelWindow(bounds
));
862 wm::ActivateWindow(window4
);
863 wm::ActivateWindow(window3
);
864 wm::ActivateWindow(window2
);
865 wm::ActivateWindow(window1
);
870 // Tests removing a display during overview.
871 TEST_F(WindowSelectorTest
, RemoveDisplay
) {
872 if (!SupportsMultipleDisplays())
875 UpdateDisplay("400x400,400x400");
876 gfx::Rect
bounds1(0, 0, 100, 100);
877 gfx::Rect
bounds2(450, 0, 100, 100);
878 scoped_ptr
<aura::Window
> window1(CreateWindow(bounds1
));
879 scoped_ptr
<aura::Window
> window2(CreateWindow(bounds2
));
880 scoped_ptr
<aura::Window
> window3(CreatePanelWindow(bounds1
));
881 scoped_ptr
<aura::Window
> window4(CreatePanelWindow(bounds2
));
883 aura::Window::Windows root_windows
= Shell::GetAllRootWindows();
884 EXPECT_EQ(root_windows
[0], window1
->GetRootWindow());
885 EXPECT_EQ(root_windows
[1], window2
->GetRootWindow());
886 EXPECT_EQ(root_windows
[0], window3
->GetRootWindow());
887 EXPECT_EQ(root_windows
[1], window4
->GetRootWindow());
889 wm::ActivateWindow(window4
.get());
890 wm::ActivateWindow(window3
.get());
891 wm::ActivateWindow(window2
.get());
892 wm::ActivateWindow(window1
.get());
895 EXPECT_TRUE(IsSelecting());
896 UpdateDisplay("400x400");
897 EXPECT_FALSE(IsSelecting());
900 // Tests starting overview during a drag and drop tracking operation.
901 // TODO(flackr): Fix memory corruption crash when running locally (not failing
902 // on bots). See http://crbug.com/342528.
903 TEST_F(WindowSelectorTest
, DISABLED_DragDropInProgress
) {
904 bool drag_canceled_by_test
= false;
905 gfx::Rect
bounds(0, 0, 400, 400);
906 scoped_ptr
<aura::Window
> window(CreateWindow(bounds
));
907 test::ShellTestApi
shell_test_api(Shell::GetInstance());
908 ash::DragDropController
* drag_drop_controller
=
909 shell_test_api
.drag_drop_controller();
910 ui::OSExchangeData data
;
911 base::MessageLoopForUI::current()->PostTask(FROM_HERE
,
912 base::Bind(&WindowSelectorTest::ToggleOverview
,
913 base::Unretained(this)));
914 base::MessageLoopForUI::current()->PostTask(FROM_HERE
,
915 base::Bind(&CancelDrag
, drag_drop_controller
, &drag_canceled_by_test
));
916 data
.SetString(base::UTF8ToUTF16("I am being dragged"));
917 drag_drop_controller
->StartDragAndDrop(data
, window
->GetRootWindow(),
918 window
.get(), gfx::Point(5, 5), ui::DragDropTypes::DRAG_MOVE
,
919 ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE
);
920 RunAllPendingInMessageLoop();
921 EXPECT_FALSE(drag_canceled_by_test
);
922 ASSERT_TRUE(IsSelecting());
923 RunAllPendingInMessageLoop();
926 // Test that a label is created under the window on entering overview mode.
927 TEST_F(WindowSelectorTest
, CreateLabelUnderWindow
) {
928 scoped_ptr
<aura::Window
> window(CreateWindow(gfx::Rect(0, 0, 100, 100)));
929 base::string16 window_title
= base::UTF8ToUTF16("My window");
930 window
->SetTitle(window_title
);
932 WindowSelectorItem
* window_item
= GetWindowItemsForRoot(0).back();
933 views::Label
* label
= GetLabelView(window_item
);
934 // Has the label view been created?
937 // Verify the label matches the window title.
938 EXPECT_EQ(label
->text(), window_title
);
940 // Update the window title and check that the label is updated, too.
941 base::string16 updated_title
= base::UTF8ToUTF16("Updated title");
942 window
->SetTitle(updated_title
);
943 EXPECT_EQ(label
->text(), updated_title
);
945 // Labels are located based on target_bounds, not the actual window item
947 gfx::Rect
target_bounds(window_item
->target_bounds());
948 gfx::Rect
expected_label_bounds(target_bounds
.x(),
949 target_bounds
.bottom() - label
->
950 GetPreferredSize().height(),
951 target_bounds
.width(),
952 label
->GetPreferredSize().height());
953 gfx::Rect real_label_bounds
= label
->GetWidget()->GetNativeWindow()->bounds();
954 EXPECT_EQ(real_label_bounds
, expected_label_bounds
);
957 // Tests that a label is created for the active panel in a group of panels in
959 TEST_F(WindowSelectorTest
, CreateLabelUnderPanel
) {
960 scoped_ptr
<aura::Window
> panel1(CreatePanelWindow(gfx::Rect(0, 0, 100, 100)));
961 scoped_ptr
<aura::Window
> panel2(CreatePanelWindow(gfx::Rect(0, 0, 100, 100)));
962 base::string16 panel1_title
= base::UTF8ToUTF16("My panel");
963 base::string16 panel2_title
= base::UTF8ToUTF16("Another panel");
964 base::string16 updated_panel1_title
= base::UTF8ToUTF16("WebDriver Torso");
965 base::string16 updated_panel2_title
= base::UTF8ToUTF16("Da panel");
966 panel1
->SetTitle(panel1_title
);
967 panel2
->SetTitle(panel2_title
);
968 wm::ActivateWindow(panel1
.get());
970 WindowSelectorItem
* window_item
= GetWindowItemsForRoot(0).back();
971 views::Label
* label
= GetLabelView(window_item
);
972 // Has the label view been created?
975 // Verify the label matches the active window title.
976 EXPECT_EQ(label
->text(), panel1_title
);
977 // Verify that updating the title also updates the label.
978 panel1
->SetTitle(updated_panel1_title
);
979 EXPECT_EQ(label
->text(), updated_panel1_title
);
980 // After destroying the first panel, the label should match the second panel.
982 label
= GetLabelView(window_item
);
983 EXPECT_EQ(label
->text(), panel2_title
);
984 // Also test updating the title on the second panel.
985 panel2
->SetTitle(updated_panel2_title
);
986 EXPECT_EQ(label
->text(), updated_panel2_title
);
989 // Tests that overview updates the window positions if the display orientation
991 TEST_F(WindowSelectorTest
, DisplayOrientationChanged
) {
992 if (!SupportsHostWindowResize())
995 aura::Window
* root_window
= Shell::GetInstance()->GetPrimaryRootWindow();
996 UpdateDisplay("600x200");
997 EXPECT_EQ("0,0 600x200", root_window
->bounds().ToString());
998 gfx::Rect
window_bounds(0, 0, 150, 150);
999 ScopedVector
<aura::Window
> windows
;
1000 for (int i
= 0; i
< 3; i
++) {
1001 windows
.push_back(CreateWindow(window_bounds
));
1005 for (ScopedVector
<aura::Window
>::iterator iter
= windows
.begin();
1006 iter
!= windows
.end(); ++iter
) {
1007 EXPECT_TRUE(root_window
->bounds().Contains(
1008 ToEnclosingRect(GetTransformedTargetBounds(*iter
))));
1011 // Rotate the display, windows should be repositioned to be within the screen
1013 UpdateDisplay("600x200/r");
1014 EXPECT_EQ("0,0 200x600", root_window
->bounds().ToString());
1015 for (ScopedVector
<aura::Window
>::iterator iter
= windows
.begin();
1016 iter
!= windows
.end(); ++iter
) {
1017 EXPECT_TRUE(root_window
->bounds().Contains(
1018 ToEnclosingRect(GetTransformedTargetBounds(*iter
))));
1022 // Tests traversing some windows in overview mode with the tab key.
1023 TEST_F(WindowSelectorTest
, BasicTabKeyNavigation
) {
1024 gfx::Rect
bounds(0, 0, 100, 100);
1025 scoped_ptr
<aura::Window
> window2(CreateWindow(bounds
));
1026 scoped_ptr
<aura::Window
> window1(CreateWindow(bounds
));
1029 SendKey(ui::VKEY_TAB
);
1030 EXPECT_EQ(GetSelectedWindow(), window1
.get());
1031 SendKey(ui::VKEY_TAB
);
1032 EXPECT_EQ(GetSelectedWindow(), window2
.get());
1033 SendKey(ui::VKEY_TAB
);
1034 EXPECT_EQ(GetSelectedWindow(), window1
.get());
1037 // Tests traversing some windows in overview mode with the arrow keys in every
1038 // possible direction.
1039 TEST_F(WindowSelectorTest
, BasicArrowKeyNavigation
) {
1040 if (!SupportsHostWindowResize())
1042 const size_t test_windows
= 9;
1043 UpdateDisplay("800x600");
1044 ScopedVector
<aura::Window
> windows
;
1045 for (size_t i
= test_windows
; i
> 0; i
--)
1046 windows
.push_back(CreateWindowWithId(gfx::Rect(0, 0, 100, 100), i
));
1048 ui::KeyboardCode arrow_keys
[] = {
1054 // Expected window layout, assuming that the text filtering feature is
1055 // enabled by default (i.e., --ash-disable-text-filtering-in-overview-mode
1056 // is not being used).
1057 // +-------+ +-------+ +-------+ +-------+
1058 // | 1 | | 2 | | 3 | | 4 |
1059 // +-------+ +-------+ +-------+ +-------+
1060 // +-------+ +-------+ +-------+ +-------+
1061 // | 5 | | 6 | | 7 | | 8 |
1062 // +-------+ +-------+ +-------+ +-------+
1066 // Index for each window during a full loop plus wrapping around.
1067 int index_path_for_direction
[][test_windows
+ 1] = {
1068 {1, 2, 3, 4, 5, 6, 7, 8, 9, 1}, // Right
1069 {1, 5, 9, 2, 6, 3, 7, 4, 8, 1}, // Down
1070 {9, 8, 7, 6, 5, 4, 3, 2, 1, 9}, // Left
1071 {8, 4, 7, 3, 6, 2, 9, 5, 1, 8} // Up
1074 for (size_t key_index
= 0; key_index
< arraysize(arrow_keys
); key_index
++) {
1076 for (size_t i
= 0; i
< test_windows
+ 1; i
++) {
1077 SendKey(arrow_keys
[key_index
]);
1078 // TODO(flackr): Add a more readable error message by constructing a
1079 // string from the window IDs.
1080 EXPECT_EQ(GetSelectedWindow()->id(),
1081 index_path_for_direction
[key_index
][i
]);
1087 // Tests basic selection across multiple monitors.
1088 TEST_F(WindowSelectorTest
, BasicMultiMonitorArrowKeyNavigation
) {
1089 if (!SupportsMultipleDisplays())
1092 UpdateDisplay("400x400,400x400");
1093 gfx::Rect
bounds1(0, 0, 100, 100);
1094 gfx::Rect
bounds2(450, 0, 100, 100);
1095 scoped_ptr
<aura::Window
> window4(CreateWindow(bounds2
));
1096 scoped_ptr
<aura::Window
> window3(CreateWindow(bounds2
));
1097 scoped_ptr
<aura::Window
> window2(CreateWindow(bounds1
));
1098 scoped_ptr
<aura::Window
> window1(CreateWindow(bounds1
));
1103 SendKey(ui::VKEY_RIGHT
);
1104 EXPECT_EQ(GetSelectedWindow(), window1
.get());
1105 SendKey(ui::VKEY_RIGHT
);
1106 EXPECT_EQ(GetSelectedWindow(), window2
.get());
1107 SendKey(ui::VKEY_RIGHT
);
1108 EXPECT_EQ(GetSelectedWindow(), window3
.get());
1109 SendKey(ui::VKEY_RIGHT
);
1110 EXPECT_EQ(GetSelectedWindow(), window4
.get());
1113 // Tests selecting a window in overview mode with the return key.
1114 TEST_F(WindowSelectorTest
, SelectWindowWithReturnKey
) {
1115 gfx::Rect
bounds(0, 0, 100, 100);
1116 scoped_ptr
<aura::Window
> window2(CreateWindow(bounds
));
1117 scoped_ptr
<aura::Window
> window1(CreateWindow(bounds
));
1120 // Pressing the return key without a selection widget should not do anything.
1121 SendKey(ui::VKEY_RETURN
);
1122 EXPECT_TRUE(IsSelecting());
1124 // Select the first window.
1125 SendKey(ui::VKEY_RIGHT
);
1126 SendKey(ui::VKEY_RETURN
);
1127 ASSERT_FALSE(IsSelecting());
1128 EXPECT_TRUE(wm::IsActiveWindow(window1
.get()));
1130 // Select the second window.
1132 SendKey(ui::VKEY_RIGHT
);
1133 SendKey(ui::VKEY_RIGHT
);
1134 SendKey(ui::VKEY_RETURN
);
1135 EXPECT_FALSE(IsSelecting());
1136 EXPECT_TRUE(wm::IsActiveWindow(window2
.get()));
1139 // Tests that overview mode hides the callout widget.
1140 TEST_F(WindowSelectorTest
, WindowOverviewHidesCalloutWidgets
) {
1141 scoped_ptr
<aura::Window
> panel1(CreatePanelWindow(gfx::Rect(0, 0, 100, 100)));
1142 scoped_ptr
<aura::Window
> panel2(CreatePanelWindow(gfx::Rect(0, 0, 100, 100)));
1143 PanelLayoutManager
* panel_manager
=
1144 static_cast<PanelLayoutManager
*>(panel1
->parent()->layout_manager());
1146 // By default, panel callout widgets are visible.
1148 panel_manager
->GetCalloutWidgetForPanel(panel1
.get())->IsVisible());
1150 panel_manager
->GetCalloutWidgetForPanel(panel2
.get())->IsVisible());
1152 // Toggling the overview should hide the callout widgets.
1155 panel_manager
->GetCalloutWidgetForPanel(panel1
.get())->IsVisible());
1157 panel_manager
->GetCalloutWidgetForPanel(panel2
.get())->IsVisible());
1159 // Ending the overview should show them again.
1162 panel_manager
->GetCalloutWidgetForPanel(panel1
.get())->IsVisible());
1164 panel_manager
->GetCalloutWidgetForPanel(panel2
.get())->IsVisible());
1167 // Tests that when panels are grouped that the close button only closes the
1168 // currently active panel. After the removal window selection should still be
1169 // active, and the label should have changed. Removing the last panel should
1170 // cause selection to end.
1171 TEST_F(WindowSelectorTest
, CloseButtonOnPanels
) {
1172 scoped_ptr
<views::Widget
> widget1(CreatePanelWindowWidget(
1173 gfx::Rect(0, 0, 300, 100)));
1174 scoped_ptr
<views::Widget
> widget2(CreatePanelWindowWidget(
1175 gfx::Rect(100, 0, 100, 100)));
1176 aura::Window
* window1
= widget1
->GetNativeWindow();
1177 aura::Window
* window2
= widget2
->GetNativeWindow();
1178 base::string16 panel1_title
= base::UTF8ToUTF16("Panel 1");
1179 base::string16 panel2_title
= base::UTF8ToUTF16("Panel 2");
1180 window1
->SetTitle(panel1_title
);
1181 window2
->SetTitle(panel2_title
);
1182 wm::ActivateWindow(window1
);
1185 gfx::RectF bounds1
= GetTransformedBoundsInRootWindow(window1
);
1186 gfx::Point
point1(bounds1
.top_right().x() - 1, bounds1
.top_right().y() - 1);
1187 ui::test::EventGenerator
event_generator1(window1
->GetRootWindow(), point1
);
1189 EXPECT_FALSE(widget1
->IsClosed());
1190 event_generator1
.ClickLeftButton();
1191 EXPECT_TRUE(widget1
->IsClosed());
1192 RunAllPendingInMessageLoop();
1193 EXPECT_TRUE(IsSelecting());
1194 WindowSelectorItem
* window_item
= GetWindowItemsForRoot(0).front();
1195 EXPECT_FALSE(window_item
->empty());
1196 EXPECT_TRUE(window_item
->Contains(window2
));
1197 EXPECT_TRUE(GetCloseButton(window_item
)->IsVisible());
1200 views::Label
* label
= GetLabelView(window_item
);
1201 EXPECT_EQ(label
->text(), panel2_title
);
1203 gfx::RectF bounds2
= GetTransformedBoundsInRootWindow(window2
);
1204 gfx::Point
point2(bounds2
.top_right().x() - 1, bounds2
.top_right().y() - 1);
1205 ui::test::EventGenerator
event_generator2(window2
->GetRootWindow(), point2
);
1207 EXPECT_FALSE(widget2
->IsClosed());
1208 event_generator2
.ClickLeftButton();
1209 EXPECT_TRUE(widget2
->IsClosed());
1210 RunAllPendingInMessageLoop();
1211 EXPECT_FALSE(IsSelecting());
1214 // Creates three windows and tests filtering them by title.
1215 TEST_F(WindowSelectorTest
, BasicTextFiltering
) {
1216 gfx::Rect
bounds(0, 0, 100, 100);
1217 scoped_ptr
<aura::Window
> window2(CreateWindow(bounds
));
1218 scoped_ptr
<aura::Window
> window1(CreateWindow(bounds
));
1219 scoped_ptr
<aura::Window
> window0(CreateWindow(bounds
));
1220 base::string16 window2_title
= base::UTF8ToUTF16("Highway to test");
1221 base::string16 window1_title
= base::UTF8ToUTF16("For those about to test");
1222 base::string16 window0_title
= base::UTF8ToUTF16("We salute you");
1223 window0
->SetTitle(window0_title
);
1224 window1
->SetTitle(window1_title
);
1225 window2
->SetTitle(window2_title
);
1227 EXPECT_FALSE(selection_widget_active());
1228 EXPECT_FALSE(showing_filter_widget());
1229 FilterItems("Test");
1231 // The selection widget should appear when filtering starts, and should be
1232 // selecting the first matching window.
1233 EXPECT_TRUE(selection_widget_active());
1234 EXPECT_TRUE(showing_filter_widget());
1235 EXPECT_EQ(GetSelectedWindow(), window1
.get());
1237 // Window 0 has no "test" on it so it should be the only dimmed item.
1238 std::vector
<WindowSelectorItem
*> items
= GetWindowItemsForRoot(0);
1239 EXPECT_TRUE(items
[0]->dimmed());
1240 EXPECT_FALSE(items
[1]->dimmed());
1241 EXPECT_FALSE(items
[2]->dimmed());
1243 // No items match the search.
1244 FilterItems("I'm testing 'n testing");
1245 EXPECT_TRUE(items
[0]->dimmed());
1246 EXPECT_TRUE(items
[1]->dimmed());
1247 EXPECT_TRUE(items
[2]->dimmed());
1249 // All the items should match the empty string. The filter widget should also
1252 EXPECT_FALSE(showing_filter_widget());
1253 EXPECT_FALSE(items
[0]->dimmed());
1254 EXPECT_FALSE(items
[1]->dimmed());
1255 EXPECT_FALSE(items
[2]->dimmed());
1258 // Tests selecting in the overview with dimmed and undimmed items.
1259 TEST_F(WindowSelectorTest
, TextFilteringSelection
) {
1260 gfx::Rect
bounds(0, 0, 100, 100);
1261 scoped_ptr
<aura::Window
> window2(CreateWindow(bounds
));
1262 scoped_ptr
<aura::Window
> window1(CreateWindow(bounds
));
1263 scoped_ptr
<aura::Window
> window0(CreateWindow(bounds
));
1264 base::string16 window2_title
= base::UTF8ToUTF16("Rock and roll");
1265 base::string16 window1_title
= base::UTF8ToUTF16("Rock and");
1266 base::string16 window0_title
= base::UTF8ToUTF16("Rock");
1267 window0
->SetTitle(window0_title
);
1268 window1
->SetTitle(window1_title
);
1269 window2
->SetTitle(window2_title
);
1271 SendKey(ui::VKEY_RIGHT
);
1272 EXPECT_TRUE(selection_widget_active());
1273 EXPECT_EQ(GetSelectedWindow(), window0
.get());
1275 // Dim the first item, the selection should jump to the next item.
1276 std::vector
<WindowSelectorItem
*> items
= GetWindowItemsForRoot(0);
1277 FilterItems("Rock and");
1278 EXPECT_EQ(GetSelectedWindow(), window1
.get());
1280 // Cycle the selection, the dimmed window should not be selected.
1281 SendKey(ui::VKEY_RIGHT
);
1282 EXPECT_EQ(GetSelectedWindow(), window2
.get());
1283 SendKey(ui::VKEY_RIGHT
);
1284 EXPECT_EQ(GetSelectedWindow(), window1
.get());
1286 // Dimming all the items should hide the selection widget.
1288 EXPECT_FALSE(selection_widget_active());
1290 // Undimming one window should automatically select it.
1291 FilterItems("Rock and roll");
1292 EXPECT_EQ(GetSelectedWindow(), window2
.get());
1295 // Tests clicking on the desktop itself to cancel overview mode.
1296 TEST_F(WindowSelectorTest
, CancelOverviewOnMouseClick
) {
1297 // Overview disabled by default.
1298 EXPECT_FALSE(IsSelecting());
1300 // Point and bounds selected so that they don't intersect. This causes
1301 // events located at the point to be passed to DesktopBackgroundController,
1302 // and not the window.
1303 gfx::Point
point_in_background_page(0, 0);
1304 gfx::Rect
bounds(10, 10, 100, 100);
1305 scoped_ptr
<aura::Window
> window1(CreateWindow(bounds
));
1306 ui::test::EventGenerator
& generator
= GetEventGenerator();
1307 // Move mouse to point in the background page. Sending an event here will pass
1308 // it to the DesktopBackgroundController in both regular and overview mode.
1309 generator
.MoveMouseTo(point_in_background_page
);
1311 // Clicking on the background page while not in overview should not toggle
1313 generator
.ClickLeftButton();
1314 EXPECT_FALSE(IsSelecting());
1316 // Switch to overview mode.
1318 ASSERT_TRUE(IsSelecting());
1320 // Click should now exit overview mode.
1321 generator
.ClickLeftButton();
1322 EXPECT_FALSE(IsSelecting());
1325 // Tests tapping on the desktop itself to cancel overview mode.
1326 TEST_F(WindowSelectorTest
, CancelOverviewOnTap
) {
1327 // Overview disabled by default.
1328 EXPECT_FALSE(IsSelecting());
1330 // Point and bounds selected so that they don't intersect. This causes
1331 // events located at the point to be passed to DesktopBackgroundController,
1332 // and not the window.
1333 gfx::Point
point_in_background_page(0, 0);
1334 gfx::Rect
bounds(10, 10, 100, 100);
1335 scoped_ptr
<aura::Window
> window1(CreateWindow(bounds
));
1336 ui::test::EventGenerator
& generator
= GetEventGenerator();
1338 // Tapping on the background page while not in overview should not toggle
1340 generator
.GestureTapAt(point_in_background_page
);
1341 EXPECT_FALSE(IsSelecting());
1343 // Switch to overview mode.
1345 ASSERT_TRUE(IsSelecting());
1347 // Tap should now exit overview mode.
1348 generator
.GestureTapAt(point_in_background_page
);
1349 EXPECT_FALSE(IsSelecting());
1352 // Tests that the transparent overlays z-order matches the windows z-order.
1353 TEST_F(WindowSelectorTest
, PanelStackOrdering
) {
1354 gfx::Rect
bounds1(0, 0, 100, 100);
1355 gfx::Rect
bounds2(10, 0, 100, 100);
1356 gfx::Rect
bounds3(20, 0, 100, 100);
1357 scoped_ptr
<aura::Window
> panel1(CreatePanelWindow(bounds1
));
1358 scoped_ptr
<aura::Window
> panel2(CreatePanelWindow(bounds2
));
1359 scoped_ptr
<aura::Window
> panel3(CreatePanelWindow(bounds3
));
1361 wm::ActivateWindow(panel2
.get());
1362 wm::ActivateWindow(panel3
.get());
1363 wm::ActivateWindow(panel1
.get());
1365 WindowIndexMap z_order
;
1366 GetWindowIndexMap(panel1
->parent()->children(), &z_order
);
1367 ASSERT_GT(z_order
[panel1
.get()], z_order
[panel2
.get()]);
1368 ASSERT_GT(z_order
[panel2
.get()], z_order
[panel3
.get()]);
1370 WindowList mru_windows
= ash::Shell::GetInstance()->mru_window_tracker()->
1371 BuildMruWindowList();
1372 WindowIndexMap mru_rank
;
1373 GetWindowIndexMap(mru_windows
, &mru_rank
);
1374 ASSERT_LT(mru_rank
[panel1
.get()], mru_rank
[panel3
.get()]);
1375 ASSERT_LT(mru_rank
[panel3
.get()], mru_rank
[panel2
.get()]);
1379 gfx::Rect
panel1_transformed_bounds(
1380 gfx::ToEnclosingRect(GetTransformedTargetBounds(panel1
.get())));
1381 gfx::Rect
panel2_transformed_bounds(
1382 gfx::ToEnclosingRect(GetTransformedTargetBounds(panel2
.get())));
1383 gfx::Rect
panel3_transformed_bounds(
1384 gfx::ToEnclosingRect(GetTransformedTargetBounds(panel3
.get())));
1386 gfx::Rect
target_rect(panel2_transformed_bounds
);
1387 target_rect
.Intersect(panel3_transformed_bounds
);
1388 target_rect
.Subtract(panel1_transformed_bounds
);
1389 gfx::Point
tap_target(target_rect
.CenterPoint());
1391 ASSERT_TRUE(panel3_transformed_bounds
.Contains(tap_target
));
1393 ui::test::EventGenerator
generator(Shell::GetPrimaryRootWindow());
1394 generator
.GestureTapAt(tap_target
);
1395 EXPECT_EQ(panel2
.get(), GetFocusedWindow());
1398 // Tests that individual panels can be selected on a secondary display.
1399 TEST_F(WindowSelectorTest
, PanelSelectionOnSecondaryDisplay
) {
1400 if (!SupportsMultipleDisplays())
1403 UpdateDisplay("600x400,600x400");
1404 aura::Window::Windows root_windows
= Shell::GetAllRootWindows();
1406 gfx::Rect
bounds1(700, 0, 100, 100);
1407 gfx::Rect
bounds2(710, 0, 100, 100);
1408 scoped_ptr
<aura::Window
> panel1(CreatePanelWindow(bounds1
));
1409 scoped_ptr
<aura::Window
> panel2(CreatePanelWindow(bounds2
));
1411 wm::ActivateWindow(panel2
.get());
1412 wm::ActivateWindow(panel1
.get());
1414 WindowList mru_windows
= ash::Shell::GetInstance()->mru_window_tracker()->
1415 BuildMruWindowList();
1416 WindowIndexMap mru_rank
;
1417 GetWindowIndexMap(mru_windows
, &mru_rank
);
1418 ASSERT_LT(mru_rank
[panel1
.get()], mru_rank
[panel2
.get()]);
1419 ASSERT_EQ(panel1
.get(), GetFocusedWindow());
1423 gfx::Rect
panel2_transformed_bounds(
1424 gfx::ToEnclosingRect(GetTransformedBoundsInRootWindow(panel2
.get())));
1425 gfx::Point
tap_target(panel2_transformed_bounds
.right() - 2,
1426 panel2_transformed_bounds
.bottom() - 2);
1428 ui::test::EventGenerator
generator(root_windows
[1]);
1429 generator
.GestureTapAt(tap_target
);
1430 EXPECT_EQ(panel2
.get(), GetFocusedWindow());