| // Copyright 2019 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/overview/overview_highlight_controller.h" |
| |
| #include "ash/public/cpp/ash_features.h" |
| #include "ash/shell.h" |
| #include "ash/test/ash_test_base.h" |
| #include "ash/wm/desks/close_desk_button.h" |
| #include "ash/wm/desks/desk.h" |
| #include "ash/wm/desks/desk_mini_view.h" |
| #include "ash/wm/desks/desk_name_view.h" |
| #include "ash/wm/desks/desks_bar_view.h" |
| #include "ash/wm/desks/desks_test_util.h" |
| #include "ash/wm/desks/expanded_state_new_desk_button.h" |
| #include "ash/wm/desks/new_desk_button.h" |
| #include "ash/wm/desks/zero_state_button.h" |
| #include "ash/wm/overview/overview_constants.h" |
| #include "ash/wm/overview/overview_controller.h" |
| #include "ash/wm/overview/overview_grid.h" |
| #include "ash/wm/overview/overview_item.h" |
| #include "ash/wm/overview/overview_item_view.h" |
| #include "ash/wm/overview/overview_test_util.h" |
| #include "ash/wm/overview/scoped_overview_transform_window.h" |
| #include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h" |
| #include "ash/wm/window_util.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "ui/aura/window.h" |
| #include "ui/display/manager/display_manager.h" |
| #include "ui/display/test/display_manager_test_api.h" |
| #include "ui/events/keycodes/keyboard_codes_posix.h" |
| #include "ui/events/test/event_generator.h" |
| #include "ui/views/widget/widget.h" |
| |
| namespace ash { |
| |
| class OverviewHighlightControllerTest : public AshTestBase { |
| public: |
| OverviewHighlightControllerTest() = default; |
| ~OverviewHighlightControllerTest() override = default; |
| |
| // AshTestBase: |
| void SetUp() override { |
| AshTestBase::SetUp(); |
| ScopedOverviewTransformWindow::SetImmediateCloseForTests(true); |
| } |
| |
| OverviewHighlightController* GetHighlightController() { |
| return GetOverviewSession()->highlight_controller(); |
| } |
| |
| // Press the key repeatedly until a window is highlighted, i.e. ignoring any |
| // desk items. |
| void SendKeyUntilOverviewItemIsHighlighted(ui::KeyboardCode key) { |
| do { |
| SendKey(key); |
| } while (!GetOverviewHighlightedWindow()); |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(OverviewHighlightControllerTest); |
| }; |
| |
| // Tests traversing some windows in overview mode with the tab key. |
| TEST_F(OverviewHighlightControllerTest, BasicTabKeyNavigation) { |
| std::unique_ptr<aura::Window> window2(CreateTestWindow()); |
| std::unique_ptr<aura::Window> window1(CreateTestWindow()); |
| |
| ToggleOverview(); |
| const std::vector<std::unique_ptr<OverviewItem>>& overview_windows = |
| GetOverviewItemsForRoot(0); |
| SendKeyUntilOverviewItemIsHighlighted(ui::VKEY_TAB); |
| EXPECT_EQ(overview_windows[0]->GetWindow(), GetOverviewHighlightedWindow()); |
| SendKeyUntilOverviewItemIsHighlighted(ui::VKEY_TAB); |
| EXPECT_EQ(overview_windows[1]->GetWindow(), GetOverviewHighlightedWindow()); |
| SendKeyUntilOverviewItemIsHighlighted(ui::VKEY_TAB); |
| EXPECT_EQ(overview_windows[0]->GetWindow(), GetOverviewHighlightedWindow()); |
| SendKeyUntilOverviewItemIsHighlighted(ui::VKEY_RIGHT); |
| EXPECT_EQ(overview_windows[1]->GetWindow(), GetOverviewHighlightedWindow()); |
| SendKeyUntilOverviewItemIsHighlighted(ui::VKEY_LEFT); |
| EXPECT_EQ(overview_windows[0]->GetWindow(), GetOverviewHighlightedWindow()); |
| } |
| |
| // Same as above but for tablet mode. Regression test for crbug.com/1036140. |
| TEST_F(OverviewHighlightControllerTest, BasicTabKeyNavigationTablet) { |
| std::unique_ptr<aura::Window> window1(CreateTestWindow()); |
| std::unique_ptr<aura::Window> window2(CreateTestWindow()); |
| std::unique_ptr<aura::Window> window3(CreateTestWindow()); |
| |
| TabletModeControllerTestApi().EnterTabletMode(); |
| ToggleOverview(); |
| const std::vector<std::unique_ptr<OverviewItem>>& overview_windows = |
| GetOverviewItemsForRoot(0); |
| SendKeyUntilOverviewItemIsHighlighted(ui::VKEY_TAB); |
| EXPECT_EQ(overview_windows[0]->GetWindow(), GetOverviewHighlightedWindow()); |
| SendKeyUntilOverviewItemIsHighlighted(ui::VKEY_TAB); |
| EXPECT_EQ(overview_windows[1]->GetWindow(), GetOverviewHighlightedWindow()); |
| SendKeyUntilOverviewItemIsHighlighted(ui::VKEY_RIGHT); |
| EXPECT_EQ(overview_windows[2]->GetWindow(), GetOverviewHighlightedWindow()); |
| SendKeyUntilOverviewItemIsHighlighted(ui::VKEY_LEFT); |
| EXPECT_EQ(overview_windows[1]->GetWindow(), GetOverviewHighlightedWindow()); |
| } |
| |
| // Tests that pressing Ctrl+W while a window is selected in overview closes it. |
| TEST_F(OverviewHighlightControllerTest, CloseWindowWithKey) { |
| std::unique_ptr<views::Widget> widget(CreateTestWidget()); |
| ToggleOverview(); |
| |
| SendKeyUntilOverviewItemIsHighlighted(ui::VKEY_RIGHT); |
| EXPECT_EQ(widget->GetNativeWindow(), GetOverviewHighlightedWindow()); |
| SendKey(ui::VKEY_W, ui::EF_CONTROL_DOWN); |
| EXPECT_TRUE(widget->IsClosed()); |
| } |
| |
| // Tests traversing some windows in overview mode with the arrow keys in every |
| // possible direction. |
| TEST_F(OverviewHighlightControllerTest, BasicArrowKeyNavigation) { |
| const size_t test_windows = 9; |
| UpdateDisplay("800x600"); |
| std::vector<std::unique_ptr<aura::Window>> windows; |
| for (size_t i = test_windows; i > 0; --i) { |
| windows.push_back( |
| std::unique_ptr<aura::Window>(CreateTestWindowInShellWithId(i))); |
| } |
| |
| ui::KeyboardCode arrow_keys[] = {ui::VKEY_RIGHT, ui::VKEY_DOWN, ui::VKEY_LEFT, |
| ui::VKEY_UP}; |
| // The rows contain variable number of items making vertical navigation not |
| // feasible. [Down] is equivalent to [Right] and [Up] is equivalent to [Left]. |
| int index_path_for_direction[][test_windows + 1] = { |
| {1, 2, 3, 4, 5, 6, 7, 8, 9, 1}, // Right |
| {1, 2, 3, 4, 5, 6, 7, 8, 9, 1}, // Down (same as Right) |
| {9, 8, 7, 6, 5, 4, 3, 2, 1, 9}, // Left |
| {9, 8, 7, 6, 5, 4, 3, 2, 1, 9} // Up (same as Left) |
| }; |
| |
| for (size_t key_index = 0; key_index < base::size(arrow_keys); ++key_index) { |
| ToggleOverview(); |
| const std::vector<std::unique_ptr<OverviewItem>>& overview_windows = |
| GetOverviewItemsForRoot(0); |
| for (size_t i = 0; i < test_windows + 1; ++i) { |
| SendKeyUntilOverviewItemIsHighlighted(arrow_keys[key_index]); |
| // TODO(flackr): Add a more readable error message by constructing a |
| // string from the window IDs. |
| const int index = index_path_for_direction[key_index][i]; |
| EXPECT_EQ(GetOverviewHighlightedWindow()->id(), |
| overview_windows[index - 1]->GetWindow()->id()); |
| } |
| ToggleOverview(); |
| } |
| } |
| |
| // Tests that when an item is removed while highlighted, the highlight |
| // disappears, and when we tab again we pick up where we left off. |
| TEST_F(OverviewHighlightControllerTest, ItemClosed) { |
| auto widget1 = CreateTestWidget(); |
| auto widget2 = CreateTestWidget(); |
| auto widget3 = CreateTestWidget(); |
| ToggleOverview(); |
| |
| SendKeyUntilOverviewItemIsHighlighted(ui::VKEY_TAB); |
| SendKeyUntilOverviewItemIsHighlighted(ui::VKEY_TAB); |
| EXPECT_EQ(widget2->GetNativeWindow(), GetOverviewHighlightedWindow()); |
| |
| // Remove |widget2| by closing it with ctrl + W. Test that the highlight |
| // becomes invisible (neither widget is highlighted). |
| SendKey(ui::VKEY_W, ui::EF_CONTROL_DOWN); |
| EXPECT_TRUE(widget2->IsClosed()); |
| widget2.reset(); |
| EXPECT_FALSE(GetOverviewHighlightedWindow()); |
| |
| // Tests that on pressing tab, the highlight becomes visible and we highlight |
| // the window that comes after the deleted one. |
| SendKeyUntilOverviewItemIsHighlighted(ui::VKEY_TAB); |
| EXPECT_EQ(widget1->GetNativeWindow(), GetOverviewHighlightedWindow()); |
| } |
| |
| // Tests basic selection across multiple monitors. |
| TEST_F(OverviewHighlightControllerTest, BasicMultiMonitorArrowKeyNavigation) { |
| UpdateDisplay("400x400,400x400"); |
| const gfx::Rect bounds1(100, 100); |
| const gfx::Rect bounds2(450, 0, 100, 100); |
| std::unique_ptr<aura::Window> window4(CreateTestWindow(bounds2)); |
| std::unique_ptr<aura::Window> window3(CreateTestWindow(bounds2)); |
| std::unique_ptr<aura::Window> window2(CreateTestWindow(bounds1)); |
| std::unique_ptr<aura::Window> window1(CreateTestWindow(bounds1)); |
| |
| ToggleOverview(); |
| |
| const std::vector<std::unique_ptr<OverviewItem>>& overview_root1 = |
| GetOverviewItemsForRoot(0); |
| const std::vector<std::unique_ptr<OverviewItem>>& overview_root2 = |
| GetOverviewItemsForRoot(1); |
| SendKeyUntilOverviewItemIsHighlighted(ui::VKEY_RIGHT); |
| EXPECT_EQ(GetOverviewHighlightedWindow(), overview_root1[0]->GetWindow()); |
| SendKeyUntilOverviewItemIsHighlighted(ui::VKEY_RIGHT); |
| EXPECT_EQ(GetOverviewHighlightedWindow(), overview_root1[1]->GetWindow()); |
| SendKeyUntilOverviewItemIsHighlighted(ui::VKEY_RIGHT); |
| EXPECT_EQ(GetOverviewHighlightedWindow(), overview_root2[0]->GetWindow()); |
| SendKeyUntilOverviewItemIsHighlighted(ui::VKEY_RIGHT); |
| EXPECT_EQ(GetOverviewHighlightedWindow(), overview_root2[1]->GetWindow()); |
| } |
| |
| // Tests first monitor when display order doesn't match left to right screen |
| // positions. |
| TEST_F(OverviewHighlightControllerTest, MultiMonitorReversedOrder) { |
| UpdateDisplay("400x400,400x400"); |
| Shell::Get()->display_manager()->SetLayoutForCurrentDisplays( |
| display::test::CreateDisplayLayout(display_manager(), |
| display::DisplayPlacement::LEFT, 0)); |
| aura::Window::Windows root_windows = Shell::GetAllRootWindows(); |
| std::unique_ptr<aura::Window> window2(CreateTestWindow(gfx::Rect(100, 100))); |
| std::unique_ptr<aura::Window> window1( |
| CreateTestWindow(gfx::Rect(-350, 0, 100, 100))); |
| EXPECT_EQ(root_windows[1], window1->GetRootWindow()); |
| EXPECT_EQ(root_windows[0], window2->GetRootWindow()); |
| |
| ToggleOverview(); |
| |
| // Coming from the left to right, we should select window1 first being on the |
| // display to the left. |
| SendKeyUntilOverviewItemIsHighlighted(ui::VKEY_RIGHT); |
| EXPECT_EQ(GetOverviewHighlightedWindow(), window1.get()); |
| |
| // Exit and reenter overview. |
| ToggleOverview(); |
| ToggleOverview(); |
| |
| // Coming from right to left, we should select window2 first being on the |
| // display on the right. |
| SendKeyUntilOverviewItemIsHighlighted(ui::VKEY_LEFT); |
| EXPECT_EQ(GetOverviewHighlightedWindow(), window2.get()); |
| } |
| |
| // Tests three monitors where the grid becomes empty on one of the monitors. |
| TEST_F(OverviewHighlightControllerTest, ThreeMonitor) { |
| UpdateDisplay("400x400,400x400,400x400"); |
| aura::Window::Windows root_windows = Shell::GetAllRootWindows(); |
| std::unique_ptr<aura::Window> window3( |
| CreateTestWindow(gfx::Rect(800, 0, 100, 100))); |
| std::unique_ptr<aura::Window> window2( |
| CreateTestWindow(gfx::Rect(400, 0, 100, 100))); |
| std::unique_ptr<aura::Window> window1(CreateTestWindow(gfx::Rect(100, 100))); |
| EXPECT_EQ(root_windows[0], window1->GetRootWindow()); |
| EXPECT_EQ(root_windows[1], window2->GetRootWindow()); |
| EXPECT_EQ(root_windows[2], window3->GetRootWindow()); |
| |
| ToggleOverview(); |
| |
| SendKeyUntilOverviewItemIsHighlighted(ui::VKEY_RIGHT); |
| SendKeyUntilOverviewItemIsHighlighted(ui::VKEY_RIGHT); |
| SendKeyUntilOverviewItemIsHighlighted(ui::VKEY_RIGHT); |
| EXPECT_EQ(window3.get(), GetOverviewHighlightedWindow()); |
| |
| // If the selected window is closed, then nothing should be selected. |
| window3.reset(); |
| EXPECT_EQ(nullptr, GetOverviewHighlightedWindow()); |
| ToggleOverview(); |
| |
| window3 = CreateTestWindow(gfx::Rect(800, 0, 100, 100)); |
| ToggleOverview(); |
| SendKeyUntilOverviewItemIsHighlighted(ui::VKEY_RIGHT); |
| SendKeyUntilOverviewItemIsHighlighted(ui::VKEY_RIGHT); |
| SendKeyUntilOverviewItemIsHighlighted(ui::VKEY_RIGHT); |
| |
| // If the window on the second display is removed, the selected window should |
| // remain window3. |
| EXPECT_EQ(window3.get(), GetOverviewHighlightedWindow()); |
| window2.reset(); |
| EXPECT_EQ(window3.get(), GetOverviewHighlightedWindow()); |
| } |
| |
| // Tests selecting a window in overview mode with the return key. |
| TEST_F(OverviewHighlightControllerTest, HighlightOverviewWindowWithReturnKey) { |
| std::unique_ptr<aura::Window> window2(CreateTestWindow()); |
| std::unique_ptr<aura::Window> window1(CreateTestWindow()); |
| ToggleOverview(); |
| |
| // Pressing the return key on an item that is not highlighted should not do |
| // anything. |
| SendKey(ui::VKEY_RETURN); |
| EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession()); |
| |
| // Highlight the first window. |
| ASSERT_TRUE(HighlightOverviewWindow(window1.get())); |
| SendKey(ui::VKEY_RETURN); |
| EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession()); |
| EXPECT_TRUE(wm::IsActiveWindow(window1.get())); |
| |
| // Highlight the second window. |
| ToggleOverview(); |
| ASSERT_TRUE(HighlightOverviewWindow(window2.get())); |
| SendKey(ui::VKEY_RETURN); |
| EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession()); |
| EXPECT_TRUE(wm::IsActiveWindow(window2.get())); |
| } |
| |
| // Tests that the location of the overview highlight is as expected while |
| // dragging an overview item. |
| TEST_F(OverviewHighlightControllerTest, HighlightLocationWhileDragging) { |
| std::unique_ptr<aura::Window> window1(CreateTestWindow(gfx::Rect(200, 200))); |
| std::unique_ptr<aura::Window> window2(CreateTestWindow(gfx::Rect(200, 200))); |
| std::unique_ptr<aura::Window> window3(CreateTestWindow(gfx::Rect(200, 200))); |
| |
| ToggleOverview(); |
| |
| // Tab once to show the highlight. |
| SendKeyUntilOverviewItemIsHighlighted(ui::VKEY_TAB); |
| EXPECT_EQ(window3.get(), GetOverviewHighlightedWindow()); |
| OverviewItem* item = GetOverviewItemForWindow(window3.get()); |
| |
| // Tests that while dragging an item, tabbing does not change which item the |
| // highlight is hovered over, but the highlight is hidden. Drag the item in a |
| // way which does not enter splitview, or close overview. |
| const gfx::PointF start_point = item->target_bounds().CenterPoint(); |
| const gfx::PointF end_point(20.f, 20.f); |
| GetOverviewSession()->InitiateDrag(item, start_point, |
| /*is_touch_dragging=*/true); |
| GetOverviewSession()->Drag(item, end_point); |
| SendKeyUntilOverviewItemIsHighlighted(ui::VKEY_TAB); |
| EXPECT_EQ(window3.get(), GetOverviewHighlightedWindow()); |
| EXPECT_FALSE(GetHighlightController()->IsFocusHighlightVisible()); |
| |
| // Tests that on releasing the item, the highlighted window remains the same. |
| GetOverviewSession()->Drag(item, start_point); |
| GetOverviewSession()->CompleteDrag(item, start_point); |
| EXPECT_EQ(window3.get(), GetOverviewHighlightedWindow()); |
| EXPECT_TRUE(GetHighlightController()->IsFocusHighlightVisible()); |
| |
| // Tests that on tabbing after releasing, the highlighted window is the next |
| // one. |
| SendKeyUntilOverviewItemIsHighlighted(ui::VKEY_TAB); |
| EXPECT_EQ(window2.get(), GetOverviewHighlightedWindow()); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // DesksOverviewHighlightControllerTest: |
| |
| class DesksOverviewHighlightControllerTest |
| : public OverviewHighlightControllerTest { |
| public: |
| DesksOverviewHighlightControllerTest() = default; |
| ~DesksOverviewHighlightControllerTest() override = default; |
| |
| // OverviewHighlightControllerTest: |
| void SetUp() override { |
| OverviewHighlightControllerTest::SetUp(); |
| |
| // All tests in this suite require the desks bar to be visible in overview, |
| // which requires at least two desks. |
| auto* desk_controller = DesksController::Get(); |
| desk_controller->NewDesk(DesksCreationRemovalSource::kButton); |
| ASSERT_EQ(2u, desk_controller->desks().size()); |
| } |
| |
| OverviewHighlightController::OverviewHighlightableView* GetHighlightedView() { |
| return OverviewHighlightController::TestApi(GetHighlightController()) |
| .GetHighlightView(); |
| } |
| |
| const DesksBarView* GetDesksBarViewForRoot(aura::Window* root_window) { |
| OverviewGrid* grid = |
| GetOverviewSession()->GetGridWithRootWindow(root_window); |
| const DesksBarView* bar_view = grid->desks_bar_view(); |
| DCHECK(bar_view->IsZeroState() ^ grid->IsDesksBarViewActive()); |
| return bar_view; |
| } |
| |
| OverviewHighlightController::OverviewHighlightableView* GetNewDeskButton( |
| const DesksBarView* bar_view) { |
| DCHECK(bar_view); |
| if (features::IsBentoEnabled()) |
| return bar_view->expanded_state_new_desk_button()->new_desk_button(); |
| |
| return bar_view->new_desk_button(); |
| } |
| |
| protected: |
| static void CheckDeskBarViewSize(const DesksBarView* view, |
| const std::string& scope) { |
| SCOPED_TRACE(scope); |
| EXPECT_EQ(view->bounds().height(), |
| view->GetWidget()->GetWindowBoundsInScreen().height()); |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(DesksOverviewHighlightControllerTest); |
| }; |
| |
| // Tests that we can tab through the desk mini views, new desk button and |
| // overview items in the correct order. Overview items will have the overview |
| // highlight shown when highlighted, but desks items will not. |
| TEST_F(DesksOverviewHighlightControllerTest, TabbingBasic) { |
| std::unique_ptr<aura::Window> window1(CreateTestWindow(gfx::Rect(200, 200))); |
| std::unique_ptr<aura::Window> window2(CreateTestWindow(gfx::Rect(200, 200))); |
| |
| ToggleOverview(); |
| const auto* desk_bar_view = |
| GetDesksBarViewForRoot(Shell::GetPrimaryRootWindow()); |
| |
| CheckDeskBarViewSize(desk_bar_view, "initial"); |
| EXPECT_EQ(2u, desk_bar_view->mini_views().size()); |
| |
| // Tests that the first highlighted item is the first mini view. |
| SendKey(ui::VKEY_TAB); |
| EXPECT_EQ(desk_bar_view->mini_views()[0], GetHighlightedView()); |
| CheckDeskBarViewSize(desk_bar_view, "first mini view"); |
| |
| // Test that one more tab highlights the desks name view. |
| SendKey(ui::VKEY_TAB); |
| EXPECT_EQ(desk_bar_view->mini_views()[0]->desk_name_view(), |
| GetHighlightedView()); |
| |
| // Tests that after tabbing through the mini views, we highlight the new desk |
| // button. |
| SendKey(ui::VKEY_TAB); |
| SendKey(ui::VKEY_TAB); |
| SendKey(ui::VKEY_TAB); |
| EXPECT_EQ(GetNewDeskButton(desk_bar_view), GetHighlightedView()); |
| CheckDeskBarViewSize(desk_bar_view, "new desk button"); |
| |
| // Tests that the overview item gets highlighted after the new desk button. |
| SendKey(ui::VKEY_TAB); |
| auto* item2 = GetOverviewItemForWindow(window2.get()); |
| EXPECT_EQ(item2->overview_item_view(), GetHighlightedView()); |
| CheckDeskBarViewSize(desk_bar_view, "overview item"); |
| |
| // Tests that after tabbing through the overview items, we go back to the |
| // first mini view. |
| SendKey(ui::VKEY_TAB); |
| SendKey(ui::VKEY_TAB); |
| EXPECT_EQ(desk_bar_view->mini_views()[0], GetHighlightedView()); |
| CheckDeskBarViewSize(desk_bar_view, "go back to first"); |
| } |
| |
| // Tests that we can reverse tab through the desk mini views, new desk button |
| // and overview items in the correct order. |
| TEST_F(DesksOverviewHighlightControllerTest, TabbingReverse) { |
| std::unique_ptr<aura::Window> window1(CreateTestWindow(gfx::Rect(200, 200))); |
| std::unique_ptr<aura::Window> window2(CreateTestWindow(gfx::Rect(200, 200))); |
| |
| ToggleOverview(); |
| const auto* desk_bar_view = |
| GetDesksBarViewForRoot(Shell::GetPrimaryRootWindow()); |
| EXPECT_EQ(2u, desk_bar_view->mini_views().size()); |
| |
| // Tests that the first highlighted item when reversing is the last overview |
| // item. |
| SendKey(ui::VKEY_TAB, ui::EF_SHIFT_DOWN); |
| auto* item1 = GetOverviewItemForWindow(window1.get()); |
| EXPECT_EQ(item1->overview_item_view(), GetHighlightedView()); |
| |
| // Tests that after reverse tabbing through the overview items, we highlight |
| // the new desk button. |
| SendKey(ui::VKEY_TAB, ui::EF_SHIFT_DOWN); |
| SendKey(ui::VKEY_TAB, ui::EF_SHIFT_DOWN); |
| EXPECT_EQ(GetNewDeskButton(desk_bar_view), GetHighlightedView()); |
| |
| // Tests that after the new desk button comes the mini views and their desk |
| // name views in reverse order. |
| SendKey(ui::VKEY_TAB, ui::EF_SHIFT_DOWN); |
| EXPECT_EQ(desk_bar_view->mini_views()[1]->desk_name_view(), |
| GetHighlightedView()); |
| SendKey(ui::VKEY_TAB, ui::EF_SHIFT_DOWN); |
| EXPECT_EQ(desk_bar_view->mini_views()[1], GetHighlightedView()); |
| SendKey(ui::VKEY_TAB, ui::EF_SHIFT_DOWN); |
| EXPECT_EQ(desk_bar_view->mini_views()[0]->desk_name_view(), |
| GetHighlightedView()); |
| SendKey(ui::VKEY_TAB, ui::EF_SHIFT_DOWN); |
| EXPECT_EQ(desk_bar_view->mini_views()[0], GetHighlightedView()); |
| |
| // Tests that we return to the last overview item after reverse tabbing from |
| // the first mini view. |
| SendKey(ui::VKEY_TAB, ui::EF_SHIFT_DOWN); |
| EXPECT_EQ(item1->overview_item_view(), GetHighlightedView()); |
| } |
| |
| // Tests that tabbing with desk items and multiple displays works as expected. |
| TEST_F(DesksOverviewHighlightControllerTest, TabbingMultiDisplay) { |
| UpdateDisplay("600x400,600x400,600x400"); |
| std::vector<aura::Window*> roots = Shell::GetAllRootWindows(); |
| ASSERT_EQ(3u, roots.size()); |
| |
| // Create two windows on the first display, and one each on the second and |
| // third displays. |
| std::unique_ptr<aura::Window> window1(CreateTestWindow(gfx::Rect(200, 200))); |
| std::unique_ptr<aura::Window> window2(CreateTestWindow(gfx::Rect(200, 200))); |
| std::unique_ptr<aura::Window> window3( |
| CreateTestWindow(gfx::Rect(600, 0, 200, 200))); |
| std::unique_ptr<aura::Window> window4( |
| CreateTestWindow(gfx::Rect(1200, 0, 200, 200))); |
| ASSERT_EQ(roots[0], window1->GetRootWindow()); |
| ASSERT_EQ(roots[0], window2->GetRootWindow()); |
| ASSERT_EQ(roots[1], window3->GetRootWindow()); |
| ASSERT_EQ(roots[2], window4->GetRootWindow()); |
| |
| ToggleOverview(); |
| const auto* desk_bar_view1 = GetDesksBarViewForRoot(roots[0]); |
| EXPECT_EQ(2u, desk_bar_view1->mini_views().size()); |
| |
| // Tests that tabbing initially will go through the desk mini views and their |
| // desk name views, then the new desk button on the first display. |
| SendKey(ui::VKEY_TAB); |
| EXPECT_EQ(desk_bar_view1->mini_views()[0], GetHighlightedView()); |
| SendKey(ui::VKEY_TAB); |
| EXPECT_EQ(desk_bar_view1->mini_views()[0]->desk_name_view(), |
| GetHighlightedView()); |
| SendKey(ui::VKEY_TAB); |
| EXPECT_EQ(desk_bar_view1->mini_views()[1], GetHighlightedView()); |
| SendKey(ui::VKEY_TAB); |
| EXPECT_EQ(desk_bar_view1->mini_views()[1]->desk_name_view(), |
| GetHighlightedView()); |
| SendKey(ui::VKEY_TAB); |
| EXPECT_EQ(GetNewDeskButton(desk_bar_view1), GetHighlightedView()); |
| |
| // Tests that two more tabs, will highlight the two overview items on the |
| // first display. |
| SendKey(ui::VKEY_TAB); |
| auto* item2 = GetOverviewItemForWindow(window2.get()); |
| EXPECT_EQ(item2->overview_item_view(), GetHighlightedView()); |
| SendKey(ui::VKEY_TAB); |
| auto* item1 = GetOverviewItemForWindow(window1.get()); |
| EXPECT_EQ(item1->overview_item_view(), GetHighlightedView()); |
| |
| // Tests that the next tab will bring us to the first mini view on the |
| // second display. |
| SendKey(ui::VKEY_TAB); |
| const auto* desk_bar_view2 = GetDesksBarViewForRoot(roots[1]); |
| EXPECT_EQ(desk_bar_view2->mini_views()[0], GetHighlightedView()); |
| |
| // Tab through all items on the second display. |
| SendKey(ui::VKEY_TAB); |
| SendKey(ui::VKEY_TAB); |
| SendKey(ui::VKEY_TAB); |
| SendKey(ui::VKEY_TAB); |
| EXPECT_EQ(GetNewDeskButton(desk_bar_view2), GetHighlightedView()); |
| SendKey(ui::VKEY_TAB); |
| auto* item3 = GetOverviewItemForWindow(window3.get()); |
| EXPECT_EQ(item3->overview_item_view(), GetHighlightedView()); |
| |
| // Tests that after tabbing through the items on the second display, the |
| // next tab will bring us to the first mini view on the third display. |
| SendKey(ui::VKEY_TAB); |
| const auto* desk_bar_view3 = GetDesksBarViewForRoot(roots[2]); |
| EXPECT_EQ(desk_bar_view3->mini_views()[0], GetHighlightedView()); |
| |
| // Tab through all items on the third display. |
| SendKey(ui::VKEY_TAB); |
| SendKey(ui::VKEY_TAB); |
| SendKey(ui::VKEY_TAB); |
| SendKey(ui::VKEY_TAB); |
| EXPECT_EQ(GetNewDeskButton(desk_bar_view3), GetHighlightedView()); |
| SendKey(ui::VKEY_TAB); |
| auto* item4 = GetOverviewItemForWindow(window4.get()); |
| EXPECT_EQ(item4->overview_item_view(), GetHighlightedView()); |
| |
| // Tests that after tabbing through the items on the third display, the next |
| // tab will bring us to the first mini view on the first display. |
| SendKey(ui::VKEY_TAB); |
| EXPECT_EQ(desk_bar_view1->mini_views()[0], GetHighlightedView()); |
| } |
| |
| TEST_F(DesksOverviewHighlightControllerTest, |
| ActivateCloseHighlightOnNewDeskButton) { |
| // Tests the new desk button in classic desks. |
| if (features::IsBentoEnabled()) |
| return; |
| |
| ToggleOverview(); |
| const auto* desk_bar_view = |
| GetDesksBarViewForRoot(Shell::GetPrimaryRootWindow()); |
| const auto* new_desk_button = desk_bar_view->new_desk_button(); |
| const auto* desks_controller = DesksController::Get(); |
| |
| // Use the keyboard to navigate to the new desk button. |
| SendKey(ui::VKEY_TAB); |
| SendKey(ui::VKEY_TAB); |
| SendKey(ui::VKEY_TAB); |
| SendKey(ui::VKEY_TAB); |
| SendKey(ui::VKEY_TAB); |
| ASSERT_EQ(new_desk_button, GetHighlightedView()); |
| |
| SendKey(ui::VKEY_RETURN); |
| EXPECT_EQ(3u, desks_controller->desks().size()); |
| EXPECT_TRUE(new_desk_button->GetEnabled()); |
| |
| // Tests that pressing the key command to close a highlighted item does |
| // nothing. |
| SendKey(ui::VKEY_W, ui::EF_CONTROL_DOWN); |
| EXPECT_EQ(3u, desks_controller->desks().size()); |
| |
| // Keep adding new desks until we reach the maximum allowed amount. Verify the |
| // amount of desks is indeed the maximum allowed and that the new desk button |
| // is disabled. |
| while (desks_controller->CanCreateDesks()) |
| SendKey(ui::VKEY_RETURN); |
| EXPECT_FALSE(new_desk_button->GetEnabled()); |
| EXPECT_EQ(desks_util::GetMaxNumberOfDesks(), |
| desks_controller->desks().size()); |
| |
| // Tests that after the button is disabled, it is no longer highlighted. |
| EXPECT_FALSE(GetHighlightedView()); |
| } |
| |
| TEST_F(DesksOverviewHighlightControllerTest, ActivateHighlightOnMiniView) { |
| // We are initially on desk 1. |
| const auto* desks_controller = DesksController::Get(); |
| auto& desks = desks_controller->desks(); |
| ASSERT_EQ(desks_controller->active_desk(), desks[0].get()); |
| |
| ToggleOverview(); |
| const auto* desk_bar_view = |
| GetDesksBarViewForRoot(Shell::GetPrimaryRootWindow()); |
| |
| // Use keyboard to navigate to the miniview associated with desk 2. |
| SendKey(ui::VKEY_TAB); |
| SendKey(ui::VKEY_TAB); |
| SendKey(ui::VKEY_TAB); |
| ASSERT_EQ(desk_bar_view->mini_views()[1], GetHighlightedView()); |
| |
| // Tests that after hitting the return key on the highlighted mini view |
| // associated with desk 2, we switch to desk 2. |
| DeskSwitchAnimationWaiter waiter; |
| SendKey(ui::VKEY_RETURN); |
| waiter.Wait(); |
| EXPECT_EQ(desks_controller->active_desk(), desks[1].get()); |
| } |
| |
| TEST_F(DesksOverviewHighlightControllerTest, CloseHighlightOnMiniView) { |
| const auto* desks_controller = DesksController::Get(); |
| ASSERT_EQ(2u, desks_controller->desks().size()); |
| auto* desk1 = desks_controller->desks()[0].get(); |
| auto* desk2 = desks_controller->desks()[1].get(); |
| ASSERT_EQ(desk1, desks_controller->active_desk()); |
| |
| ToggleOverview(); |
| const auto* desk_bar_view = |
| GetDesksBarViewForRoot(Shell::GetPrimaryRootWindow()); |
| auto* mini_view1 = desk_bar_view->mini_views()[0]; |
| auto* mini_view2 = desk_bar_view->mini_views()[1]; |
| |
| // Use keyboard to navigate to the miniview associated with desk 2. |
| SendKey(ui::VKEY_TAB); |
| SendKey(ui::VKEY_TAB); |
| SendKey(ui::VKEY_TAB); |
| ASSERT_EQ(mini_view2, GetHighlightedView()); |
| |
| // Tests that after hitting ctrl-w on the highlighted miniview associated |
| // with desk 2, desk 2 is destroyed. |
| SendKey(ui::VKEY_W, ui::EF_CONTROL_DOWN); |
| EXPECT_EQ(1u, desks_controller->desks().size()); |
| EXPECT_NE(desk2, desks_controller->desks()[0].get()); |
| |
| if (features::IsBentoEnabled()) { |
| // Go back to zero state since there is only a single desk and mini views |
| // are empty in zero state. |
| EXPECT_TRUE(desk_bar_view->IsZeroState()); |
| EXPECT_TRUE(desk_bar_view->mini_views().empty()); |
| } else { |
| // Tests that hitting ctrl-w on the highlighted miniview if it is the last |
| // one does nothing. |
| while (mini_view1 != GetHighlightedView()) |
| SendKey(ui::VKEY_TAB); |
| SendKey(ui::VKEY_W, ui::EF_CONTROL_DOWN); |
| EXPECT_EQ(1u, desks_controller->desks().size()); |
| } |
| } |
| |
| TEST_F(DesksOverviewHighlightControllerTest, ActivateDeskNameView) { |
| ToggleOverview(); |
| const auto* desk_bar_view = |
| GetDesksBarViewForRoot(Shell::GetPrimaryRootWindow()); |
| auto* desk_name_view_1 = desk_bar_view->mini_views()[0]->desk_name_view(); |
| |
| // Tab until the desk name view of the first desk is highlighted. |
| SendKey(ui::VKEY_TAB); |
| SendKey(ui::VKEY_TAB); |
| EXPECT_EQ(desk_name_view_1, GetHighlightedView()); |
| |
| // Press enter and expect that the desk name is being edited. |
| SendKey(ui::VKEY_RETURN); |
| EXPECT_TRUE(desk_name_view_1->HasFocus()); |
| EXPECT_TRUE(desk_bar_view->IsDeskNameBeingModified()); |
| |
| // All should be selected. |
| EXPECT_TRUE(desk_name_view_1->HasSelection()); |
| const auto* desks_controller = DesksController::Get(); |
| auto* desk_1 = desks_controller->desks()[0].get(); |
| EXPECT_EQ(desk_1->name(), desk_name_view_1->GetSelectedText()); |
| |
| // Arrow keys should not change neither the focus nor the highlight. |
| SendKey(ui::VKEY_RIGHT); |
| SendKey(ui::VKEY_RIGHT); |
| SendKey(ui::VKEY_RIGHT); |
| SendKey(ui::VKEY_LEFT); |
| EXPECT_EQ(desk_name_view_1, GetHighlightedView()); |
| EXPECT_TRUE(desk_name_view_1->HasFocus()); |
| |
| // Select all and delete. |
| SendKey(ui::VKEY_A, ui::EF_CONTROL_DOWN); |
| SendKey(ui::VKEY_BACK); |
| // Type "code" and hit Tab, this should commit the changes and move the |
| // highlight to the next item. |
| SendKey(ui::VKEY_C); |
| SendKey(ui::VKEY_O); |
| SendKey(ui::VKEY_D); |
| SendKey(ui::VKEY_E); |
| SendKey(ui::VKEY_TAB); |
| |
| EXPECT_FALSE(desk_name_view_1->HasFocus()); |
| EXPECT_EQ(desk_bar_view->mini_views()[1], GetHighlightedView()); |
| EXPECT_EQ(base::UTF8ToUTF16("code"), desk_1->name()); |
| EXPECT_TRUE(desk_1->is_name_set_by_user()); |
| } |
| |
| TEST_F(DesksOverviewHighlightControllerTest, RemoveDeskWhileNameIsHighlighted) { |
| ToggleOverview(); |
| const auto* desk_bar_view = |
| GetDesksBarViewForRoot(Shell::GetPrimaryRootWindow()); |
| auto* desk_name_view_1 = desk_bar_view->mini_views()[0]->desk_name_view(); |
| |
| // Tab until the desk name view of the second desk is highlighted. |
| SendKey(ui::VKEY_TAB); |
| SendKey(ui::VKEY_TAB); |
| EXPECT_EQ(desk_name_view_1, GetHighlightedView()); |
| |
| const auto* desks_controller = DesksController::Get(); |
| auto* desk_1 = desks_controller->desks()[0].get(); |
| RemoveDesk(desk_1); |
| |
| // Tabbing again should cause no crashes. |
| EXPECT_EQ(nullptr, GetHighlightedView()); |
| SendKey(ui::VKEY_TAB); |
| if (features::IsBentoEnabled()) { |
| EXPECT_TRUE(desk_bar_view->IsZeroState()); |
| EXPECT_EQ(desk_bar_view->zero_state_default_desk_button(), |
| GetHighlightedView()); |
| } else { |
| EXPECT_EQ(desk_bar_view->mini_views()[0], GetHighlightedView()); |
| } |
| } |
| |
| // A test class for testing Bento features. |
| class BentoOverviewHighlightControllerTest |
| : public DesksOverviewHighlightControllerTest { |
| public: |
| BentoOverviewHighlightControllerTest() = default; |
| BentoOverviewHighlightControllerTest( |
| const BentoOverviewHighlightControllerTest&) = delete; |
| BentoOverviewHighlightControllerTest& operator=( |
| const BentoOverviewHighlightControllerTest&) = delete; |
| ~BentoOverviewHighlightControllerTest() override = default; |
| |
| // DesksOverviewHighlightControllerTest: |
| void SetUp() override { |
| scoped_feature_list_.InitAndEnableFeature(features::kBento); |
| DesksOverviewHighlightControllerTest::SetUp(); |
| } |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| // Tests the overview highlight controller behavior when a user uses the new |
| // desk button and Bento is enabled. |
| TEST_F(BentoOverviewHighlightControllerTest, |
| ActivateCloseHighlightOnNewDeskButton) { |
| // Make sure the display is large enough to hold the max number of desks. |
| UpdateDisplay("1200x800"); |
| ToggleOverview(); |
| const auto* desk_bar_view = |
| GetDesksBarViewForRoot(Shell::GetPrimaryRootWindow()); |
| ASSERT_FALSE(desk_bar_view->IsZeroState()); |
| const auto* new_desk_button = |
| desk_bar_view->expanded_state_new_desk_button()->new_desk_button(); |
| const auto* desks_controller = DesksController::Get(); |
| |
| auto check_name_view_at_index = [this](const auto* desk_bar_view, int index) { |
| const auto* desk_name_view = |
| desk_bar_view->mini_views()[index]->desk_name_view(); |
| EXPECT_TRUE(desk_name_view->HasFocus()); |
| EXPECT_EQ(GetHighlightedView(), desk_name_view); |
| EXPECT_EQ(std::u16string(), desk_name_view->GetText()); |
| }; |
| |
| // Use the keyboard to navigate to the new desk button. |
| SendKey(ui::VKEY_TAB); |
| SendKey(ui::VKEY_TAB); |
| SendKey(ui::VKEY_TAB); |
| SendKey(ui::VKEY_TAB); |
| SendKey(ui::VKEY_TAB); |
| ASSERT_EQ(new_desk_button, GetHighlightedView()); |
| |
| // Keep adding new desks until we reach the maximum allowed amount. Verify the |
| // amount of desks is indeed the maximum allowed and that the new desk button |
| // is disabled. |
| while (desks_controller->CanCreateDesks()) { |
| SendKey(ui::VKEY_RETURN); |
| check_name_view_at_index(desk_bar_view, |
| desks_controller->desks().size() - 1); |
| SendKey(ui::VKEY_TAB); |
| } |
| EXPECT_FALSE(new_desk_button->GetEnabled()); |
| EXPECT_EQ(desks_util::GetMaxNumberOfDesks(), |
| desks_controller->desks().size()); |
| } |
| |
| TEST_F(BentoOverviewHighlightControllerTest, ZeroStateOfDesksBar) { |
| ToggleOverview(); |
| auto* desks_bar_view = GetDesksBarViewForRoot(Shell::GetPrimaryRootWindow()); |
| ASSERT_FALSE(desks_bar_view->IsZeroState()); |
| ASSERT_EQ(2u, desks_bar_view->mini_views().size()); |
| |
| // Remove one desk to enter zero state desks bar. |
| auto* event_generator = GetEventGenerator(); |
| auto* mini_view = desks_bar_view->mini_views()[1]; |
| event_generator->MoveMouseTo(mini_view->GetBoundsInScreen().CenterPoint()); |
| EXPECT_TRUE(mini_view->close_desk_button()->GetVisible()); |
| event_generator->MoveMouseTo( |
| mini_view->close_desk_button()->GetBoundsInScreen().CenterPoint()); |
| event_generator->ClickLeftButton(); |
| EXPECT_TRUE(desks_bar_view->IsZeroState()); |
| |
| // Both zero state default desk button and zero state new desk button can be |
| // focused in overview mode. |
| SendKey(ui::VKEY_TAB); |
| EXPECT_EQ(desks_bar_view->zero_state_default_desk_button(), |
| GetHighlightedView()); |
| SendKey(ui::VKEY_TAB); |
| EXPECT_EQ(desks_bar_view->zero_state_new_desk_button(), GetHighlightedView()); |
| |
| // Trigger the zero state default desk button will focus on the default desk's |
| // name view. |
| SendKey(ui::VKEY_TAB); |
| EXPECT_EQ(desks_bar_view->zero_state_default_desk_button(), |
| GetHighlightedView()); |
| SendKey(ui::VKEY_RETURN); |
| EXPECT_EQ(desks_bar_view->mini_views()[0]->desk_name_view(), |
| GetHighlightedView()); |
| ToggleOverview(); |
| |
| // Trigger the zero state new desk button will focus on the new created desk's |
| // name view. |
| ToggleOverview(); |
| EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession()); |
| desks_bar_view = GetOverviewSession() |
| ->GetGridWithRootWindow(Shell::GetPrimaryRootWindow()) |
| ->desks_bar_view(); |
| EXPECT_TRUE(desks_bar_view->IsZeroState()); |
| SendKey(ui::VKEY_TAB); |
| SendKey(ui::VKEY_TAB); |
| EXPECT_EQ(desks_bar_view->zero_state_new_desk_button(), GetHighlightedView()); |
| SendKey(ui::VKEY_RETURN); |
| EXPECT_EQ(desks_bar_view->mini_views()[1]->desk_name_view(), |
| GetHighlightedView()); |
| } |
| |
| } // namespace ash |