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/launcher/launcher.h"
8 #include "ash/root_window_controller.h"
9 #include "ash/shelf/shelf_widget.h"
10 #include "ash/shell.h"
11 #include "ash/shell_window_ids.h"
12 #include "ash/system/status_area_widget.h"
13 #include "ash/system/status_area_widget_delegate.h"
14 #include "ash/system/tray/system_tray.h"
15 #include "ash/wm/window_util.h"
16 #include "ash/test/ash_test_base.h"
17 #include "ash/shell_factory.h"
18 #include "ui/aura/root_window.h"
19 #include "ui/aura/test/event_generator.h"
20 #include "ui/aura/test/test_windows.h"
21 #include "ui/aura/window.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
) {
50 std::copy(accessible_panes_
.begin(), accessible_panes_
.end(),
51 std::back_inserter(*panes
));
53 virtual views::Widget
* GetWidget() OVERRIDE
{
56 virtual const views::Widget
* GetWidget() const OVERRIDE
{
61 views::Widget
* widget_
;
62 std::vector
<views::View
*> accessible_panes_
;
67 class FocusCyclerTest
: public AshTestBase
{
71 virtual void SetUp() OVERRIDE
{
74 focus_cycler_
.reset(new FocusCycler());
76 ASSERT_TRUE(Launcher::ForPrimaryDisplay());
79 virtual void TearDown() OVERRIDE
{
81 GetStatusAreaWidgetDelegate(tray_
->GetWidget())->
82 SetFocusCyclerForTesting(NULL
);
86 shelf_widget()->SetFocusCycler(NULL
);
88 focus_cycler_
.reset();
90 AshTestBase::TearDown();
94 // Creates the system tray, returning true on success.
98 aura::Window
* parent
= Shell::GetPrimaryRootWindowController()->
99 GetContainer(ash::internal::kShellWindowId_StatusContainer
);
101 internal::StatusAreaWidget
* widget
= new internal::StatusAreaWidget(parent
);
102 widget
->CreateTrayViews();
104 tray_
.reset(widget
->system_tray());
105 if (!tray_
->GetWidget())
107 focus_cycler_
->AddWidget(tray()->GetWidget());
108 GetStatusAreaWidgetDelegate(tray_
->GetWidget())->SetFocusCyclerForTesting(
113 FocusCycler
* focus_cycler() { return focus_cycler_
.get(); }
115 SystemTray
* tray() { return tray_
.get(); }
117 ShelfWidget
* shelf_widget() {
118 return Launcher::ForPrimaryDisplay()->shelf_widget();
121 void InstallFocusCycleOnShelf() {
123 shelf_widget()->SetFocusCycler(focus_cycler());
127 scoped_ptr
<FocusCycler
> focus_cycler_
;
128 scoped_ptr
<SystemTray
> tray_
;
130 DISALLOW_COPY_AND_ASSIGN(FocusCyclerTest
);
133 TEST_F(FocusCyclerTest
, CycleFocusBrowserOnly
) {
134 // Create a single test window.
135 scoped_ptr
<Window
> window0(CreateTestWindowInShellWithId(0));
136 wm::ActivateWindow(window0
.get());
137 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
140 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
141 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
144 TEST_F(FocusCyclerTest
, CycleFocusForward
) {
145 ASSERT_TRUE(CreateTray());
147 InstallFocusCycleOnShelf();
149 // Create a single test window.
150 scoped_ptr
<Window
> window0(CreateTestWindowInShellWithId(0));
151 wm::ActivateWindow(window0
.get());
152 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
154 // Cycle focus to the status area.
155 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
156 EXPECT_TRUE(tray()->GetWidget()->IsActive());
158 // Cycle focus to the shelf.
159 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
160 EXPECT_TRUE(shelf_widget()->IsActive());
162 // Cycle focus to the browser.
163 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
164 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
167 TEST_F(FocusCyclerTest
, CycleFocusBackward
) {
168 ASSERT_TRUE(CreateTray());
170 InstallFocusCycleOnShelf();
172 // Create a single test window.
173 scoped_ptr
<Window
> window0(CreateTestWindowInShellWithId(0));
174 wm::ActivateWindow(window0
.get());
175 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
177 // Cycle focus to the shelf.
178 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
179 EXPECT_TRUE(shelf_widget()->IsActive());
181 // Cycle focus to the status area.
182 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
183 EXPECT_TRUE(tray()->GetWidget()->IsActive());
185 // Cycle focus to the browser.
186 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
187 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
190 TEST_F(FocusCyclerTest
, CycleFocusForwardBackward
) {
191 ASSERT_TRUE(CreateTray());
193 InstallFocusCycleOnShelf();
195 // Create a single test window.
196 scoped_ptr
<Window
> window0(CreateTestWindowInShellWithId(0));
197 wm::ActivateWindow(window0
.get());
198 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
200 // Cycle focus to the shelf.
201 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
202 EXPECT_TRUE(shelf_widget()->IsActive());
204 // Cycle focus to the status area.
205 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
206 EXPECT_TRUE(tray()->GetWidget()->IsActive());
208 // Cycle focus to the browser.
209 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
210 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
212 // Cycle focus to the status area.
213 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
214 EXPECT_TRUE(tray()->GetWidget()->IsActive());
216 // Cycle focus to the shelf.
217 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
218 EXPECT_TRUE(shelf_widget()->IsActive());
220 // Cycle focus to the browser.
221 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
222 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
225 TEST_F(FocusCyclerTest
, CycleFocusNoBrowser
) {
226 ASSERT_TRUE(CreateTray());
228 InstallFocusCycleOnShelf();
230 // Add the shelf and focus it.
231 focus_cycler()->FocusWidget(shelf_widget());
233 // Cycle focus to the status area.
234 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
235 EXPECT_TRUE(tray()->GetWidget()->IsActive());
237 // Cycle focus to the shelf.
238 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
239 EXPECT_TRUE(shelf_widget()->IsActive());
241 // Cycle focus to the status area.
242 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
243 EXPECT_TRUE(tray()->GetWidget()->IsActive());
245 // Cycle focus to the shelf.
246 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
247 EXPECT_TRUE(shelf_widget()->IsActive());
249 // Cycle focus to the status area.
250 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
251 EXPECT_TRUE(tray()->GetWidget()->IsActive());
254 TEST_F(FocusCyclerTest
, Shelf_CycleFocusForward
) {
255 ASSERT_TRUE(CreateTray());
256 InstallFocusCycleOnShelf();
257 shelf_widget()->Hide();
259 // Create a single test window.
260 scoped_ptr
<Window
> window0(CreateTestWindowInShellWithId(0));
261 wm::ActivateWindow(window0
.get());
262 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
264 // Cycle focus to the status area.
265 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
266 EXPECT_TRUE(tray()->GetWidget()->IsActive());
268 // Cycle focus to the browser.
269 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
270 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
273 TEST_F(FocusCyclerTest
, Shelf_CycleFocusBackwardInvisible
) {
274 ASSERT_TRUE(CreateTray());
275 InstallFocusCycleOnShelf();
276 shelf_widget()->Hide();
278 // Create a single test window.
279 scoped_ptr
<Window
> window0(CreateTestWindowInShellWithId(0));
280 wm::ActivateWindow(window0
.get());
281 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
283 // Cycle focus to the status area.
284 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
285 EXPECT_TRUE(tray()->GetWidget()->IsActive());
287 // Cycle focus to the browser.
288 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
289 EXPECT_TRUE(wm::IsActiveWindow(window0
.get()));
292 TEST_F(FocusCyclerTest
, CycleFocusThroughWindowWithPanes
) {
293 ASSERT_TRUE(CreateTray());
295 InstallFocusCycleOnShelf();
297 scoped_ptr
<views::Widget
> browser_widget(new views::Widget
);
298 PanedWidgetDelegate
* test_widget_delegate
=
299 new PanedWidgetDelegate(browser_widget
.get());
300 views::Widget::InitParams
widget_params(
301 views::Widget::InitParams::TYPE_WINDOW
);
302 widget_params
.context
= CurrentContext();
303 widget_params
.delegate
= test_widget_delegate
;
304 widget_params
.ownership
=
305 views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
306 browser_widget
->Init(widget_params
);
307 browser_widget
->Show();
309 aura::Window
* browser_window
= browser_widget
->GetNativeView();
311 views::View
* root_view
= browser_widget
->GetRootView();
313 views::AccessiblePaneView
* pane1
= new views::AccessiblePaneView();
314 root_view
->AddChildView(pane1
);
316 views::View
* view1
= new views::View
;
317 view1
->set_focusable(true);
318 pane1
->AddChildView(view1
);
320 views::View
* view2
= new views::View
;
321 view2
->set_focusable(true);
322 pane1
->AddChildView(view2
);
324 views::AccessiblePaneView
* pane2
= new views::AccessiblePaneView();
325 root_view
->AddChildView(pane2
);
327 views::View
* view3
= new views::View
;
328 view3
->set_focusable(true);
329 pane2
->AddChildView(view3
);
331 views::View
* view4
= new views::View
;
332 view4
->set_focusable(true);
333 pane2
->AddChildView(view4
);
335 std::vector
<views::View
*> panes
;
336 panes
.push_back(pane1
);
337 panes
.push_back(pane2
);
339 test_widget_delegate
->SetAccessiblePanes(panes
);
341 views::FocusManager
* focus_manager
= browser_widget
->GetFocusManager();
343 // Cycle focus to the status area.
344 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
345 EXPECT_TRUE(tray()->GetWidget()->IsActive());
347 // Cycle focus to the shelf.
348 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
349 EXPECT_TRUE(shelf_widget()->IsActive());
351 // Cycle focus to the first pane in the browser.
352 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
353 EXPECT_TRUE(wm::IsActiveWindow(browser_window
));
354 EXPECT_EQ(focus_manager
->GetFocusedView(), view1
);
356 // Cycle focus to the second pane in the browser.
357 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
358 EXPECT_TRUE(wm::IsActiveWindow(browser_window
));
359 EXPECT_EQ(focus_manager
->GetFocusedView(), view3
);
361 // Cycle focus back to the status area.
362 focus_cycler()->RotateFocus(FocusCycler::FORWARD
);
363 EXPECT_TRUE(tray()->GetWidget()->IsActive());
365 // Reverse direction - back to the second pane in the browser.
366 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
367 EXPECT_TRUE(wm::IsActiveWindow(browser_window
));
368 EXPECT_EQ(focus_manager
->GetFocusedView(), view3
);
370 // Back to the first pane in the browser.
371 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
372 EXPECT_TRUE(wm::IsActiveWindow(browser_window
));
373 EXPECT_EQ(focus_manager
->GetFocusedView(), view1
);
375 // Back to the shelf.
376 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
377 EXPECT_TRUE(shelf_widget()->IsActive());
379 // Back to the status area.
380 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
381 EXPECT_TRUE(tray()->GetWidget()->IsActive());
383 // Pressing "Escape" while on the status area should
384 // deactivate it, and activate the browser window.
385 aura::RootWindow
* root
= Shell::GetPrimaryRootWindow();
386 aura::test::EventGenerator
event_generator(root
, root
);
387 event_generator
.PressKey(ui::VKEY_ESCAPE
, 0);
388 EXPECT_TRUE(wm::IsActiveWindow(browser_window
));
389 EXPECT_EQ(focus_manager
->GetFocusedView(), view1
);
391 // Similarly, pressing "Escape" while on the shelf.
392 // should do the same thing.
393 focus_cycler()->RotateFocus(FocusCycler::BACKWARD
);
394 EXPECT_TRUE(shelf_widget()->IsActive());
395 event_generator
.PressKey(ui::VKEY_ESCAPE
, 0);
396 EXPECT_TRUE(wm::IsActiveWindow(browser_window
));
397 EXPECT_EQ(focus_manager
->GetFocusedView(), view1
);