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/test_windows.h"
19 #include "ui/aura/window.h"
20 #include "ui/aura/window_event_dispatcher.h"
21 #include "ui/events/test/event_generator.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"
33 StatusAreaWidgetDelegate
* GetStatusAreaWidgetDelegate(views::Widget
* widget
) {
34 return static_cast<StatusAreaWidgetDelegate
*>(widget
->GetContentsView());
37 class PanedWidgetDelegate
: public views::WidgetDelegate
{
39 PanedWidgetDelegate(views::Widget
* widget
) : widget_(widget
) {}
41 void SetAccessiblePanes(const std::vector
<views::View
*>& panes
) {
42 accessible_panes_
= panes
;
45 // views::WidgetDelegate.
46 void GetAccessiblePanes(std::vector
<views::View
*>* panes
) override
{
47 std::copy(accessible_panes_
.begin(),
48 accessible_panes_
.end(),
49 std::back_inserter(*panes
));
51 views::Widget
* GetWidget() override
{ return widget_
; };
52 const views::Widget
* GetWidget() const override
{ return widget_
; }
55 views::Widget
* widget_
;
56 std::vector
<views::View
*> accessible_panes_
;
61 class FocusCyclerTest
: public AshTestBase
{
65 void SetUp() override
{
68 focus_cycler_
.reset(new FocusCycler());
70 ASSERT_TRUE(Shelf::ForPrimaryDisplay());
73 void TearDown() override
{
75 GetStatusAreaWidgetDelegate(tray_
->GetWidget())->
76 SetFocusCyclerForTesting(NULL
);
80 shelf_widget()->SetFocusCycler(NULL
);
82 focus_cycler_
.reset();
84 AshTestBase::TearDown();
88 // Creates the system tray, returning true on success.
92 aura::Window
* parent
=
93 Shell::GetPrimaryRootWindowController()->GetContainer(
94 ash::kShellWindowId_StatusContainer
);
96 StatusAreaWidget
* widget
= new StatusAreaWidget(parent
);
97 widget
->CreateTrayViews();
99 tray_
.reset(widget
->system_tray());
100 if (!tray_
->GetWidget())
102 focus_cycler_
->AddWidget(tray()->GetWidget());
103 GetStatusAreaWidgetDelegate(tray_
->GetWidget())->SetFocusCyclerForTesting(
108 FocusCycler
* focus_cycler() { return focus_cycler_
.get(); }
110 SystemTray
* tray() { return tray_
.get(); }
112 ShelfWidget
* shelf_widget() {
113 return Shelf::ForPrimaryDisplay()->shelf_widget();
116 void InstallFocusCycleOnShelf() {
118 shelf_widget()->SetFocusCycler(focus_cycler());
122 scoped_ptr
<FocusCycler
> focus_cycler_
;
123 scoped_ptr
<SystemTray
> tray_
;
125 DISALLOW_COPY_AND_ASSIGN(FocusCyclerTest
);
128 TEST_F(FocusCyclerTest
, CycleFocusBrowserOnly
) {
129 // Create a single test window.
130 scoped_ptr
<Window
> window0(CreateTestWindowInShellWithId(0));
131 wm::ActivateWindow(window0
.get());
132 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
135 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
136 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
139 TEST_F(FocusCyclerTest
, CycleFocusForward
) {
140 ASSERT_TRUE(CreateTray());
142 InstallFocusCycleOnShelf();
144 // Create a single test window.
145 scoped_ptr
<Window
> window0(CreateTestWindowInShellWithId(0));
146 wm::ActivateWindow(window0
.get());
147 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
149 // Cycle focus to the status area.
150 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
151 EXPECT_TRUE(tray()->GetWidget()->IsActive());
153 // Cycle focus to the shelf.
154 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
155 EXPECT_TRUE(shelf_widget()->IsActive());
157 // Cycle focus to the browser.
158 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
159 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
162 TEST_F(FocusCyclerTest
, CycleFocusBackward
) {
163 ASSERT_TRUE(CreateTray());
165 InstallFocusCycleOnShelf();
167 // Create a single test window.
168 scoped_ptr
<Window
> window0(CreateTestWindowInShellWithId(0));
169 wm::ActivateWindow(window0
.get());
170 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
172 // Cycle focus to the shelf.
173 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
174 EXPECT_TRUE(shelf_widget()->IsActive());
176 // Cycle focus to the status area.
177 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
178 EXPECT_TRUE(tray()->GetWidget()->IsActive());
180 // Cycle focus to the browser.
181 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
182 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
185 TEST_F(FocusCyclerTest
, CycleFocusForwardBackward
) {
186 ASSERT_TRUE(CreateTray());
188 InstallFocusCycleOnShelf();
190 // Create a single test window.
191 scoped_ptr
<Window
> window0(CreateTestWindowInShellWithId(0));
192 wm::ActivateWindow(window0
.get());
193 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
195 // Cycle focus to the shelf.
196 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
197 EXPECT_TRUE(shelf_widget()->IsActive());
199 // Cycle focus to the status area.
200 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
201 EXPECT_TRUE(tray()->GetWidget()->IsActive());
203 // Cycle focus to the browser.
204 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
205 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
207 // Cycle focus to the status area.
208 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
209 EXPECT_TRUE(tray()->GetWidget()->IsActive());
211 // Cycle focus to the shelf.
212 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
213 EXPECT_TRUE(shelf_widget()->IsActive());
215 // Cycle focus to the browser.
216 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
217 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
220 TEST_F(FocusCyclerTest
, CycleFocusNoBrowser
) {
221 ASSERT_TRUE(CreateTray());
223 InstallFocusCycleOnShelf();
225 // Add the shelf and focus it.
226 focus_cycler()->FocusWidget(shelf_widget());
228 // Cycle focus to the status area.
229 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
230 EXPECT_TRUE(tray()->GetWidget()->IsActive());
232 // Cycle focus to the shelf.
233 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
234 EXPECT_TRUE(shelf_widget()->IsActive());
236 // Cycle focus to the status area.
237 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
238 EXPECT_TRUE(tray()->GetWidget()->IsActive());
240 // Cycle focus to the shelf.
241 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
242 EXPECT_TRUE(shelf_widget()->IsActive());
244 // Cycle focus to the status area.
245 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
246 EXPECT_TRUE(tray()->GetWidget()->IsActive());
249 // Tests that focus cycles from the active browser to the status area and back.
250 TEST_F(FocusCyclerTest
, Shelf_CycleFocusForward
) {
251 ASSERT_TRUE(CreateTray());
252 InstallFocusCycleOnShelf();
253 shelf_widget()->Hide();
255 // Create two test windows.
256 scoped_ptr
<Window
> window0(CreateTestWindowInShellWithId(0));
257 scoped_ptr
<Window
> window1(CreateTestWindowInShellWithId(1));
258 wm::ActivateWindow(window1
.get());
259 wm::ActivateWindow(window0
.get());
260 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
262 // Cycle focus to the status area.
263 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
264 EXPECT_TRUE(tray()->GetWidget()->IsActive());
266 // Cycle focus to the browser.
267 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
268 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
270 // Cycle focus to the status area.
271 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
272 EXPECT_TRUE(tray()->GetWidget()->IsActive());
275 TEST_F(FocusCyclerTest
, Shelf_CycleFocusBackwardInvisible
) {
276 ASSERT_TRUE(CreateTray());
277 InstallFocusCycleOnShelf();
278 shelf_widget()->Hide();
280 // Create a single test window.
281 scoped_ptr
<Window
> window0(CreateTestWindowInShellWithId(0));
282 wm::ActivateWindow(window0
.get());
283 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
285 // Cycle focus to the status area.
286 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
287 EXPECT_TRUE(tray()->GetWidget()->IsActive());
289 // Cycle focus to the browser.
290 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
291 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
294 TEST_F(FocusCyclerTest
, CycleFocusThroughWindowWithPanes
) {
295 ASSERT_TRUE(CreateTray());
297 InstallFocusCycleOnShelf();
299 scoped_ptr
<PanedWidgetDelegate
> test_widget_delegate
;
300 scoped_ptr
<views::Widget
> browser_widget(new views::Widget
);
301 test_widget_delegate
.reset(new PanedWidgetDelegate(browser_widget
.get()));
302 views::Widget::InitParams
widget_params(
303 views::Widget::InitParams::TYPE_WINDOW
);
304 widget_params
.context
= CurrentContext();
305 widget_params
.delegate
= test_widget_delegate
.get();
306 widget_params
.ownership
=
307 views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
308 browser_widget
->Init(widget_params
);
309 browser_widget
->Show();
311 aura::Window
* browser_window
= browser_widget
->GetNativeView();
313 views::View
* root_view
= browser_widget
->GetRootView();
315 views::AccessiblePaneView
* pane1
= new views::AccessiblePaneView();
316 root_view
->AddChildView(pane1
);
318 views::View
* view1
= new views::View
;
319 view1
->SetFocusable(true);
320 pane1
->AddChildView(view1
);
322 views::View
* view2
= new views::View
;
323 view2
->SetFocusable(true);
324 pane1
->AddChildView(view2
);
326 views::AccessiblePaneView
* pane2
= new views::AccessiblePaneView();
327 root_view
->AddChildView(pane2
);
329 views::View
* view3
= new views::View
;
330 view3
->SetFocusable(true);
331 pane2
->AddChildView(view3
);
333 views::View
* view4
= new views::View
;
334 view4
->SetFocusable(true);
335 pane2
->AddChildView(view4
);
337 std::vector
<views::View
*> panes
;
338 panes
.push_back(pane1
);
339 panes
.push_back(pane2
);
341 test_widget_delegate
->SetAccessiblePanes(panes
);
343 views::FocusManager
* focus_manager
= browser_widget
->GetFocusManager();
345 // Cycle focus to the status area.
346 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
347 EXPECT_TRUE(tray()->GetWidget()->IsActive());
349 // Cycle focus to the shelf.
350 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
351 EXPECT_TRUE(shelf_widget()->IsActive());
353 // Cycle focus to the first pane in the browser.
354 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
355 EXPECT_TRUE(wm::IsActiveWindow(browser_window
));
356 EXPECT_EQ(focus_manager
->GetFocusedView(), view1
);
358 // Cycle focus to the second pane in the browser.
359 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
360 EXPECT_TRUE(wm::IsActiveWindow(browser_window
));
361 EXPECT_EQ(focus_manager
->GetFocusedView(), view3
);
363 // Cycle focus back to the status area.
364 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
365 EXPECT_TRUE(tray()->GetWidget()->IsActive());
367 // Reverse direction - back to the second pane in the browser.
368 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
369 EXPECT_TRUE(wm::IsActiveWindow(browser_window
));
370 EXPECT_EQ(focus_manager
->GetFocusedView(), view3
);
372 // Back to the first pane in the browser.
373 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
374 EXPECT_TRUE(wm::IsActiveWindow(browser_window
));
375 EXPECT_EQ(focus_manager
->GetFocusedView(), view1
);
377 // Back to the shelf.
378 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
379 EXPECT_TRUE(shelf_widget()->IsActive());
381 // Back to the status area.
382 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
383 EXPECT_TRUE(tray()->GetWidget()->IsActive());
385 // Pressing "Escape" while on the status area should
386 // deactivate it, and activate the browser window.
387 aura::Window
* root
= Shell::GetPrimaryRootWindow();
388 ui::test::EventGenerator
event_generator(root
, root
);
389 event_generator
.PressKey(ui::VKEY_ESCAPE
, 0);
390 EXPECT_TRUE(wm::IsActiveWindow(browser_window
));
391 EXPECT_EQ(focus_manager
->GetFocusedView(), view1
);
393 // Similarly, pressing "Escape" while on the shelf.
394 // should do the same thing.
395 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
396 EXPECT_TRUE(shelf_widget()->IsActive());
397 event_generator
.PressKey(ui::VKEY_ESCAPE
, 0);
398 EXPECT_TRUE(wm::IsActiveWindow(browser_window
));
399 EXPECT_EQ(focus_manager
->GetFocusedView(), view1
);