1 // Copyright 2012 The Chromium Authors
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"
9 #include "ash/shelf/shelf.h"
10 #include "ash/shelf/shelf_focus_cycler.h"
11 #include "ash/shelf/shelf_navigation_widget.h"
12 #include "ash/shelf/shelf_widget.h"
13 #include "ash/shell.h"
14 #include "ash/system/status_area_widget.h"
15 #include "ash/system/status_area_widget_delegate.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"
32 StatusAreaWidgetDelegate* GetStatusAreaWidgetDelegate(views::Widget* widget) {
33 return static_cast<StatusAreaWidgetDelegate*>(widget->GetContentsView());
36 class PanedWidgetDelegate : public views::WidgetDelegate {
38 PanedWidgetDelegate(views::Widget* widget) : widget_(widget) {}
40 void SetAccessiblePanes(const std::vector<views::View*>& panes) {
41 accessible_panes_ = panes;
44 // views::WidgetDelegate:
45 void GetAccessiblePanes(std::vector<views::View*>* panes) override {
46 std::copy(accessible_panes_.begin(), accessible_panes_.end(),
47 std::back_inserter(*panes));
49 views::Widget* GetWidget() override { return widget_; }
50 const views::Widget* GetWidget() const override { return widget_; }
53 views::Widget* widget_;
54 std::vector<views::View*> accessible_panes_;
59 class FocusCyclerTest : public AshTestBase {
61 FocusCyclerTest() = default;
63 FocusCyclerTest(const FocusCyclerTest&) = delete;
64 FocusCyclerTest& operator=(const FocusCyclerTest&) = delete;
66 void SetUp() override {
69 focus_cycler_ = std::make_unique<FocusCycler>();
72 void TearDown() override {
73 GetStatusAreaWidgetDelegate(GetPrimaryStatusAreaWidget())
74 ->SetFocusCyclerForTesting(nullptr);
76 GetPrimaryShelf()->shelf_widget()->SetFocusCycler(nullptr);
78 focus_cycler_.reset();
80 AshTestBase::TearDown();
84 // Setup the system tray focus cycler.
85 void SetUpTrayFocusCycle() {
86 views::Widget* system_tray_widget = GetPrimaryStatusAreaWidget();
87 ASSERT_TRUE(system_tray_widget);
88 focus_cycler_->AddWidget(system_tray_widget);
89 GetStatusAreaWidgetDelegate(system_tray_widget)
90 ->SetFocusCyclerForTesting(focus_cycler());
93 views::Widget* GetPrimaryStatusAreaWidget() {
94 return GetPrimaryShelf()->GetStatusAreaWidget();
97 FocusCycler* focus_cycler() { return focus_cycler_.get(); }
99 void InstallFocusCycleOnShelf() {
101 GetPrimaryShelf()->hotseat_widget()->SetFocusCycler(focus_cycler());
105 std::unique_ptr<FocusCycler> focus_cycler_;
108 TEST_F(FocusCyclerTest, CycleFocusBrowserOnly) {
109 // Create a single test window.
110 std::unique_ptr<Window> window0(CreateTestWindowInShellWithId(0));
111 wm::ActivateWindow(window0.get());
112 EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
115 focus_cycler()->RotateFocus(FocusCycler::FORWARD);
116 EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
119 TEST_F(FocusCyclerTest, CycleFocusForward) {
120 SetUpTrayFocusCycle();
122 InstallFocusCycleOnShelf();
124 // Create a single test window.
125 std::unique_ptr<Window> window0(CreateTestWindowInShellWithId(0));
126 wm::ActivateWindow(window0.get());
127 EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
129 // Cycle focus to the status area.
130 focus_cycler()->RotateFocus(FocusCycler::FORWARD);
131 EXPECT_TRUE(GetPrimaryStatusAreaWidget()->IsActive());
133 // Cycle focus to the shelf.
134 focus_cycler()->RotateFocus(FocusCycler::FORWARD);
135 EXPECT_TRUE(GetPrimaryShelf()->hotseat_widget()->IsActive());
137 // Cycle focus to the browser.
138 focus_cycler()->RotateFocus(FocusCycler::FORWARD);
139 EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
142 TEST_F(FocusCyclerTest, CycleFocusBackward) {
143 SetUpTrayFocusCycle();
145 InstallFocusCycleOnShelf();
147 // Create a single test window.
148 std::unique_ptr<Window> window0(CreateTestWindowInShellWithId(0));
149 wm::ActivateWindow(window0.get());
150 EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
152 // Cycle focus to the shelf.
153 focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
154 EXPECT_TRUE(GetPrimaryShelf()->hotseat_widget()->IsActive());
156 // Cycle focus to the status area.
157 focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
158 EXPECT_TRUE(GetPrimaryStatusAreaWidget()->IsActive());
160 // Cycle focus to the browser.
161 focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
162 EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
165 TEST_F(FocusCyclerTest, CycleFocusForwardBackward) {
166 SetUpTrayFocusCycle();
168 InstallFocusCycleOnShelf();
170 // Create a single test window.
171 std::unique_ptr<Window> window0(CreateTestWindowInShellWithId(0));
172 wm::ActivateWindow(window0.get());
173 EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
175 // Cycle focus to the shelf.
176 focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
177 EXPECT_TRUE(GetPrimaryShelf()->hotseat_widget()->IsActive());
179 // Cycle focus to the status area.
180 focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
181 EXPECT_TRUE(GetPrimaryStatusAreaWidget()->IsActive());
183 // Cycle focus to the browser.
184 focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
185 EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
187 // Cycle focus to the status area.
188 focus_cycler()->RotateFocus(FocusCycler::FORWARD);
189 EXPECT_TRUE(GetPrimaryStatusAreaWidget()->IsActive());
191 // Cycle focus to the shelf.
192 focus_cycler()->RotateFocus(FocusCycler::FORWARD);
193 EXPECT_TRUE(GetPrimaryShelf()->hotseat_widget()->IsActive());
195 // Cycle focus to the browser.
196 focus_cycler()->RotateFocus(FocusCycler::FORWARD);
197 EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
200 TEST_F(FocusCyclerTest, CycleFocusNoBrowser) {
201 SetUpTrayFocusCycle();
203 InstallFocusCycleOnShelf();
205 // Add the shelf and focus it.
206 focus_cycler()->FocusWidget(GetPrimaryShelf()->hotseat_widget());
208 // Cycle focus to the status area.
209 focus_cycler()->RotateFocus(FocusCycler::FORWARD);
210 EXPECT_TRUE(GetPrimaryStatusAreaWidget()->IsActive());
212 // Cycle focus to the shelf.
213 focus_cycler()->RotateFocus(FocusCycler::FORWARD);
214 EXPECT_TRUE(GetPrimaryShelf()->hotseat_widget()->IsActive());
216 // Cycle focus to the status area.
217 focus_cycler()->RotateFocus(FocusCycler::FORWARD);
218 EXPECT_TRUE(GetPrimaryStatusAreaWidget()->IsActive());
220 // Cycle focus to the shelf.
221 focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
222 EXPECT_TRUE(GetPrimaryShelf()->hotseat_widget()->IsActive());
224 // Cycle focus to the status area.
225 focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
226 EXPECT_TRUE(GetPrimaryStatusAreaWidget()->IsActive());
229 // Tests that focus cycles from the active browser to the status area and back.
230 TEST_F(FocusCyclerTest, Shelf_CycleFocusForward) {
231 SetUpTrayFocusCycle();
232 InstallFocusCycleOnShelf();
233 GetPrimaryShelf()->hotseat_widget()->Hide();
235 // Create two test windows.
236 std::unique_ptr<Window> window0(CreateTestWindowInShellWithId(0));
237 std::unique_ptr<Window> window1(CreateTestWindowInShellWithId(1));
238 wm::ActivateWindow(window1.get());
239 wm::ActivateWindow(window0.get());
240 EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
242 // Cycle focus to the status area.
243 focus_cycler()->RotateFocus(FocusCycler::FORWARD);
244 EXPECT_TRUE(GetPrimaryStatusAreaWidget()->IsActive());
246 // Cycle focus to the browser.
247 focus_cycler()->RotateFocus(FocusCycler::FORWARD);
248 EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
250 // Cycle focus to the status area.
251 focus_cycler()->RotateFocus(FocusCycler::FORWARD);
252 EXPECT_TRUE(GetPrimaryStatusAreaWidget()->IsActive());
255 TEST_F(FocusCyclerTest, Shelf_CycleFocusBackwardInvisible) {
256 SetUpTrayFocusCycle();
257 InstallFocusCycleOnShelf();
258 GetPrimaryShelf()->hotseat_widget()->Hide();
260 // Create a single test window.
261 std::unique_ptr<Window> window0(CreateTestWindowInShellWithId(0));
262 wm::ActivateWindow(window0.get());
263 EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
265 // Cycle focus to the status area.
266 focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
267 EXPECT_TRUE(GetPrimaryStatusAreaWidget()->IsActive());
269 // Cycle focus to the browser.
270 focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
271 EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
274 TEST_F(FocusCyclerTest, CycleFocusThroughWindowWithPanes) {
275 SetUpTrayFocusCycle();
277 InstallFocusCycleOnShelf();
279 std::unique_ptr<PanedWidgetDelegate> test_widget_delegate;
280 std::unique_ptr<views::Widget> browser_widget(new views::Widget);
281 test_widget_delegate =
282 std::make_unique<PanedWidgetDelegate>(browser_widget.get());
283 views::Widget::InitParams widget_params(
284 views::Widget::InitParams::TYPE_WINDOW);
285 widget_params.delegate = test_widget_delegate.get();
286 widget_params.ownership =
287 views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
288 widget_params.context = GetContext();
289 browser_widget->Init(std::move(widget_params));
290 browser_widget->Show();
292 aura::Window* browser_window = browser_widget->GetNativeView();
294 views::View* root_view = browser_widget->GetRootView();
296 // pane1 contains view1 and view2, pane2 contains view3 and view4.
297 views::AccessiblePaneView* pane1 = new views::AccessiblePaneView();
298 root_view->AddChildView(pane1);
300 views::View* view1 = new views::View;
301 view1->SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
302 pane1->AddChildView(view1);
304 views::View* view2 = new views::View;
305 view2->SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
306 pane1->AddChildView(view2);
308 views::AccessiblePaneView* pane2 = new views::AccessiblePaneView();
309 root_view->AddChildView(pane2);
311 views::View* view3 = new views::View;
312 view3->SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
313 pane2->AddChildView(view3);
315 views::View* view4 = new views::View;
316 view4->SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
317 pane2->AddChildView(view4);
319 std::vector<views::View*> panes;
320 panes.push_back(pane1);
321 panes.push_back(pane2);
323 test_widget_delegate->SetAccessiblePanes(panes);
325 views::FocusManager* focus_manager = browser_widget->GetFocusManager();
327 // Cycle focus to the status area.
328 focus_cycler()->RotateFocus(FocusCycler::FORWARD);
329 EXPECT_TRUE(GetPrimaryStatusAreaWidget()->IsActive());
331 // Cycle focus to the shelf.
332 focus_cycler()->RotateFocus(FocusCycler::FORWARD);
333 EXPECT_TRUE(GetPrimaryShelf()->hotseat_widget()->IsActive());
335 // Cycle focus to the first pane in the browser.
336 focus_cycler()->RotateFocus(FocusCycler::FORWARD);
337 EXPECT_TRUE(wm::IsActiveWindow(browser_window));
338 EXPECT_EQ(focus_manager->GetFocusedView(), view1);
340 // Cycle focus to the second pane in the browser.
341 focus_cycler()->RotateFocus(FocusCycler::FORWARD);
342 EXPECT_TRUE(wm::IsActiveWindow(browser_window));
343 EXPECT_EQ(focus_manager->GetFocusedView(), view3);
345 // Cycle focus back to the status area.
346 focus_cycler()->RotateFocus(FocusCycler::FORWARD);
347 EXPECT_TRUE(GetPrimaryStatusAreaWidget()->IsActive());
349 // Reverse direction - back to the second pane in the browser.
350 focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
351 EXPECT_TRUE(wm::IsActiveWindow(browser_window));
352 EXPECT_EQ(focus_manager->GetFocusedView(), view3);
354 // Back to the first pane in the browser.
355 focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
356 EXPECT_TRUE(wm::IsActiveWindow(browser_window));
357 EXPECT_EQ(focus_manager->GetFocusedView(), view1);
359 // Back to the shelf.
360 focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
361 EXPECT_TRUE(GetPrimaryShelf()->hotseat_widget()->IsActive());
363 // Back to the status area.
364 focus_cycler()->RotateFocus(FocusCycler::BACKWARD);
365 EXPECT_TRUE(GetPrimaryStatusAreaWidget()->IsActive());
367 // Pressing "Escape" while on the status area should
368 // deactivate it, and activate the browser window.
369 PressAndReleaseKey(ui::VKEY_ESCAPE);
370 EXPECT_TRUE(wm::IsActiveWindow(browser_window));
371 EXPECT_EQ(focus_manager->GetFocusedView(), view1);
373 // Similarly, pressing "Escape" while on the shelf should do the same thing.
374 // Focus the navigation widget directly because the shelf has no apps here.
375 GetPrimaryShelf()->shelf_focus_cycler()->FocusNavigation(false /* last */);
376 EXPECT_TRUE(GetPrimaryShelf()->navigation_widget()->IsActive());
377 PressAndReleaseKey(ui::VKEY_ESCAPE);
378 EXPECT_TRUE(wm::IsActiveWindow(browser_window));
379 EXPECT_EQ(focus_manager->GetFocusedView(), view1);
382 // Test that when the shelf widget & status area widget are removed, they should
383 // also be removed from focus cycler.
384 TEST_F(FocusCyclerTest, RemoveWidgetOnDisplayRemoved) {
385 // Two displays are added, so two shelf widgets and two status area widgets
386 // are added to focus cycler.
387 UpdateDisplay("800x700, 600x500");
388 // Remove one display. Its shelf widget and status area widget should also be
389 // removed from focus cycler.
390 UpdateDisplay("800x700");
392 // Create a single test window.
393 std::unique_ptr<Window> window(CreateTestWindowInShellWithId(0));
394 wm::ActivateWindow(window.get());
395 EXPECT_TRUE(wm::IsActiveWindow(window.get()));
397 // Cycle focus to the navigation widget.
398 Shell::Get()->focus_cycler()->RotateFocus(FocusCycler::FORWARD);
399 EXPECT_TRUE(GetPrimaryShelf()->navigation_widget()->IsActive());
401 // Cycle focus to the hotseat widget.
402 Shell::Get()->focus_cycler()->RotateFocus(FocusCycler::FORWARD);
403 EXPECT_TRUE(GetPrimaryShelf()->hotseat_widget()->IsActive());
405 // Cycle focus to the status area.
406 Shell::Get()->focus_cycler()->RotateFocus(FocusCycler::FORWARD);
407 EXPECT_TRUE(GetPrimaryStatusAreaWidget()->IsActive());
409 // Cycle focus should go back to the browser.
410 Shell::Get()->focus_cycler()->RotateFocus(FocusCycler::FORWARD);
411 EXPECT_TRUE(wm::IsActiveWindow(window.get()));