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.
5 #include "ash/focus_cycler.h"
7 #include "ash/root_window_controller.h"
8 #include "ash/shelf/shelf.h"
9 #include "ash/shelf/shelf_widget.h"
10 #include "ash/shell.h"
11 #include "ash/shell_factory.h"
12 #include "ash/shell_window_ids.h"
13 #include "ash/system/status_area_widget.h"
14 #include "ash/system/status_area_widget_delegate.h"
15 #include "ash/system/tray/system_tray.h"
16 #include "ash/test/ash_test_base.h"
17 #include "ash/wm/window_util.h"
18 #include "ui/aura/test/event_generator.h"
19 #include "ui/aura/test/test_windows.h"
20 #include "ui/aura/window.h"
21 #include "ui/aura/window_event_dispatcher.h"
22 #include "ui/views/accessible_pane_view.h"
23 #include "ui/views/controls/button/menu_button.h"
24 #include "ui/views/widget/widget.h"
30 using internal::FocusCycler
;
34 internal::StatusAreaWidgetDelegate
* GetStatusAreaWidgetDelegate(
35 views::Widget
* widget
) {
36 return static_cast<internal::StatusAreaWidgetDelegate
*>(
37 widget
->GetContentsView());
40 class PanedWidgetDelegate
: public views::WidgetDelegate
{
42 PanedWidgetDelegate(views::Widget
* widget
) : widget_(widget
) {}
44 void SetAccessiblePanes(const std::vector
<views::View
*>& panes
) {
45 accessible_panes_
= panes
;
48 // views::WidgetDelegate.
49 virtual void GetAccessiblePanes(std::vector
<views::View
*>* panes
) OVERRIDE
{
50 std::copy(accessible_panes_
.begin(),
51 accessible_panes_
.end(),
52 std::back_inserter(*panes
));
54 virtual views::Widget
* GetWidget() OVERRIDE
{
57 virtual const views::Widget
* GetWidget() const OVERRIDE
{
62 views::Widget
* widget_
;
63 std::vector
<views::View
*> accessible_panes_
;
68 class FocusCyclerTest
: public AshTestBase
{
72 virtual void SetUp() OVERRIDE
{
75 focus_cycler_
.reset(new FocusCycler());
77 ASSERT_TRUE(Shelf::ForPrimaryDisplay());
80 virtual void TearDown() OVERRIDE
{
82 GetStatusAreaWidgetDelegate(tray_
->GetWidget())->
83 SetFocusCyclerForTesting(NULL
);
87 shelf_widget()->SetFocusCycler(NULL
);
89 focus_cycler_
.reset();
91 AshTestBase::TearDown();
95 // Creates the system tray, returning true on success.
99 aura::Window
* parent
= Shell::GetPrimaryRootWindowController()->
100 GetContainer(ash::internal::kShellWindowId_StatusContainer
);
102 internal::StatusAreaWidget
* widget
= new internal::StatusAreaWidget(parent
);
103 widget
->CreateTrayViews();
105 tray_
.reset(widget
->system_tray());
106 if (!tray_
->GetWidget())
108 focus_cycler_
->AddWidget(tray()->GetWidget());
109 GetStatusAreaWidgetDelegate(tray_
->GetWidget())->SetFocusCyclerForTesting(
114 FocusCycler
* focus_cycler() { return focus_cycler_
.get(); }
116 SystemTray
* tray() { return tray_
.get(); }
118 ShelfWidget
* shelf_widget() {
119 return Shelf::ForPrimaryDisplay()->shelf_widget();
122 void InstallFocusCycleOnShelf() {
124 shelf_widget()->SetFocusCycler(focus_cycler());
128 scoped_ptr
<FocusCycler
> focus_cycler_
;
129 scoped_ptr
<SystemTray
> tray_
;
131 DISALLOW_COPY_AND_ASSIGN(FocusCyclerTest
);
134 TEST_F(FocusCyclerTest
, CycleFocusBrowserOnly
) {
135 // Create a single test window.
136 scoped_ptr
<Window
> window0(CreateTestWindowInShellWithId(0));
137 wm::ActivateWindow(window0
.get());
138 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
141 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
142 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
145 TEST_F(FocusCyclerTest
, CycleFocusForward
) {
146 ASSERT_TRUE(CreateTray());
148 InstallFocusCycleOnShelf();
150 // Create a single test window.
151 scoped_ptr
<Window
> window0(CreateTestWindowInShellWithId(0));
152 wm::ActivateWindow(window0
.get());
153 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
155 // Cycle focus to the status area.
156 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
157 EXPECT_TRUE(tray()->GetWidget()->IsActive());
159 // Cycle focus to the shelf.
160 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
161 EXPECT_TRUE(shelf_widget()->IsActive());
163 // Cycle focus to the browser.
164 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
165 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
168 TEST_F(FocusCyclerTest
, CycleFocusBackward
) {
169 ASSERT_TRUE(CreateTray());
171 InstallFocusCycleOnShelf();
173 // Create a single test window.
174 scoped_ptr
<Window
> window0(CreateTestWindowInShellWithId(0));
175 wm::ActivateWindow(window0
.get());
176 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
178 // Cycle focus to the shelf.
179 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
180 EXPECT_TRUE(shelf_widget()->IsActive());
182 // Cycle focus to the status area.
183 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
184 EXPECT_TRUE(tray()->GetWidget()->IsActive());
186 // Cycle focus to the browser.
187 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
188 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
191 TEST_F(FocusCyclerTest
, CycleFocusForwardBackward
) {
192 ASSERT_TRUE(CreateTray());
194 InstallFocusCycleOnShelf();
196 // Create a single test window.
197 scoped_ptr
<Window
> window0(CreateTestWindowInShellWithId(0));
198 wm::ActivateWindow(window0
.get());
199 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
201 // Cycle focus to the shelf.
202 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
203 EXPECT_TRUE(shelf_widget()->IsActive());
205 // Cycle focus to the status area.
206 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
207 EXPECT_TRUE(tray()->GetWidget()->IsActive());
209 // Cycle focus to the browser.
210 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
211 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
213 // Cycle focus to the status area.
214 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
215 EXPECT_TRUE(tray()->GetWidget()->IsActive());
217 // Cycle focus to the shelf.
218 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
219 EXPECT_TRUE(shelf_widget()->IsActive());
221 // Cycle focus to the browser.
222 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
223 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
226 TEST_F(FocusCyclerTest
, CycleFocusNoBrowser
) {
227 ASSERT_TRUE(CreateTray());
229 InstallFocusCycleOnShelf();
231 // Add the shelf and focus it.
232 focus_cycler()->FocusWidget(shelf_widget());
234 // Cycle focus to the status area.
235 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
236 EXPECT_TRUE(tray()->GetWidget()->IsActive());
238 // Cycle focus to the shelf.
239 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
240 EXPECT_TRUE(shelf_widget()->IsActive());
242 // Cycle focus to the status area.
243 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
244 EXPECT_TRUE(tray()->GetWidget()->IsActive());
246 // Cycle focus to the shelf.
247 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
248 EXPECT_TRUE(shelf_widget()->IsActive());
250 // Cycle focus to the status area.
251 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
252 EXPECT_TRUE(tray()->GetWidget()->IsActive());
255 // Tests that focus cycles from the active browser to the status area and back.
256 TEST_F(FocusCyclerTest
, Shelf_CycleFocusForward
) {
257 ASSERT_TRUE(CreateTray());
258 InstallFocusCycleOnShelf();
259 shelf_widget()->Hide();
261 // Create two test windows.
262 scoped_ptr
<Window
> window0(CreateTestWindowInShellWithId(0));
263 scoped_ptr
<Window
> window1(CreateTestWindowInShellWithId(1));
264 wm::ActivateWindow(window1
.get());
265 wm::ActivateWindow(window0
.get());
266 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
268 // Cycle focus to the status area.
269 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
270 EXPECT_TRUE(tray()->GetWidget()->IsActive());
272 // Cycle focus to the browser.
273 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
274 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
276 // Cycle focus to the status area.
277 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
278 EXPECT_TRUE(tray()->GetWidget()->IsActive());
281 TEST_F(FocusCyclerTest
, Shelf_CycleFocusBackwardInvisible
) {
282 ASSERT_TRUE(CreateTray());
283 InstallFocusCycleOnShelf();
284 shelf_widget()->Hide();
286 // Create a single test window.
287 scoped_ptr
<Window
> window0(CreateTestWindowInShellWithId(0));
288 wm::ActivateWindow(window0
.get());
289 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
291 // Cycle focus to the status area.
292 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
293 EXPECT_TRUE(tray()->GetWidget()->IsActive());
295 // Cycle focus to the browser.
296 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
297 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
300 TEST_F(FocusCyclerTest
, CycleFocusThroughWindowWithPanes
) {
301 ASSERT_TRUE(CreateTray());
303 InstallFocusCycleOnShelf();
305 scoped_ptr
<PanedWidgetDelegate
> test_widget_delegate
;
306 scoped_ptr
<views::Widget
> browser_widget(new views::Widget
);
307 test_widget_delegate
.reset(new PanedWidgetDelegate(browser_widget
.get()));
308 views::Widget::InitParams
widget_params(
309 views::Widget::InitParams::TYPE_WINDOW
);
310 widget_params
.context
= CurrentContext();
311 widget_params
.delegate
= test_widget_delegate
.get();
312 widget_params
.ownership
=
313 views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
314 browser_widget
->Init(widget_params
);
315 browser_widget
->Show();
317 aura::Window
* browser_window
= browser_widget
->GetNativeView();
319 views::View
* root_view
= browser_widget
->GetRootView();
321 views::AccessiblePaneView
* pane1
= new views::AccessiblePaneView();
322 root_view
->AddChildView(pane1
);
324 views::View
* view1
= new views::View
;
325 view1
->SetFocusable(true);
326 pane1
->AddChildView(view1
);
328 views::View
* view2
= new views::View
;
329 view2
->SetFocusable(true);
330 pane1
->AddChildView(view2
);
332 views::AccessiblePaneView
* pane2
= new views::AccessiblePaneView();
333 root_view
->AddChildView(pane2
);
335 views::View
* view3
= new views::View
;
336 view3
->SetFocusable(true);
337 pane2
->AddChildView(view3
);
339 views::View
* view4
= new views::View
;
340 view4
->SetFocusable(true);
341 pane2
->AddChildView(view4
);
343 std::vector
<views::View
*> panes
;
344 panes
.push_back(pane1
);
345 panes
.push_back(pane2
);
347 test_widget_delegate
->SetAccessiblePanes(panes
);
349 views::FocusManager
* focus_manager
= browser_widget
->GetFocusManager();
351 // Cycle focus to the status area.
352 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
353 EXPECT_TRUE(tray()->GetWidget()->IsActive());
355 // Cycle focus to the shelf.
356 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
357 EXPECT_TRUE(shelf_widget()->IsActive());
359 // Cycle focus to the first pane in the browser.
360 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
361 EXPECT_TRUE(wm::IsActiveWindow(browser_window
));
362 EXPECT_EQ(focus_manager
->GetFocusedView(), view1
);
364 // Cycle focus to the second pane in the browser.
365 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
366 EXPECT_TRUE(wm::IsActiveWindow(browser_window
));
367 EXPECT_EQ(focus_manager
->GetFocusedView(), view3
);
369 // Cycle focus back to the status area.
370 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
371 EXPECT_TRUE(tray()->GetWidget()->IsActive());
373 // Reverse direction - back to the second pane in the browser.
374 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
375 EXPECT_TRUE(wm::IsActiveWindow(browser_window
));
376 EXPECT_EQ(focus_manager
->GetFocusedView(), view3
);
378 // Back to the first pane in the browser.
379 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
380 EXPECT_TRUE(wm::IsActiveWindow(browser_window
));
381 EXPECT_EQ(focus_manager
->GetFocusedView(), view1
);
383 // Back to the shelf.
384 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
385 EXPECT_TRUE(shelf_widget()->IsActive());
387 // Back to the status area.
388 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
389 EXPECT_TRUE(tray()->GetWidget()->IsActive());
391 // Pressing "Escape" while on the status area should
392 // deactivate it, and activate the browser window.
393 aura::Window
* root
= Shell::GetPrimaryRootWindow();
394 aura::test::EventGenerator
event_generator(root
, root
);
395 event_generator
.PressKey(ui::VKEY_ESCAPE
, 0);
396 EXPECT_TRUE(wm::IsActiveWindow(browser_window
));
397 EXPECT_EQ(focus_manager
->GetFocusedView(), view1
);
399 // Similarly, pressing "Escape" while on the shelf.
400 // should do the same thing.
401 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
402 EXPECT_TRUE(shelf_widget()->IsActive());
403 event_generator
.PressKey(ui::VKEY_ESCAPE
, 0);
404 EXPECT_TRUE(wm::IsActiveWindow(browser_window
));
405 EXPECT_EQ(focus_manager
->GetFocusedView(), view1
);