| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ash/wm/window_cycle_controller.h" |
| |
| #include <algorithm> |
| #include <memory> |
| |
| #include "ash/app_list/test/app_list_test_helper.h" |
| #include "ash/focus_cycler.h" |
| #include "ash/public/cpp/shell_window_ids.h" |
| #include "ash/public/cpp/window_properties.h" |
| #include "ash/scoped_root_window_for_new_windows.h" |
| #include "ash/session/session_controller.h" |
| #include "ash/session/test_session_controller_client.h" |
| #include "ash/shelf/shelf.h" |
| #include "ash/shelf/shelf_view_test_api.h" |
| #include "ash/shelf/shelf_widget.h" |
| #include "ash/shell.h" |
| #include "ash/test/ash_test_base.h" |
| #include "ash/test_shell_delegate.h" |
| #include "ash/wm/window_cycle_list.h" |
| #include "ash/wm/window_state.h" |
| #include "ash/wm/window_util.h" |
| #include "ash/wm/wm_event.h" |
| #include "ui/aura/client/aura_constants.h" |
| #include "ui/aura/client/screen_position_client.h" |
| #include "ui/aura/env.h" |
| #include "ui/aura/test/test_windows.h" |
| #include "ui/aura/window.h" |
| #include "ui/aura/window_event_dispatcher.h" |
| #include "ui/display/display_layout_builder.h" |
| #include "ui/display/manager/display_layout_store.h" |
| #include "ui/display/manager/display_manager.h" |
| #include "ui/display/test/display_manager_test_api.h" |
| #include "ui/events/event_handler.h" |
| #include "ui/events/test/event_generator.h" |
| #include "ui/gfx/geometry/rect.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| class EventCounter : public ui::EventHandler { |
| public: |
| EventCounter() : key_events_(0), mouse_events_(0) {} |
| ~EventCounter() override = default; |
| |
| int GetKeyEventCountAndReset() { |
| int count = key_events_; |
| key_events_ = 0; |
| return count; |
| } |
| |
| int GetMouseEventCountAndReset() { |
| int count = mouse_events_; |
| mouse_events_ = 0; |
| return count; |
| } |
| |
| // ui::EventHandler: |
| void OnKeyEvent(ui::KeyEvent* event) override { key_events_++; } |
| void OnMouseEvent(ui::MouseEvent* event) override { mouse_events_++; } |
| |
| private: |
| int key_events_; |
| int mouse_events_; |
| |
| DISALLOW_COPY_AND_ASSIGN(EventCounter); |
| }; |
| |
| bool IsWindowMinimized(aura::Window* window) { |
| return wm::GetWindowState(window)->IsMinimized(); |
| } |
| |
| } // namespace |
| |
| using aura::test::CreateTestWindowWithId; |
| using aura::test::TestWindowDelegate; |
| using aura::Window; |
| |
| class WindowCycleControllerTest : public AshTestBase { |
| public: |
| WindowCycleControllerTest() = default; |
| ~WindowCycleControllerTest() override = default; |
| |
| void SetUp() override { |
| AshTestBase::SetUp(); |
| |
| WindowCycleList::DisableInitialDelayForTesting(); |
| |
| shelf_view_test_.reset( |
| new ShelfViewTestAPI(GetPrimaryShelf()->GetShelfViewForTesting())); |
| shelf_view_test_->SetAnimationDuration(1); |
| } |
| |
| const aura::Window::Windows GetWindows(WindowCycleController* controller) { |
| return controller->window_cycle_list()->windows(); |
| } |
| |
| const views::Widget* GetWindowCycleListWidget() const { |
| return Shell::Get() |
| ->window_cycle_controller() |
| ->window_cycle_list() |
| ->widget(); |
| } |
| |
| private: |
| std::unique_ptr<ShelfViewTestAPI> shelf_view_test_; |
| |
| DISALLOW_COPY_AND_ASSIGN(WindowCycleControllerTest); |
| }; |
| |
| TEST_F(WindowCycleControllerTest, HandleCycleWindowBaseCases) { |
| WindowCycleController* controller = Shell::Get()->window_cycle_controller(); |
| |
| // Cycling doesn't crash if there are no windows. |
| controller->HandleCycleWindow(WindowCycleController::FORWARD); |
| |
| // Create a single test window. |
| std::unique_ptr<Window> window0(CreateTestWindowInShellWithId(0)); |
| wm::ActivateWindow(window0.get()); |
| EXPECT_TRUE(wm::IsActiveWindow(window0.get())); |
| |
| // Cycling works for a single window, even though nothing changes. |
| controller->HandleCycleWindow(WindowCycleController::FORWARD); |
| EXPECT_TRUE(wm::IsActiveWindow(window0.get())); |
| } |
| |
| // Verifies if there is only one window and it isn't active that cycling |
| // activates it. |
| TEST_F(WindowCycleControllerTest, SingleWindowNotActive) { |
| WindowCycleController* controller = Shell::Get()->window_cycle_controller(); |
| |
| // Create a single test window. |
| std::unique_ptr<Window> window0(CreateTestWindowInShellWithId(0)); |
| wm::ActivateWindow(window0.get()); |
| EXPECT_TRUE(wm::IsActiveWindow(window0.get())); |
| |
| // Rotate focus, this should move focus to another window that isn't part of |
| // the default container. |
| Shell::Get()->focus_cycler()->RotateFocus(FocusCycler::FORWARD); |
| EXPECT_FALSE(wm::IsActiveWindow(window0.get())); |
| |
| // Cycling should activate the window. |
| controller->HandleCycleWindow(WindowCycleController::FORWARD); |
| EXPECT_TRUE(wm::IsActiveWindow(window0.get())); |
| } |
| |
| TEST_F(WindowCycleControllerTest, HandleCycleWindow) { |
| WindowCycleController* controller = Shell::Get()->window_cycle_controller(); |
| |
| // Set up several windows to use to test cycling. Create them in reverse |
| // order so they are stacked 0 over 1 over 2. |
| std::unique_ptr<Window> window2(CreateTestWindowInShellWithId(2)); |
| std::unique_ptr<Window> window1(CreateTestWindowInShellWithId(1)); |
| std::unique_ptr<Window> window0(CreateTestWindowInShellWithId(0)); |
| wm::ActivateWindow(window0.get()); |
| |
| // Simulate pressing and releasing Alt-tab. |
| EXPECT_TRUE(wm::IsActiveWindow(window0.get())); |
| controller->HandleCycleWindow(WindowCycleController::FORWARD); |
| |
| // Window lists should return the topmost window in front. |
| ASSERT_TRUE(controller->window_cycle_list()); |
| ASSERT_EQ(3u, GetWindows(controller).size()); |
| ASSERT_EQ(window0.get(), GetWindows(controller)[0]); |
| ASSERT_EQ(window1.get(), GetWindows(controller)[1]); |
| ASSERT_EQ(window2.get(), GetWindows(controller)[2]); |
| |
| controller->CompleteCycling(); |
| EXPECT_TRUE(wm::IsActiveWindow(window1.get())); |
| |
| // Pressing and releasing Alt-tab again should cycle back to the most- |
| // recently-used window in the current child order. |
| controller->HandleCycleWindow(WindowCycleController::FORWARD); |
| controller->CompleteCycling(); |
| EXPECT_TRUE(wm::IsActiveWindow(window0.get())); |
| |
| // Cancelled cycling shouldn't move the active window. |
| controller->HandleCycleWindow(WindowCycleController::FORWARD); |
| controller->CancelCycling(); |
| EXPECT_TRUE(wm::IsActiveWindow(window0.get())); |
| |
| // Pressing Alt-tab multiple times without releasing Alt should cycle through |
| // all the windows and wrap around. |
| controller->HandleCycleWindow(WindowCycleController::FORWARD); |
| EXPECT_TRUE(controller->IsCycling()); |
| |
| controller->HandleCycleWindow(WindowCycleController::FORWARD); |
| EXPECT_TRUE(controller->IsCycling()); |
| |
| controller->HandleCycleWindow(WindowCycleController::FORWARD); |
| EXPECT_TRUE(controller->IsCycling()); |
| |
| controller->CompleteCycling(); |
| EXPECT_FALSE(controller->IsCycling()); |
| EXPECT_TRUE(wm::IsActiveWindow(window0.get())); |
| |
| // Reset our stacking order. |
| wm::ActivateWindow(window2.get()); |
| wm::ActivateWindow(window1.get()); |
| wm::ActivateWindow(window0.get()); |
| |
| // Likewise we can cycle backwards through the windows. |
| controller->HandleCycleWindow(WindowCycleController::BACKWARD); |
| controller->HandleCycleWindow(WindowCycleController::BACKWARD); |
| controller->CompleteCycling(); |
| EXPECT_TRUE(wm::IsActiveWindow(window1.get())); |
| |
| // Reset our stacking order. |
| wm::ActivateWindow(window2.get()); |
| wm::ActivateWindow(window1.get()); |
| wm::ActivateWindow(window0.get()); |
| |
| // When the screen is locked, cycling window does not take effect. |
| Shell::Get()->session_controller()->LockScreenAndFlushForTest(); |
| EXPECT_TRUE(wm::IsActiveWindow(window0.get())); |
| controller->HandleCycleWindow(WindowCycleController::FORWARD); |
| EXPECT_FALSE(controller->IsCycling()); |
| |
| // Unlock, it works again. |
| GetSessionControllerClient()->UnlockScreen(); |
| EXPECT_TRUE(wm::IsActiveWindow(window0.get())); |
| controller->HandleCycleWindow(WindowCycleController::FORWARD); |
| controller->HandleCycleWindow(WindowCycleController::FORWARD); |
| controller->CompleteCycling(); |
| EXPECT_TRUE(wm::IsActiveWindow(window2.get())); |
| |
| // When a modal window is active, cycling window does not take effect. |
| aura::Window* modal_container = Shell::GetContainer( |
| Shell::GetPrimaryRootWindow(), kShellWindowId_SystemModalContainer); |
| std::unique_ptr<Window> modal_window( |
| CreateTestWindowWithId(-2, modal_container)); |
| modal_window->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_SYSTEM); |
| wm::ActivateWindow(modal_window.get()); |
| EXPECT_TRUE(wm::IsActiveWindow(modal_window.get())); |
| controller->HandleCycleWindow(WindowCycleController::FORWARD); |
| EXPECT_TRUE(wm::IsActiveWindow(modal_window.get())); |
| EXPECT_FALSE(controller->IsCycling()); |
| EXPECT_FALSE(wm::IsActiveWindow(window0.get())); |
| EXPECT_FALSE(wm::IsActiveWindow(window1.get())); |
| EXPECT_FALSE(wm::IsActiveWindow(window2.get())); |
| controller->HandleCycleWindow(WindowCycleController::BACKWARD); |
| EXPECT_TRUE(wm::IsActiveWindow(modal_window.get())); |
| EXPECT_FALSE(controller->IsCycling()); |
| EXPECT_FALSE(wm::IsActiveWindow(window0.get())); |
| EXPECT_FALSE(wm::IsActiveWindow(window1.get())); |
| EXPECT_FALSE(wm::IsActiveWindow(window2.get())); |
| |
| modal_window.reset(); |
| std::unique_ptr<Window> skip_overview_window( |
| CreateTestWindowInShellWithId(-3)); |
| skip_overview_window->SetProperty(kHideInOverviewKey, true); |
| wm::ActivateWindow(window0.get()); |
| wm::ActivateWindow(skip_overview_window.get()); |
| wm::ActivateWindow(window1.get()); |
| EXPECT_FALSE(wm::IsActiveWindow(window0.get())); |
| controller->HandleCycleWindow(WindowCycleController::FORWARD); |
| controller->CompleteCycling(); |
| EXPECT_TRUE(wm::IsActiveWindow(window0.get())); |
| EXPECT_FALSE(wm::IsActiveWindow(skip_overview_window.get())); |
| EXPECT_FALSE(wm::IsActiveWindow(window1.get())); |
| } |
| |
| // Cycles between a maximized and normal window. |
| TEST_F(WindowCycleControllerTest, MaximizedWindow) { |
| // Create a couple of test windows. |
| std::unique_ptr<Window> window0(CreateTestWindowInShellWithId(0)); |
| std::unique_ptr<Window> window1(CreateTestWindowInShellWithId(1)); |
| wm::WindowState* window1_state = wm::GetWindowState(window1.get()); |
| window1_state->Maximize(); |
| window1_state->Activate(); |
| EXPECT_TRUE(window1_state->IsActive()); |
| |
| // Rotate focus, this should move focus to window0. |
| WindowCycleController* controller = Shell::Get()->window_cycle_controller(); |
| controller->HandleCycleWindow(WindowCycleController::FORWARD); |
| controller->CompleteCycling(); |
| EXPECT_TRUE(wm::GetWindowState(window0.get())->IsActive()); |
| EXPECT_FALSE(window1_state->IsActive()); |
| |
| // One more time. |
| controller->HandleCycleWindow(WindowCycleController::FORWARD); |
| controller->CompleteCycling(); |
| EXPECT_TRUE(window1_state->IsActive()); |
| } |
| |
| // Cycles to a minimized window. |
| TEST_F(WindowCycleControllerTest, Minimized) { |
| // Create a couple of test windows. |
| std::unique_ptr<Window> window0(CreateTestWindowInShellWithId(0)); |
| std::unique_ptr<Window> window1(CreateTestWindowInShellWithId(1)); |
| wm::WindowState* window0_state = wm::GetWindowState(window0.get()); |
| wm::WindowState* window1_state = wm::GetWindowState(window1.get()); |
| |
| window1_state->Minimize(); |
| window0_state->Activate(); |
| EXPECT_TRUE(window0_state->IsActive()); |
| |
| // Rotate focus, this should move focus to window1 and unminimize it. |
| WindowCycleController* controller = Shell::Get()->window_cycle_controller(); |
| controller->HandleCycleWindow(WindowCycleController::FORWARD); |
| controller->CompleteCycling(); |
| EXPECT_FALSE(window0_state->IsActive()); |
| EXPECT_FALSE(window1_state->IsMinimized()); |
| EXPECT_TRUE(window1_state->IsActive()); |
| |
| // One more time back to w0. |
| controller->HandleCycleWindow(WindowCycleController::FORWARD); |
| controller->CompleteCycling(); |
| EXPECT_TRUE(window0_state->IsActive()); |
| } |
| |
| // Tests that when all windows are minimized, cycling starts with the first one |
| // rather than the second. |
| TEST_F(WindowCycleControllerTest, AllAreMinimized) { |
| // Create a couple of test windows. |
| std::unique_ptr<Window> window0(CreateTestWindowInShellWithId(0)); |
| std::unique_ptr<Window> window1(CreateTestWindowInShellWithId(1)); |
| wm::WindowState* window0_state = wm::GetWindowState(window0.get()); |
| wm::WindowState* window1_state = wm::GetWindowState(window1.get()); |
| |
| window0_state->Minimize(); |
| window1_state->Minimize(); |
| |
| WindowCycleController* controller = Shell::Get()->window_cycle_controller(); |
| controller->HandleCycleWindow(WindowCycleController::FORWARD); |
| controller->CompleteCycling(); |
| EXPECT_TRUE(window0_state->IsActive()); |
| EXPECT_FALSE(window0_state->IsMinimized()); |
| EXPECT_TRUE(window1_state->IsMinimized()); |
| |
| // But it's business as usual when cycling backwards. |
| window0_state->Minimize(); |
| window1_state->Minimize(); |
| controller->HandleCycleWindow(WindowCycleController::BACKWARD); |
| controller->CompleteCycling(); |
| EXPECT_TRUE(window0_state->IsMinimized()); |
| EXPECT_TRUE(window1_state->IsActive()); |
| EXPECT_FALSE(window1_state->IsMinimized()); |
| } |
| |
| TEST_F(WindowCycleControllerTest, AlwaysOnTopWindow) { |
| WindowCycleController* controller = Shell::Get()->window_cycle_controller(); |
| |
| // Set up several windows to use to test cycling. |
| std::unique_ptr<Window> window0(CreateTestWindowInShellWithId(0)); |
| std::unique_ptr<Window> window1(CreateTestWindowInShellWithId(1)); |
| |
| Window* top_container = Shell::GetContainer( |
| Shell::GetPrimaryRootWindow(), kShellWindowId_AlwaysOnTopContainer); |
| std::unique_ptr<Window> window2(CreateTestWindowWithId(2, top_container)); |
| wm::ActivateWindow(window0.get()); |
| |
| // Simulate pressing and releasing Alt-tab. |
| EXPECT_TRUE(wm::IsActiveWindow(window0.get())); |
| controller->HandleCycleWindow(WindowCycleController::FORWARD); |
| |
| // Window lists should return the topmost window in front. |
| ASSERT_TRUE(controller->window_cycle_list()); |
| ASSERT_EQ(3u, GetWindows(controller).size()); |
| EXPECT_EQ(window0.get(), GetWindows(controller)[0]); |
| EXPECT_EQ(window2.get(), GetWindows(controller)[1]); |
| EXPECT_EQ(window1.get(), GetWindows(controller)[2]); |
| |
| controller->CompleteCycling(); |
| } |
| |
| TEST_F(WindowCycleControllerTest, AlwaysOnTopMultiWindow) { |
| WindowCycleController* controller = Shell::Get()->window_cycle_controller(); |
| |
| // Set up several windows to use to test cycling. |
| std::unique_ptr<Window> window0(CreateTestWindowInShellWithId(0)); |
| std::unique_ptr<Window> window1(CreateTestWindowInShellWithId(1)); |
| |
| Window* top_container = Shell::GetContainer( |
| Shell::GetPrimaryRootWindow(), kShellWindowId_AlwaysOnTopContainer); |
| std::unique_ptr<Window> window2(CreateTestWindowWithId(2, top_container)); |
| std::unique_ptr<Window> window3(CreateTestWindowWithId(3, top_container)); |
| wm::ActivateWindow(window0.get()); |
| |
| // Simulate pressing and releasing Alt-tab. |
| EXPECT_TRUE(wm::IsActiveWindow(window0.get())); |
| controller->HandleCycleWindow(WindowCycleController::FORWARD); |
| |
| // Window lists should return the topmost window in front. |
| ASSERT_TRUE(controller->window_cycle_list()); |
| ASSERT_EQ(4u, GetWindows(controller).size()); |
| EXPECT_EQ(window0.get(), GetWindows(controller)[0]); |
| EXPECT_EQ(window3.get(), GetWindows(controller)[1]); |
| EXPECT_EQ(window2.get(), GetWindows(controller)[2]); |
| EXPECT_EQ(window1.get(), GetWindows(controller)[3]); |
| |
| controller->CompleteCycling(); |
| } |
| |
| TEST_F(WindowCycleControllerTest, AlwaysOnTopMultipleRootWindows) { |
| // Set up a second root window |
| UpdateDisplay("1000x600,600x400"); |
| aura::Window::Windows root_windows = Shell::GetAllRootWindows(); |
| ASSERT_EQ(2U, root_windows.size()); |
| |
| WindowCycleController* controller = Shell::Get()->window_cycle_controller(); |
| |
| // Create two windows in the primary root. |
| std::unique_ptr<Window> window0(CreateTestWindowInShellWithId(0)); |
| EXPECT_EQ(root_windows[0], window0->GetRootWindow()); |
| Window* top_container0 = |
| Shell::GetContainer(root_windows[0], kShellWindowId_AlwaysOnTopContainer); |
| std::unique_ptr<Window> window1(CreateTestWindowWithId(1, top_container0)); |
| EXPECT_EQ(root_windows[0], window1->GetRootWindow()); |
| |
| // Move the active root window to the secondary root and create two windows. |
| ScopedRootWindowForNewWindows root_for_new_windows(root_windows[1]); |
| std::unique_ptr<Window> window2(CreateTestWindowInShellWithId(2)); |
| EXPECT_EQ(root_windows[1], window2->GetRootWindow()); |
| |
| Window* top_container1 = |
| Shell::GetContainer(root_windows[1], kShellWindowId_AlwaysOnTopContainer); |
| std::unique_ptr<Window> window3(CreateTestWindowWithId(3, top_container1)); |
| EXPECT_EQ(root_windows[1], window3->GetRootWindow()); |
| |
| wm::ActivateWindow(window2.get()); |
| |
| EXPECT_EQ(root_windows[0], window0->GetRootWindow()); |
| EXPECT_EQ(root_windows[0], window1->GetRootWindow()); |
| EXPECT_EQ(root_windows[1], window2->GetRootWindow()); |
| EXPECT_EQ(root_windows[1], window3->GetRootWindow()); |
| |
| // Simulate pressing and releasing Alt-tab. |
| EXPECT_TRUE(wm::IsActiveWindow(window2.get())); |
| controller->HandleCycleWindow(WindowCycleController::FORWARD); |
| |
| // Window lists should return the topmost window in front. |
| ASSERT_TRUE(controller->window_cycle_list()); |
| ASSERT_EQ(4u, GetWindows(controller).size()); |
| EXPECT_EQ(window2.get(), GetWindows(controller)[0]); |
| EXPECT_EQ(window3.get(), GetWindows(controller)[1]); |
| EXPECT_EQ(window1.get(), GetWindows(controller)[2]); |
| EXPECT_EQ(window0.get(), GetWindows(controller)[3]); |
| |
| controller->CompleteCycling(); |
| } |
| |
| TEST_F(WindowCycleControllerTest, MostRecentlyUsed) { |
| WindowCycleController* controller = Shell::Get()->window_cycle_controller(); |
| |
| // Set up several windows to use to test cycling. |
| std::unique_ptr<Window> window0(CreateTestWindowInShellWithId(0)); |
| std::unique_ptr<Window> window1(CreateTestWindowInShellWithId(1)); |
| std::unique_ptr<Window> window2(CreateTestWindowInShellWithId(2)); |
| |
| wm::ActivateWindow(window0.get()); |
| |
| // Simulate pressing and releasing Alt-tab. |
| EXPECT_TRUE(wm::IsActiveWindow(window0.get())); |
| controller->HandleCycleWindow(WindowCycleController::FORWARD); |
| |
| // Window lists should return the topmost window in front. |
| ASSERT_TRUE(controller->window_cycle_list()); |
| ASSERT_EQ(3u, GetWindows(controller).size()); |
| EXPECT_EQ(window0.get(), GetWindows(controller)[0]); |
| EXPECT_EQ(window2.get(), GetWindows(controller)[1]); |
| EXPECT_EQ(window1.get(), GetWindows(controller)[2]); |
| |
| // Cycling through then stopping the cycling will activate a window. |
| controller->HandleCycleWindow(WindowCycleController::FORWARD); |
| controller->CompleteCycling(); |
| EXPECT_TRUE(wm::IsActiveWindow(window1.get())); |
| |
| // Cycling alone (without CompleteCycling()) doesn't activate. |
| controller->HandleCycleWindow(WindowCycleController::FORWARD); |
| EXPECT_FALSE(wm::IsActiveWindow(window0.get())); |
| |
| // Showing the Alt+Tab UI does however deactivate the erstwhile active window. |
| EXPECT_FALSE(wm::IsActiveWindow(window1.get())); |
| |
| controller->CompleteCycling(); |
| } |
| |
| // Tests that beginning window selection hides the app list. |
| TEST_F(WindowCycleControllerTest, SelectingHidesAppList) { |
| WindowCycleController* controller = Shell::Get()->window_cycle_controller(); |
| |
| std::unique_ptr<aura::Window> window0(CreateTestWindowInShellWithId(0)); |
| std::unique_ptr<aura::Window> window1(CreateTestWindowInShellWithId(1)); |
| GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplay().id()); |
| GetAppListTestHelper()->CheckVisibility(true); |
| controller->HandleCycleWindow(WindowCycleController::FORWARD); |
| GetAppListTestHelper()->WaitUntilIdle(); |
| GetAppListTestHelper()->CheckVisibility(false); |
| |
| // Make sure that dismissing the app list this way doesn't pass activation |
| // to a different window. |
| EXPECT_FALSE(wm::IsActiveWindow(window0.get())); |
| EXPECT_FALSE(wm::IsActiveWindow(window1.get())); |
| |
| controller->CompleteCycling(); |
| } |
| |
| // Tests that cycling through windows doesn't change their minimized state. |
| TEST_F(WindowCycleControllerTest, CyclePreservesMinimization) { |
| WindowCycleController* controller = Shell::Get()->window_cycle_controller(); |
| |
| std::unique_ptr<aura::Window> window0(CreateTestWindowInShellWithId(0)); |
| std::unique_ptr<aura::Window> window1(CreateTestWindowInShellWithId(1)); |
| wm::ActivateWindow(window1.get()); |
| wm::GetWindowState(window1.get())->Minimize(); |
| wm::ActivateWindow(window0.get()); |
| EXPECT_TRUE(IsWindowMinimized(window1.get())); |
| |
| // On window 2. |
| controller->HandleCycleWindow(WindowCycleController::FORWARD); |
| EXPECT_TRUE(IsWindowMinimized(window1.get())); |
| |
| // Back on window 1. |
| controller->HandleCycleWindow(WindowCycleController::FORWARD); |
| EXPECT_TRUE(IsWindowMinimized(window1.get())); |
| |
| controller->CompleteCycling(); |
| |
| EXPECT_TRUE(IsWindowMinimized(window1.get())); |
| } |
| |
| // Tests that the tab key events are not sent to the window. |
| TEST_F(WindowCycleControllerTest, TabKeyNotLeaked) { |
| std::unique_ptr<Window> w0(CreateTestWindowInShellWithId(0)); |
| std::unique_ptr<Window> w1(CreateTestWindowInShellWithId(1)); |
| EventCounter event_count; |
| w0->AddPreTargetHandler(&event_count); |
| w1->AddPreTargetHandler(&event_count); |
| ui::test::EventGenerator* generator = GetEventGenerator(); |
| wm::GetWindowState(w0.get())->Activate(); |
| generator->PressKey(ui::VKEY_MENU, ui::EF_NONE); |
| EXPECT_EQ(1, event_count.GetKeyEventCountAndReset()); |
| generator->PressKey(ui::VKEY_TAB, ui::EF_ALT_DOWN); |
| EXPECT_EQ(0, event_count.GetKeyEventCountAndReset()); |
| generator->ReleaseKey(ui::VKEY_TAB, ui::EF_ALT_DOWN); |
| EXPECT_EQ(0, event_count.GetKeyEventCountAndReset()); |
| generator->ReleaseKey(ui::VKEY_MENU, ui::EF_NONE); |
| EXPECT_TRUE(wm::GetWindowState(w1.get())->IsActive()); |
| EXPECT_EQ(0, event_count.GetKeyEventCountAndReset()); |
| } |
| |
| // While the UI is active, mouse events are captured. |
| TEST_F(WindowCycleControllerTest, MouseEventsCaptured) { |
| // This delegate allows the window to receive mouse events. |
| aura::test::TestWindowDelegate delegate; |
| std::unique_ptr<Window> w0(CreateTestWindowInShellWithDelegate( |
| &delegate, 0, gfx::Rect(0, 0, 100, 100))); |
| std::unique_ptr<Window> w1(CreateTestWindowInShellWithId(1)); |
| EventCounter event_count; |
| w0->AddPreTargetHandler(&event_count); |
| w1->SetTargetHandler(&event_count); |
| ui::test::EventGenerator* generator = GetEventGenerator(); |
| wm::ActivateWindow(w0.get()); |
| |
| // Events get through. |
| generator->MoveMouseToCenterOf(w0.get()); |
| generator->ClickLeftButton(); |
| EXPECT_LT(0, event_count.GetMouseEventCountAndReset()); |
| |
| // Start cycling. |
| WindowCycleController* controller = Shell::Get()->window_cycle_controller(); |
| controller->HandleCycleWindow(WindowCycleController::FORWARD); |
| |
| // Most mouse events don't get through. |
| generator->PressLeftButton(); |
| EXPECT_EQ(0, event_count.GetMouseEventCountAndReset()); |
| |
| // Although releases do. |
| generator->ReleaseLeftButton(); |
| EXPECT_LT(0, event_count.GetMouseEventCountAndReset()); |
| |
| // Stop cycling: once again, events get through. |
| controller->CompleteCycling(); |
| generator->ClickLeftButton(); |
| EXPECT_LT(0, event_count.GetMouseEventCountAndReset()); |
| } |
| |
| // Tests that we can cycle past fullscreen windows: https://crbug.com/622396. |
| // Fullscreen windows are special in that they are allowed to handle alt+tab |
| // keypresses, which means the window cycle event filter should not handle |
| // the tab press else it prevents cycling past that window. |
| TEST_F(WindowCycleControllerTest, TabPastFullscreenWindow) { |
| std::unique_ptr<Window> w0(CreateTestWindowInShellWithId(0)); |
| std::unique_ptr<Window> w1(CreateTestWindowInShellWithId(1)); |
| wm::WMEvent maximize_event(wm::WM_EVENT_FULLSCREEN); |
| |
| // To make this test work with or without the new alt+tab selector we make |
| // both the initial window and the second window fullscreen. |
| wm::GetWindowState(w0.get())->OnWMEvent(&maximize_event); |
| wm::GetWindowState(w1.get())->Activate(); |
| wm::GetWindowState(w1.get())->OnWMEvent(&maximize_event); |
| EXPECT_TRUE(wm::GetWindowState(w0.get())->IsFullscreen()); |
| EXPECT_TRUE(wm::GetWindowState(w1.get())->IsFullscreen()); |
| wm::GetWindowState(w0.get())->Activate(); |
| EXPECT_TRUE(wm::GetWindowState(w0.get())->IsActive()); |
| |
| ui::test::EventGenerator* generator = GetEventGenerator(); |
| generator->PressKey(ui::VKEY_MENU, ui::EF_NONE); |
| |
| generator->PressKey(ui::VKEY_TAB, ui::EF_ALT_DOWN); |
| generator->ReleaseKey(ui::VKEY_TAB, ui::EF_ALT_DOWN); |
| |
| // Because w0 and w1 are full-screen, the event should be passed to the |
| // browser window to handle it (which if the browser doesn't handle it will |
| // pass on the alt+tab to continue cycling). To make this test work with or |
| // without the new alt+tab selector we check for the event on either |
| // fullscreen window. |
| EventCounter event_count; |
| w0->AddPreTargetHandler(&event_count); |
| w1->AddPreTargetHandler(&event_count); |
| generator->PressKey(ui::VKEY_TAB, ui::EF_ALT_DOWN); |
| EXPECT_EQ(1, event_count.GetKeyEventCountAndReset()); |
| } |
| |
| // Tests that the Alt+Tab UI's position isn't affected by the origin of the |
| // display it's on. See crbug.com/675718 |
| TEST_F(WindowCycleControllerTest, MultiDisplayPositioning) { |
| int64_t primary_id = GetPrimaryDisplay().id(); |
| display::DisplayIdList list = |
| display::test::CreateDisplayIdListN(2, primary_id, primary_id + 1); |
| |
| auto placements = { |
| display::DisplayPlacement::BOTTOM, display::DisplayPlacement::TOP, |
| display::DisplayPlacement::LEFT, display::DisplayPlacement::RIGHT, |
| }; |
| |
| gfx::Rect expected_bounds; |
| for (auto placement : placements) { |
| SCOPED_TRACE(placement); |
| |
| display::DisplayLayoutBuilder builder(primary_id); |
| builder.AddDisplayPlacement(list[1], primary_id, placement, 0); |
| display_manager()->layout_store()->RegisterLayoutForDisplayIdList( |
| list, builder.Build()); |
| |
| // Use two displays. |
| UpdateDisplay("500x500,600x600"); |
| |
| gfx::Rect second_display_bounds = |
| display_manager()->GetDisplayAt(1).bounds(); |
| std::unique_ptr<Window> window0( |
| CreateTestWindowInShellWithBounds(second_display_bounds)); |
| // Activate this window so that the secondary display becomes the one where |
| // the Alt+Tab UI is shown. |
| wm::ActivateWindow(window0.get()); |
| std::unique_ptr<Window> window1( |
| CreateTestWindowInShellWithBounds(second_display_bounds)); |
| |
| WindowCycleController* controller = Shell::Get()->window_cycle_controller(); |
| controller->HandleCycleWindow(WindowCycleController::FORWARD); |
| |
| const gfx::Rect bounds = |
| GetWindowCycleListWidget()->GetWindowBoundsInScreen(); |
| EXPECT_TRUE(second_display_bounds.Contains(bounds)); |
| EXPECT_FALSE( |
| display_manager()->GetDisplayAt(0).bounds().Intersects(bounds)); |
| const gfx::Rect display_relative_bounds = |
| bounds - second_display_bounds.OffsetFromOrigin(); |
| // Base case sets the expectation for other cases. |
| if (expected_bounds.IsEmpty()) |
| expected_bounds = display_relative_bounds; |
| else |
| EXPECT_EQ(expected_bounds, display_relative_bounds); |
| controller->CompleteCycling(); |
| } |
| } |
| |
| } // namespace ash |