| // Copyright 2017 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/splitview/split_view_controller.h" |
| |
| #include "ash/app_list/app_list_controller_impl.h" |
| #include "ash/display/screen_orientation_controller.h" |
| #include "ash/display/screen_orientation_controller_test_api.h" |
| #include "ash/magnifier/docked_magnifier_controller.h" |
| #include "ash/public/cpp/app_types.h" |
| #include "ash/public/cpp/window_properties.h" |
| #include "ash/screen_util.h" |
| #include "ash/session/session_controller.h" |
| #include "ash/session/test_session_controller_client.h" |
| #include "ash/shelf/shelf_layout_manager.h" |
| #include "ash/shell.h" |
| #include "ash/system/overview/overview_button_tray.h" |
| #include "ash/system/status_area_widget.h" |
| #include "ash/system/status_area_widget_test_helper.h" |
| #include "ash/test/ash_test_base.h" |
| #include "ash/wallpaper/wallpaper_controller.h" |
| #include "ash/wm/drag_window_resizer.h" |
| #include "ash/wm/mru_window_tracker.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_observer.h" |
| #include "ash/wm/splitview/split_view_divider.h" |
| #include "ash/wm/splitview/split_view_drag_indicators.h" |
| #include "ash/wm/splitview/split_view_utils.h" |
| #include "ash/wm/tablet_mode/tablet_mode_browser_window_drag_delegate.h" |
| #include "ash/wm/tablet_mode/tablet_mode_controller.h" |
| #include "ash/wm/tablet_mode/tablet_mode_window_drag_controller.h" |
| #include "ash/wm/tablet_mode/tablet_mode_window_drag_delegate.h" |
| #include "ash/wm/window_resizer.h" |
| #include "ash/wm/window_state.h" |
| #include "ash/wm/window_state_delegate.h" |
| #include "ash/wm/window_util.h" |
| #include "ash/wm/wm_event.h" |
| #include "base/stl_util.h" |
| #include "services/ws/public/mojom/window_tree_constants.mojom.h" |
| #include "ui/aura/client/aura_constants.h" |
| #include "ui/aura/test/test_window_delegate.h" |
| #include "ui/aura/test/test_windows.h" |
| #include "ui/base/hit_test.h" |
| #include "ui/compositor_extra/shadow.h" |
| #include "ui/display/test/display_manager_test_api.h" |
| #include "ui/events/test/event_generator.h" |
| #include "ui/views/bubble/bubble_dialog_delegate_view.h" |
| #include "ui/views/widget/widget.h" |
| #include "ui/wm/core/shadow_controller.h" |
| #include "ui/wm/core/shadow_types.h" |
| #include "ui/wm/core/window_util.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| // The observer to observe the overview states in |root_window_|. |
| class OverviewStatesObserver : public OverviewObserver { |
| public: |
| OverviewStatesObserver(aura::Window* root_window) |
| : root_window_(root_window) { |
| Shell::Get()->overview_controller()->AddObserver(this); |
| } |
| ~OverviewStatesObserver() override { |
| Shell::Get()->overview_controller()->RemoveObserver(this); |
| } |
| |
| // OverviewObserver: |
| void OnOverviewModeStarting() override { |
| // Reset the value to true. |
| overview_animate_when_exiting_ = true; |
| } |
| void OnOverviewModeEnding(OverviewSession* overview_session) override { |
| OverviewGrid* grid = overview_session->GetGridWithRootWindow(root_window_); |
| if (!grid) |
| return; |
| overview_animate_when_exiting_ = grid->should_animate_when_exiting(); |
| } |
| |
| bool overview_animate_when_exiting() const { |
| return overview_animate_when_exiting_; |
| } |
| |
| private: |
| bool overview_animate_when_exiting_ = true; |
| aura::Window* root_window_; |
| |
| DISALLOW_COPY_AND_ASSIGN(OverviewStatesObserver); |
| }; |
| |
| // The test BubbleDialogDelegateView for bubbles. |
| class TestBubbleDialogDelegateView : public views::BubbleDialogDelegateView { |
| public: |
| explicit TestBubbleDialogDelegateView(views::View* anchor_view) |
| : BubbleDialogDelegateView(anchor_view, views::BubbleBorder::NONE) {} |
| ~TestBubbleDialogDelegateView() override {} |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(TestBubbleDialogDelegateView); |
| }; |
| |
| bool IsTabletMode() { |
| return Shell::Get() |
| ->tablet_mode_controller() |
| ->IsTabletModeWindowManagerEnabled(); |
| } |
| |
| } // namespace |
| |
| class SplitViewControllerTest : public AshTestBase { |
| public: |
| SplitViewControllerTest() = default; |
| ~SplitViewControllerTest() override = default; |
| |
| // test::AshTestBase: |
| void SetUp() override { |
| AshTestBase::SetUp(); |
| // Avoid TabletModeController::OnGetSwitchStates() from disabling tablet |
| // mode. |
| base::RunLoop().RunUntilIdle(); |
| Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(true); |
| } |
| |
| aura::Window* CreateWindow( |
| const gfx::Rect& bounds, |
| aura::client::WindowType type = aura::client::WINDOW_TYPE_NORMAL) { |
| aura::Window* window = CreateTestWindowInShellWithDelegateAndType( |
| new SplitViewTestWindowDelegate, type, -1, bounds); |
| return window; |
| } |
| |
| aura::Window* CreateNonSnappableWindow(const gfx::Rect& bounds) { |
| aura::Window* window = CreateWindow(bounds); |
| window->SetProperty(aura::client::kResizeBehaviorKey, |
| ws::mojom::kResizeBehaviorNone); |
| return window; |
| } |
| |
| bool IsDividerAnimating() { |
| return split_view_controller()->IsDividerAnimating(); |
| } |
| |
| void SkipDividerSnapAnimation() { |
| if (!IsDividerAnimating()) |
| return; |
| split_view_controller()->StopAndShoveAnimatedDivider(); |
| split_view_controller()->EndResizeImpl(); |
| split_view_controller()->EndSplitViewAfterResizingIfAppropriate(); |
| } |
| |
| void EndSplitView() { split_view_controller()->EndSplitView(); } |
| |
| void ToggleOverview() { |
| Shell::Get()->overview_controller()->ToggleOverview(); |
| } |
| |
| void LongPressOnOverivewButtonTray() { |
| ui::GestureEvent event(0, 0, 0, base::TimeTicks(), |
| ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS)); |
| StatusAreaWidgetTestHelper::GetStatusAreaWidget() |
| ->overview_button_tray() |
| ->OnGestureEvent(&event); |
| } |
| |
| std::vector<aura::Window*> GetWindowsInOverviewGrids() { |
| return Shell::Get() |
| ->overview_controller() |
| ->GetWindowsListInOverviewGridsForTest(); |
| } |
| |
| SplitViewController* split_view_controller() { |
| return Shell::Get()->split_view_controller(); |
| } |
| |
| SplitViewDivider* split_view_divider() { |
| return split_view_controller()->split_view_divider(); |
| } |
| |
| int divider_position() { return split_view_controller()->divider_position(); } |
| |
| float divider_closest_ratio() { |
| return split_view_controller()->divider_closest_ratio_; |
| } |
| |
| protected: |
| class SplitViewTestWindowDelegate : public aura::test::TestWindowDelegate { |
| public: |
| SplitViewTestWindowDelegate() = default; |
| ~SplitViewTestWindowDelegate() override = default; |
| |
| // aura::test::TestWindowDelegate: |
| void OnWindowDestroying(aura::Window* window) override { window->Hide(); } |
| void OnWindowDestroyed(aura::Window* window) override { delete this; } |
| }; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(SplitViewControllerTest); |
| }; |
| |
| class TestWindowStateDelegate : public wm::WindowStateDelegate { |
| public: |
| TestWindowStateDelegate() = default; |
| ~TestWindowStateDelegate() override = default; |
| |
| // wm::WindowStateDelegate: |
| void OnDragStarted(int component) override { drag_in_progress_ = true; } |
| void OnDragFinished(bool cancel, const gfx::Point& location) override { |
| drag_in_progress_ = false; |
| } |
| |
| bool drag_in_progress() { return drag_in_progress_; } |
| |
| private: |
| bool drag_in_progress_ = false; |
| DISALLOW_COPY_AND_ASSIGN(TestWindowStateDelegate); |
| }; |
| |
| // Tests the basic functionalities. |
| TEST_F(SplitViewControllerTest, Basic) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| |
| EXPECT_EQ(split_view_controller()->state(), SplitViewController::NO_SNAP); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), false); |
| |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::LEFT_SNAPPED); |
| EXPECT_EQ(split_view_controller()->left_window(), window1.get()); |
| EXPECT_NE(split_view_controller()->left_window(), window2.get()); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), true); |
| EXPECT_EQ(window1->GetBoundsInScreen(), |
| split_view_controller()->GetSnappedWindowBoundsInScreen( |
| window1.get(), SplitViewController::LEFT)); |
| |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::BOTH_SNAPPED); |
| EXPECT_EQ(split_view_controller()->right_window(), window2.get()); |
| EXPECT_NE(split_view_controller()->right_window(), window1.get()); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), true); |
| EXPECT_EQ(window2->GetBoundsInScreen(), |
| split_view_controller()->GetSnappedWindowBoundsInScreen( |
| window2.get(), SplitViewController::RIGHT)); |
| |
| EndSplitView(); |
| EXPECT_EQ(split_view_controller()->state(), SplitViewController::NO_SNAP); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), false); |
| } |
| |
| // Tests that the default snapped window is the first window that gets snapped. |
| TEST_F(SplitViewControllerTest, DefaultSnappedWindow) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| EXPECT_EQ(window1.get(), split_view_controller()->GetDefaultSnappedWindow()); |
| |
| EndSplitView(); |
| split_view_controller()->SnapWindow(window2.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window1.get(), |
| SplitViewController::RIGHT); |
| EXPECT_EQ(window2.get(), split_view_controller()->GetDefaultSnappedWindow()); |
| } |
| |
| // Tests that if there are two snapped windows, closing one of them will open |
| // overview window grid on the closed window side of the screen. If there is |
| // only one snapped windows, closing the snapped window will end split view mode |
| // and adjust the overview window grid bounds if the overview mode is active at |
| // that moment. |
| TEST_F(SplitViewControllerTest, WindowCloseTest) { |
| // 1 - First test one snapped window scenario. |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window0(CreateWindow(bounds)); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), false); |
| split_view_controller()->SnapWindow(window0.get(), SplitViewController::LEFT); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), true); |
| // Closing this snapped window should exit split view mode. |
| window0.reset(); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), false); |
| |
| // 2 - Then test two snapped windows scenario. |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), false); |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), true); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::BOTH_SNAPPED); |
| EXPECT_EQ(split_view_controller()->default_snap_position(), |
| SplitViewController::LEFT); |
| |
| // Closing one of the two snapped windows will not end split view mode. |
| window1.reset(); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), true); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::RIGHT_SNAPPED); |
| // Since left window was closed, its default snap position changed to RIGHT. |
| EXPECT_EQ(split_view_controller()->default_snap_position(), |
| SplitViewController::RIGHT); |
| // Window grid is showing no recent items, and has no windows, but it is still |
| // available. |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| |
| // Now close the other snapped window. |
| window2.reset(); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), false); |
| EXPECT_EQ(split_view_controller()->state(), SplitViewController::NO_SNAP); |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| |
| // 3 - Then test the scenario with more than two windows. |
| std::unique_ptr<aura::Window> window3(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window4(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window5(CreateWindow(bounds)); |
| split_view_controller()->SnapWindow(window3.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window4.get(), |
| SplitViewController::RIGHT); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), true); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::BOTH_SNAPPED); |
| EXPECT_EQ(split_view_controller()->default_snap_position(), |
| SplitViewController::LEFT); |
| |
| // Close one of the snapped windows. |
| window4.reset(); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), true); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::LEFT_SNAPPED); |
| EXPECT_EQ(split_view_controller()->default_snap_position(), |
| SplitViewController::LEFT); |
| // Now overview window grid can be opened. |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| |
| // Close the other snapped window. |
| window3.reset(); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), false); |
| EXPECT_EQ(split_view_controller()->state(), SplitViewController::NO_SNAP); |
| // Test the overview winow grid should still open. |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| } |
| |
| // Tests that if there are two snapped windows, minimizing one of them will open |
| // overview window grid on the minimized window side of the screen. If there is |
| // only one snapped windows, minimizing the sanpped window will end split view |
| // mode and adjust the overview window grid bounds if the overview mode is |
| // active at that moment. |
| TEST_F(SplitViewControllerTest, MinimizeWindowTest) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| |
| // 1 - First test one snapped window scenario. |
| std::unique_ptr<aura::Window> window0(CreateWindow(bounds)); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), false); |
| split_view_controller()->SnapWindow(window0.get(), SplitViewController::LEFT); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), true); |
| wm::WMEvent minimize_event(wm::WM_EVENT_MINIMIZE); |
| wm::GetWindowState(window0.get())->OnWMEvent(&minimize_event); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), false); |
| |
| // 2 - Then test the scenario that has 2 or more windows. |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), true); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::BOTH_SNAPPED); |
| EXPECT_EQ(split_view_controller()->default_snap_position(), |
| SplitViewController::LEFT); |
| |
| // Minimizing one of the two snapped windows will not end split view mode. |
| wm::GetWindowState(window1.get())->OnWMEvent(&minimize_event); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), true); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::RIGHT_SNAPPED); |
| // Since left window was minimized, its default snap position changed to |
| // RIGHT. |
| EXPECT_EQ(split_view_controller()->default_snap_position(), |
| SplitViewController::RIGHT); |
| // The overview window grid will open. |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| |
| // Now minimize the other snapped window. |
| wm::GetWindowState(window2.get())->OnWMEvent(&minimize_event); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), false); |
| EXPECT_EQ(split_view_controller()->state(), SplitViewController::NO_SNAP); |
| // The overview window grid is still open. |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| } |
| |
| // Tests that if one of the snapped window gets maximized / full-screened, the |
| // split view mode ends. |
| TEST_F(SplitViewControllerTest, WindowStateChangeTest) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| // 1 - First test one snapped window scenario. |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), false); |
| |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), true); |
| |
| wm::WMEvent maximize_event(wm::WM_EVENT_MAXIMIZE); |
| wm::GetWindowState(window1.get())->OnWMEvent(&maximize_event); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), false); |
| |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), true); |
| |
| wm::WMEvent fullscreen_event(wm::WM_EVENT_FULLSCREEN); |
| wm::GetWindowState(window1.get())->OnWMEvent(&fullscreen_event); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), false); |
| |
| // 2 - Then test two snapped window scenario. |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), true); |
| |
| // Maximize one of the snapped window will end the split view mode. |
| wm::GetWindowState(window1.get())->OnWMEvent(&maximize_event); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), false); |
| |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), true); |
| |
| // Full-screen one of the snapped window will also end the split view mode. |
| wm::GetWindowState(window1.get())->OnWMEvent(&fullscreen_event); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), false); |
| |
| // 3 - Test the scenario that part of the screen is a snapped window and part |
| // of the screen is the overview window grid. |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), true); |
| ToggleOverview(); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), true); |
| |
| // Maximize the snapped window will end the split view mode and overview mode. |
| wm::GetWindowState(window1.get())->OnWMEvent(&maximize_event); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), false); |
| EXPECT_FALSE(Shell::Get()->overview_controller()->IsSelecting()); |
| |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), true); |
| ToggleOverview(); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), true); |
| |
| // Fullscreen the snapped window will end the split view mode and overview |
| // mode. |
| wm::GetWindowState(window1.get())->OnWMEvent(&fullscreen_event); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), false); |
| EXPECT_FALSE(Shell::Get()->overview_controller()->IsSelecting()); |
| } |
| |
| // Tests that if split view mode is active, activate another window will snap |
| // the window to the non-default side of the screen. |
| TEST_F(SplitViewControllerTest, WindowActivationTest) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window3(CreateWindow(bounds)); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), false); |
| |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), true); |
| EXPECT_EQ(split_view_controller()->left_window(), window1.get()); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::LEFT_SNAPPED); |
| |
| wm::ActivateWindow(window2.get()); |
| EXPECT_EQ(split_view_controller()->right_window(), window2.get()); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::BOTH_SNAPPED); |
| |
| wm::ActivateWindow(window3.get()); |
| EXPECT_EQ(split_view_controller()->right_window(), window3.get()); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::BOTH_SNAPPED); |
| } |
| |
| // Tests that if split view mode and overview mode are active at the same time, |
| // i.e., half of the screen is occupied by a snapped window and half of the |
| // screen is occupied by the overview windows grid, the next activatable window |
| // will be picked to snap when exiting the overview mode. |
| TEST_F(SplitViewControllerTest, ExitOverviewTest) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window3(CreateWindow(bounds)); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), false); |
| |
| ToggleOverview(); |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), true); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::LEFT_SNAPPED); |
| EXPECT_EQ(split_view_controller()->left_window(), window1.get()); |
| |
| ToggleOverview(); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::BOTH_SNAPPED); |
| EXPECT_EQ(split_view_controller()->right_window(), window3.get()); |
| } |
| |
| // Tests that if split view mode is active when entering overview, the overview |
| // windows grid should show in the non-default side of the screen, and the |
| // default snapped window should not be shown in the overview window grid. |
| TEST_F(SplitViewControllerTest, EnterOverviewModeTest) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window3(CreateWindow(bounds)); |
| |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::BOTH_SNAPPED); |
| EXPECT_EQ(split_view_controller()->GetDefaultSnappedWindow(), window1.get()); |
| |
| ToggleOverview(); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::LEFT_SNAPPED); |
| EXPECT_FALSE( |
| base::ContainsValue(GetWindowsInOverviewGrids(), |
| split_view_controller()->GetDefaultSnappedWindow())); |
| } |
| |
| // Tests that the split divider was created when the split view mode is active |
| // and destroyed when the split view mode is ended. The split divider should be |
| // always above the two snapped windows. |
| TEST_F(SplitViewControllerTest, SplitDividerBasicTest) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| |
| EXPECT_TRUE(!split_view_divider()); |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| EXPECT_TRUE(split_view_divider()); |
| EXPECT_TRUE(split_view_divider()->divider_widget()->IsAlwaysOnTop()); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| EXPECT_TRUE(split_view_divider()); |
| EXPECT_TRUE(split_view_divider()->divider_widget()->IsAlwaysOnTop()); |
| |
| // Test that activating an non-snappable window ends the split view mode. |
| std::unique_ptr<aura::Window> window3(CreateNonSnappableWindow(bounds)); |
| wm::ActivateWindow(window3.get()); |
| EXPECT_FALSE(split_view_divider()); |
| } |
| |
| // Verifys that the bounds of the two windows in splitview are as expected. |
| TEST_F(SplitViewControllerTest, SplitDividerWindowBounds) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| ASSERT_TRUE(split_view_divider()); |
| |
| // Verify with two freshly snapped windows are roughly the same width (off by |
| // one pixel at most due to the display maybe being even and the divider being |
| // a fixed odd pixel width). |
| int window1_width = window1->GetBoundsInScreen().width(); |
| int window2_width = window2->GetBoundsInScreen().width(); |
| gfx::Rect divider_bounds = |
| split_view_divider()->GetDividerBoundsInScreen(false /* is_dragging */); |
| const int screen_width = |
| screen_util::GetDisplayWorkAreaBoundsInParent(window1.get()).width(); |
| EXPECT_NEAR(window1_width, window2_width, 1); |
| EXPECT_EQ(screen_width, |
| window1_width + divider_bounds.width() + window2_width); |
| |
| // Drag the divider to a position two thirds of the screen size. Verify window |
| // 1 is wider than window 2. |
| GetEventGenerator()->set_current_screen_location( |
| divider_bounds.CenterPoint()); |
| GetEventGenerator()->DragMouseTo(screen_width * 0.67f, 0); |
| SkipDividerSnapAnimation(); |
| window1_width = window1->GetBoundsInScreen().width(); |
| window2_width = window2->GetBoundsInScreen().width(); |
| const int old_window1_width = window1_width; |
| const int old_window2_width = window2_width; |
| EXPECT_GT(window1_width, 2 * window2_width); |
| EXPECT_EQ(screen_width, |
| window1_width + divider_bounds.width() + window2_width); |
| |
| // Drag the divider to a position close to two thirds of the screen size. |
| // Verify the divider snaps to two thirds of the screen size, and the windows |
| // remain the same size as previously. |
| divider_bounds = |
| split_view_divider()->GetDividerBoundsInScreen(false /* is_dragging */); |
| GetEventGenerator()->set_current_screen_location( |
| divider_bounds.CenterPoint()); |
| GetEventGenerator()->DragMouseTo(screen_width * 0.7f, 0); |
| SkipDividerSnapAnimation(); |
| window1_width = window1->GetBoundsInScreen().width(); |
| window2_width = window2->GetBoundsInScreen().width(); |
| EXPECT_EQ(window1_width, old_window1_width); |
| EXPECT_EQ(window2_width, old_window2_width); |
| |
| // Drag the divider to a position one third of the screen size. Verify window |
| // 1 is wider than window 2. |
| divider_bounds = |
| split_view_divider()->GetDividerBoundsInScreen(false /* is_dragging */); |
| GetEventGenerator()->set_current_screen_location( |
| divider_bounds.CenterPoint()); |
| GetEventGenerator()->DragMouseTo(screen_width * 0.33f, 0); |
| SkipDividerSnapAnimation(); |
| window1_width = window1->GetBoundsInScreen().width(); |
| window2_width = window2->GetBoundsInScreen().width(); |
| EXPECT_GT(window2_width, 2 * window1_width); |
| EXPECT_EQ(screen_width, |
| window1_width + divider_bounds.width() + window2_width); |
| |
| // Verify that the left window from dragging the divider to two thirds of the |
| // screen size is roughly the same size as the right window after dragging the |
| // divider to one third of the screen size, and vice versa. |
| EXPECT_NEAR(window1_width, old_window2_width, 1); |
| EXPECT_NEAR(window2_width, old_window1_width, 1); |
| } |
| |
| // Tests that the bounds of the snapped windows and divider are adjusted when |
| // the screen display configuration changes. |
| TEST_F(SplitViewControllerTest, DisplayConfigurationChangeTest) { |
| UpdateDisplay("407x400"); |
| const gfx::Rect bounds(0, 0, 200, 200); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| |
| const gfx::Rect bounds_window1 = window1->GetBoundsInScreen(); |
| const gfx::Rect bounds_window2 = window2->GetBoundsInScreen(); |
| const gfx::Rect bounds_divider = |
| split_view_divider()->GetDividerBoundsInScreen(false /* is_dragging */); |
| |
| // Test that |window1| and |window2| has the same width and height after snap. |
| EXPECT_NEAR(bounds_window1.width(), bounds_window2.width(), 1); |
| EXPECT_EQ(bounds_window1.height(), bounds_window2.height()); |
| EXPECT_EQ(bounds_divider.height(), bounds_window1.height()); |
| |
| // Test that |window1|, divider, |window2| are aligned properly. |
| EXPECT_EQ(bounds_divider.x(), bounds_window1.x() + bounds_window1.width()); |
| EXPECT_EQ(bounds_window2.x(), bounds_divider.x() + bounds_divider.width()); |
| |
| // Now change the display configuration. |
| UpdateDisplay("507x500"); |
| const gfx::Rect new_bounds_window1 = window1->GetBoundsInScreen(); |
| const gfx::Rect new_bounds_window2 = window2->GetBoundsInScreen(); |
| const gfx::Rect new_bounds_divider = |
| split_view_divider()->GetDividerBoundsInScreen(false /* is_dragging */); |
| |
| // Test that the new bounds are different with the old ones. |
| EXPECT_NE(bounds_window1, new_bounds_window1); |
| EXPECT_NE(bounds_window2, new_bounds_window2); |
| EXPECT_NE(bounds_divider, new_bounds_divider); |
| |
| // Test that |window1|, divider, |window2| are still aligned properly. |
| EXPECT_EQ(new_bounds_divider.x(), |
| new_bounds_window1.x() + new_bounds_window1.width()); |
| EXPECT_EQ(new_bounds_window2.x(), |
| new_bounds_divider.x() + new_bounds_divider.width()); |
| } |
| |
| // Tests that the bounds of the snapped windows and divider are adjusted when |
| // the internal screen display configuration changes. |
| TEST_F(SplitViewControllerTest, InternalDisplayConfigurationChangeTest) { |
| UpdateDisplay("407x400"); |
| int64_t display_id = display::Screen::GetScreen()->GetPrimaryDisplay().id(); |
| display::DisplayManager* display_manager = Shell::Get()->display_manager(); |
| display::test::ScopedSetInternalDisplayId set_internal(display_manager, |
| display_id); |
| |
| const gfx::Rect bounds(0, 0, 200, 200); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| |
| const gfx::Rect bounds_window1 = window1->GetBoundsInScreen(); |
| const gfx::Rect bounds_window2 = window2->GetBoundsInScreen(); |
| const gfx::Rect bounds_divider = |
| split_view_divider()->GetDividerBoundsInScreen(false /* is_dragging */); |
| |
| // Test that |window1| and |window2| has the same width and height after snap. |
| EXPECT_NEAR(bounds_window1.width(), bounds_window2.width(), 1); |
| EXPECT_EQ(bounds_window1.height(), bounds_window2.height()); |
| EXPECT_EQ(bounds_divider.height(), bounds_window1.height()); |
| |
| // Test that |window1|, divider, |window2| are aligned properly. |
| EXPECT_EQ(bounds_divider.x(), bounds_window1.x() + bounds_window1.width()); |
| EXPECT_EQ(bounds_window2.x(), bounds_divider.x() + bounds_divider.width()); |
| |
| // Now change the display configuration. |
| UpdateDisplay("507x500"); |
| const gfx::Rect new_bounds_window1 = window1->GetBoundsInScreen(); |
| const gfx::Rect new_bounds_window2 = window2->GetBoundsInScreen(); |
| const gfx::Rect new_bounds_divider = |
| split_view_divider()->GetDividerBoundsInScreen(false /* is_dragging */); |
| |
| // Test that the new bounds are different with the old ones. |
| EXPECT_NE(bounds_window1, new_bounds_window1); |
| EXPECT_NE(bounds_window2, new_bounds_window2); |
| EXPECT_NE(bounds_divider, new_bounds_divider); |
| |
| // Test that |window1|, divider, |window2| are still aligned properly. |
| EXPECT_EQ(new_bounds_divider.x(), |
| new_bounds_window1.x() + new_bounds_window1.width()); |
| EXPECT_EQ(new_bounds_window2.x(), |
| new_bounds_divider.x() + new_bounds_divider.width()); |
| } |
| |
| // Test that if the internal screen display configuration changes during the |
| // divider snap animation, then this animation stops, and the bounds of the |
| // snapped windows and divider are adjusted as normal. |
| TEST_F(SplitViewControllerTest, |
| InternalDisplayConfigurationChangeDuringDividerSnap) { |
| UpdateDisplay("407x400"); |
| int64_t display_id = display::Screen::GetScreen()->GetPrimaryDisplay().id(); |
| display::DisplayManager* display_manager = Shell::Get()->display_manager(); |
| display::test::ScopedSetInternalDisplayId set_internal(display_manager, |
| display_id); |
| |
| const gfx::Rect bounds(0, 0, 200, 200); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| |
| const gfx::Rect bounds_window1 = window1->GetBoundsInScreen(); |
| const gfx::Rect bounds_window2 = window2->GetBoundsInScreen(); |
| const gfx::Rect bounds_divider = |
| split_view_divider()->GetDividerBoundsInScreen(false /* is_dragging */); |
| |
| // Test that |window1| and |window2| has the same width and height after snap. |
| EXPECT_NEAR(bounds_window1.width(), bounds_window2.width(), 1); |
| EXPECT_EQ(bounds_window1.height(), bounds_window2.height()); |
| EXPECT_EQ(bounds_divider.height(), bounds_window1.height()); |
| |
| // Test that |window1|, divider, |window2| are aligned properly. |
| EXPECT_EQ(bounds_divider.x(), bounds_window1.x() + bounds_window1.width()); |
| EXPECT_EQ(bounds_window2.x(), bounds_divider.x() + bounds_divider.width()); |
| |
| // Drag the divider to trigger the snap animation. |
| const gfx::Point divider_center = |
| split_view_divider() |
| ->GetDividerBoundsInScreen(false /* is_dragging */) |
| .CenterPoint(); |
| GetEventGenerator()->set_current_screen_location(divider_center); |
| GetEventGenerator()->DragMouseBy(20, 0); |
| ASSERT_TRUE(IsDividerAnimating()); |
| const gfx::Rect animation_start_bounds_window1 = window1->GetBoundsInScreen(); |
| const gfx::Rect animation_start_bounds_window2 = window2->GetBoundsInScreen(); |
| const gfx::Rect animation_start_bounds_divider = |
| split_view_divider()->GetDividerBoundsInScreen(false /* is_dragging */); |
| |
| // Change the display configuration and check that the snap animation stops. |
| UpdateDisplay("507x500"); |
| EXPECT_FALSE(IsDividerAnimating()); |
| const gfx::Rect new_bounds_window1 = window1->GetBoundsInScreen(); |
| const gfx::Rect new_bounds_window2 = window2->GetBoundsInScreen(); |
| const gfx::Rect new_bounds_divider = |
| split_view_divider()->GetDividerBoundsInScreen(false /* is_dragging */); |
| |
| // Test that the new bounds are different with the old ones. |
| EXPECT_NE(bounds_window1, new_bounds_window1); |
| EXPECT_NE(bounds_window2, new_bounds_window2); |
| EXPECT_NE(bounds_divider, new_bounds_divider); |
| |
| // Test that the new bounds are also different with the ones from the start of |
| // the divider snap animation. |
| EXPECT_NE(bounds_window1, animation_start_bounds_window1); |
| EXPECT_NE(bounds_window2, animation_start_bounds_window2); |
| EXPECT_NE(bounds_divider, animation_start_bounds_divider); |
| |
| // Test that |window1|, divider, |window2| are still aligned properly. |
| EXPECT_EQ(new_bounds_divider.x(), |
| new_bounds_window1.x() + new_bounds_window1.width()); |
| EXPECT_EQ(new_bounds_window2.x(), |
| new_bounds_divider.x() + new_bounds_divider.width()); |
| } |
| |
| // Test that if the internal screen display configuration changes during the |
| // divider snap animation, and if the adjusted divider bounds place it at an |
| // edge of the screen, then split view ends. |
| TEST_F(SplitViewControllerTest, |
| InternalDisplayConfigurationChangeDuringDividerSnapToEndSplitView) { |
| UpdateDisplay("407x400"); |
| int64_t display_id = display::Screen::GetScreen()->GetPrimaryDisplay().id(); |
| display::DisplayManager* display_manager = Shell::Get()->display_manager(); |
| display::test::ScopedSetInternalDisplayId set_internal(display_manager, |
| display_id); |
| |
| const gfx::Rect bounds(0, 0, 200, 200); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| |
| const gfx::Rect bounds_window1 = window1->GetBoundsInScreen(); |
| const gfx::Rect bounds_window2 = window2->GetBoundsInScreen(); |
| const gfx::Rect bounds_divider = |
| split_view_divider()->GetDividerBoundsInScreen(false /* is_dragging */); |
| |
| // Test that |window1| and |window2| has the same width and height after snap. |
| EXPECT_NEAR(bounds_window1.width(), bounds_window2.width(), 1); |
| EXPECT_EQ(bounds_window1.height(), bounds_window2.height()); |
| EXPECT_EQ(bounds_divider.height(), bounds_window1.height()); |
| |
| // Test that |window1|, divider, |window2| are aligned properly. |
| EXPECT_EQ(bounds_divider.x(), bounds_window1.x() + bounds_window1.width()); |
| EXPECT_EQ(bounds_window2.x(), bounds_divider.x() + bounds_divider.width()); |
| |
| // Drag the divider to end split view pending the snap animation. |
| const gfx::Point divider_center = |
| split_view_divider() |
| ->GetDividerBoundsInScreen(false /* is_dragging */) |
| .CenterPoint(); |
| GetEventGenerator()->set_current_screen_location(divider_center); |
| GetEventGenerator()->DragMouseBy(bounds_window2.width() - 20, 0); |
| ASSERT_TRUE(split_view_controller()->IsSplitViewModeActive()); |
| ASSERT_TRUE(IsDividerAnimating()); |
| |
| // Change the display configuration and check that split view ends. |
| UpdateDisplay("507x500"); |
| EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive()); |
| } |
| |
| // Verify the left and right windows get swapped when SwapWindows is called or |
| // the divider is double clicked. |
| TEST_F(SplitViewControllerTest, SwapWindows) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| ASSERT_EQ(split_view_controller()->left_window(), window1.get()); |
| ASSERT_EQ(split_view_controller()->right_window(), window2.get()); |
| |
| gfx::Rect left_bounds = window1->GetBoundsInScreen(); |
| gfx::Rect right_bounds = window2->GetBoundsInScreen(); |
| |
| // Verify that after swapping windows, the windows and their bounds have been |
| // swapped. |
| split_view_controller()->SwapWindows(); |
| EXPECT_EQ(split_view_controller()->left_window(), window2.get()); |
| EXPECT_EQ(split_view_controller()->right_window(), window1.get()); |
| EXPECT_EQ(left_bounds, window2->GetBoundsInScreen()); |
| EXPECT_EQ(right_bounds, window1->GetBoundsInScreen()); |
| |
| // End split view mode and snap the window to RIGHT first, verify the function |
| // SwapWindows() still works properly. |
| EndSplitView(); |
| split_view_controller()->SnapWindow(window1.get(), |
| SplitViewController::RIGHT); |
| split_view_controller()->SnapWindow(window2.get(), SplitViewController::LEFT); |
| ASSERT_EQ(split_view_controller()->right_window(), window1.get()); |
| ASSERT_EQ(split_view_controller()->left_window(), window2.get()); |
| |
| left_bounds = window2->GetBoundsInScreen(); |
| right_bounds = window1->GetBoundsInScreen(); |
| |
| split_view_controller()->SwapWindows(); |
| EXPECT_EQ(split_view_controller()->left_window(), window1.get()); |
| EXPECT_EQ(split_view_controller()->right_window(), window2.get()); |
| EXPECT_EQ(left_bounds, window1->GetBoundsInScreen()); |
| EXPECT_EQ(right_bounds, window2->GetBoundsInScreen()); |
| |
| // Perform a double click on the divider center. |
| const gfx::Point divider_center = |
| split_view_divider() |
| ->GetDividerBoundsInScreen(false /* is_dragging */) |
| .CenterPoint(); |
| GetEventGenerator()->set_current_screen_location(divider_center); |
| GetEventGenerator()->DoubleClickLeftButton(); |
| |
| EXPECT_EQ(split_view_controller()->left_window(), window2.get()); |
| EXPECT_EQ(split_view_controller()->right_window(), window1.get()); |
| EXPECT_EQ(left_bounds, window2->GetBoundsInScreen()); |
| EXPECT_EQ(right_bounds, window1->GetBoundsInScreen()); |
| } |
| |
| // Verify the left and right windows get swapped when the divider is double |
| // tapped. SwapWindows() contains a long code comment that shows it is worth |
| // having separate tests for double clicking and double tapping the divider. |
| TEST_F(SplitViewControllerTest, DoubleTapDivider) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| ASSERT_EQ(split_view_controller()->left_window(), window1.get()); |
| ASSERT_EQ(split_view_controller()->right_window(), window2.get()); |
| |
| gfx::Rect left_bounds = window1->GetBoundsInScreen(); |
| gfx::Rect right_bounds = window2->GetBoundsInScreen(); |
| |
| // Perform a double tap on the divider center. |
| const gfx::Point divider_center = |
| split_view_divider() |
| ->GetDividerBoundsInScreen(false /* is_dragging */) |
| .CenterPoint(); |
| GetEventGenerator()->GestureTapAt(divider_center); |
| GetEventGenerator()->GestureTapAt(divider_center); |
| |
| EXPECT_EQ(split_view_controller()->left_window(), window2.get()); |
| EXPECT_EQ(split_view_controller()->right_window(), window1.get()); |
| EXPECT_EQ(left_bounds, window2->GetBoundsInScreen()); |
| EXPECT_EQ(right_bounds, window1->GetBoundsInScreen()); |
| } |
| |
| // Verify the left and right windows do not get swapped when the divider is |
| // dragged and double clicked. |
| TEST_F(SplitViewControllerTest, DragAndDoubleClickDivider) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| ASSERT_EQ(split_view_controller()->left_window(), window1.get()); |
| ASSERT_EQ(split_view_controller()->right_window(), window2.get()); |
| |
| // Drag the divider and double click it before the snap animation moves it. |
| const gfx::Point divider_center = |
| split_view_divider() |
| ->GetDividerBoundsInScreen(false /* is_dragging */) |
| .CenterPoint(); |
| GetEventGenerator()->set_current_screen_location(divider_center); |
| GetEventGenerator()->DragMouseBy(20, 0); |
| GetEventGenerator()->DoubleClickLeftButton(); |
| |
| EXPECT_EQ(split_view_controller()->left_window(), window1.get()); |
| EXPECT_EQ(split_view_controller()->right_window(), window2.get()); |
| } |
| |
| // Verify the left and right windows do not get swapped when the divider is |
| // dragged and double tapped. |
| TEST_F(SplitViewControllerTest, DragAndDoubleTapDivider) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| ASSERT_EQ(split_view_controller()->left_window(), window1.get()); |
| ASSERT_EQ(split_view_controller()->right_window(), window2.get()); |
| |
| // Drag the divider and double tap it before the snap animation moves it. |
| const gfx::Point divider_center = |
| split_view_divider() |
| ->GetDividerBoundsInScreen(false /* is_dragging */) |
| .CenterPoint(); |
| GetEventGenerator()->set_current_screen_location(divider_center); |
| const gfx::Point drag_destination = divider_center + gfx::Vector2d(20, 0); |
| GetEventGenerator()->DragMouseTo(drag_destination); |
| GetEventGenerator()->GestureTapAt(drag_destination); |
| GetEventGenerator()->GestureTapAt(drag_destination); |
| |
| EXPECT_EQ(split_view_controller()->left_window(), window1.get()); |
| EXPECT_EQ(split_view_controller()->right_window(), window2.get()); |
| } |
| |
| // Verify that you cannot snap a window during the divider snap animation. |
| TEST_F(SplitViewControllerTest, SnapWindowDuringDividerSnapAnimation) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window3(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window4(CreateWindow(bounds)); |
| |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| ASSERT_EQ(split_view_controller()->left_window(), window1.get()); |
| ASSERT_EQ(split_view_controller()->right_window(), window2.get()); |
| |
| // Drag the divider to trigger the snap animation. |
| const gfx::Point divider_center = |
| split_view_divider() |
| ->GetDividerBoundsInScreen(false /* is_dragging */) |
| .CenterPoint(); |
| GetEventGenerator()->set_current_screen_location(divider_center); |
| GetEventGenerator()->DragMouseBy(20, 0); |
| ASSERT_TRUE(IsDividerAnimating()); |
| |
| split_view_controller()->SnapWindow(window3.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window4.get(), |
| SplitViewController::RIGHT); |
| EXPECT_EQ(split_view_controller()->left_window(), window1.get()); |
| EXPECT_EQ(split_view_controller()->right_window(), window2.get()); |
| } |
| |
| // Verify that you cannot start dragging the divider during its snap animation. |
| TEST_F(SplitViewControllerTest, StartDraggingDividerDuringSnapAnimation) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| ASSERT_EQ(split_view_controller()->left_window(), window1.get()); |
| ASSERT_EQ(split_view_controller()->right_window(), window2.get()); |
| |
| // Drag the divider and then try to start dragging it again without waiting |
| // for the snap animation. |
| const gfx::Point divider_center = |
| split_view_divider() |
| ->GetDividerBoundsInScreen(false /* is_dragging */) |
| .CenterPoint(); |
| GetEventGenerator()->set_current_screen_location(divider_center); |
| GetEventGenerator()->DragMouseBy(20, 0); |
| GetEventGenerator()->PressLeftButton(); |
| EXPECT_FALSE(split_view_controller()->is_resizing()); |
| GetEventGenerator()->ReleaseLeftButton(); |
| } |
| |
| // Verifies that by long pressing on the overview button tray, split view gets |
| // activated iff we have two or more windows in the mru list. |
| TEST_F(SplitViewControllerTest, LongPressEntersSplitView) { |
| // Verify that with no active windows, split view does not get activated. |
| LongPressOnOverivewButtonTray(); |
| EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive()); |
| |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| wm::ActivateWindow(window1.get()); |
| |
| // Verify that with only one window, split view does not get activated. |
| LongPressOnOverivewButtonTray(); |
| EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive()); |
| |
| // Verify that with two windows, split view gets activated. |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| wm::ActivateWindow(window2.get()); |
| LongPressOnOverivewButtonTray(); |
| EXPECT_TRUE(split_view_controller()->IsSplitViewModeActive()); |
| } |
| |
| // Verify that when in split view mode with either one snapped or two snapped |
| // windows, split view mode gets exited when the overview button gets a long |
| // press event. |
| TEST_F(SplitViewControllerTest, LongPressExitsSplitView) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| wm::ActivateWindow(window2.get()); |
| wm::ActivateWindow(window1.get()); |
| |
| // Snap |window1| to the left. |
| ToggleOverview(); |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| ASSERT_TRUE(split_view_controller()->IsSplitViewModeActive()); |
| |
| // Verify that by long pressing on the overview button tray with left snapped |
| // window, split view mode gets exited and the left window (|window1|) is the |
| // current active window. |
| LongPressOnOverivewButtonTray(); |
| EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive()); |
| EXPECT_FALSE(Shell::Get()->overview_controller()->IsSelecting()); |
| EXPECT_EQ(window1.get(), wm::GetActiveWindow()); |
| |
| // Snap |window1| to the right. |
| ToggleOverview(); |
| split_view_controller()->SnapWindow(window1.get(), |
| SplitViewController::RIGHT); |
| ASSERT_TRUE(split_view_controller()->IsSplitViewModeActive()); |
| |
| // Verify that by long pressing on the overview button tray with right snapped |
| // window, split view mode gets exited and the right window (|window1|) is the |
| // current active window. |
| LongPressOnOverivewButtonTray(); |
| EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive()); |
| EXPECT_FALSE(Shell::Get()->overview_controller()->IsSelecting()); |
| EXPECT_EQ(window1.get(), wm::GetActiveWindow()); |
| |
| // Snap two windows and activate the left window, |window1|. |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| wm::ActivateWindow(window1.get()); |
| ASSERT_TRUE(split_view_controller()->IsSplitViewModeActive()); |
| |
| // Verify that by long pressing on the overview button tray with two snapped |
| // windows, split view mode gets exited. |
| LongPressOnOverivewButtonTray(); |
| EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive()); |
| EXPECT_EQ(window1.get(), wm::GetActiveWindow()); |
| |
| // Snap two windows and activate the right window, |window2|. |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| wm::ActivateWindow(window2.get()); |
| ASSERT_TRUE(split_view_controller()->IsSplitViewModeActive()); |
| |
| // Verify that by long pressing on the overview button tray with two snapped |
| // windows, split view mode gets exited, and the activated window in splitview |
| // is the current active window. |
| LongPressOnOverivewButtonTray(); |
| EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive()); |
| EXPECT_EQ(window2.get(), wm::GetActiveWindow()); |
| } |
| |
| // Verify that if a window with a transient child which is not snappable is |
| // activated, and the the overview tray is long pressed, we will enter splitview |
| // with the transient parent snapped. |
| TEST_F(SplitViewControllerTest, LongPressEntersSplitViewWithTransientChild) { |
| // Add two windows with one being a transient child of the first. |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> parent(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> child( |
| CreateWindow(bounds, aura::client::WINDOW_TYPE_POPUP)); |
| ::wm::AddTransientChild(parent.get(), child.get()); |
| ::wm::ActivateWindow(parent.get()); |
| ::wm::ActivateWindow(child.get()); |
| |
| // Verify that long press on the overview button will not enter split view |
| // mode, as there needs to be two non-transient child windows. |
| LongPressOnOverivewButtonTray(); |
| EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive()); |
| |
| // Add a third window. Focus the transient child. |
| std::unique_ptr<aura::Window> third_window(CreateWindow(bounds)); |
| ::wm::ActivateWindow(third_window.get()); |
| ::wm::ActivateWindow(parent.get()); |
| ::wm::ActivateWindow(child.get()); |
| |
| // Verify that long press will snap the focused transient child's parent. |
| LongPressOnOverivewButtonTray(); |
| EXPECT_TRUE(split_view_controller()->IsSplitViewModeActive()); |
| EXPECT_EQ(split_view_controller()->GetDefaultSnappedWindow(), parent.get()); |
| } |
| |
| TEST_F(SplitViewControllerTest, LongPressExitsSplitViewWithTransientChild) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> left_window(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> right_window(CreateWindow(bounds)); |
| wm::ActivateWindow(left_window.get()); |
| wm::ActivateWindow(right_window.get()); |
| |
| ToggleOverview(); |
| split_view_controller()->SnapWindow(left_window.get(), |
| SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(right_window.get(), |
| SplitViewController::RIGHT); |
| ASSERT_TRUE(split_view_controller()->IsSplitViewModeActive()); |
| |
| // Add a transient child to |right_window|, and activate it. |
| aura::Window* transient_child = |
| aura::test::CreateTestWindowWithId(0, right_window.get()); |
| ::wm::AddTransientChild(right_window.get(), transient_child); |
| wm::ActivateWindow(transient_child); |
| |
| // Verify that by long pressing on the overview button tray, split view mode |
| // gets exited and the window which contained |transient_child| is the |
| // current active window. |
| LongPressOnOverivewButtonTray(); |
| EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive()); |
| EXPECT_FALSE(Shell::Get()->overview_controller()->IsSelecting()); |
| EXPECT_EQ(right_window.get(), wm::GetActiveWindow()); |
| } |
| |
| // Verify that split view mode get activated when long pressing on the overview |
| // button while in overview mode iff we have more than two windows in the mru |
| // list. |
| TEST_F(SplitViewControllerTest, LongPressInOverviewMode) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| wm::ActivateWindow(window1.get()); |
| |
| ToggleOverview(); |
| ASSERT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| ASSERT_FALSE(split_view_controller()->IsSplitViewModeActive()); |
| |
| // Nothing happens if there is only one window. |
| LongPressOnOverivewButtonTray(); |
| EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive()); |
| |
| // Verify that with two windows, a long press on the overview button tray will |
| // enter splitview. |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| wm::ActivateWindow(window2.get()); |
| ToggleOverview(); |
| ASSERT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| ASSERT_FALSE(split_view_controller()->IsSplitViewModeActive()); |
| |
| LongPressOnOverivewButtonTray(); |
| EXPECT_TRUE(split_view_controller()->IsSplitViewModeActive()); |
| EXPECT_EQ(window2.get(), split_view_controller()->left_window()); |
| } |
| |
| TEST_F(SplitViewControllerTest, LongPressWithUnsnappableWindow) { |
| // Add one unsnappable window and two regular windows. |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> unsnappable_window( |
| CreateNonSnappableWindow(bounds)); |
| ASSERT_FALSE(split_view_controller()->IsSplitViewModeActive()); |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window3(CreateWindow(bounds)); |
| wm::ActivateWindow(window2.get()); |
| wm::ActivateWindow(window3.get()); |
| wm::ActivateWindow(unsnappable_window.get()); |
| ASSERT_EQ(unsnappable_window.get(), wm::GetActiveWindow()); |
| |
| // Verify split view is not activated when long press occurs when active |
| // window is unsnappable. |
| LongPressOnOverivewButtonTray(); |
| EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive()); |
| |
| // Verify split view is not activated when long press occurs in overview mode |
| // and the most recent window is unsnappable. |
| ToggleOverview(); |
| ASSERT_TRUE( |
| Shell::Get()->mru_window_tracker()->BuildWindowForCycleList().size() > 0); |
| ASSERT_EQ(unsnappable_window.get(), |
| Shell::Get()->mru_window_tracker()->BuildWindowForCycleList()[0]); |
| LongPressOnOverivewButtonTray(); |
| EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive()); |
| } |
| |
| // Test the rotation functionalities in split view mode. |
| TEST_F(SplitViewControllerTest, RotationTest) { |
| UpdateDisplay("807x407"); |
| int64_t display_id = display::Screen::GetScreen()->GetPrimaryDisplay().id(); |
| display::DisplayManager* display_manager = Shell::Get()->display_manager(); |
| display::test::ScopedSetInternalDisplayId set_internal(display_manager, |
| display_id); |
| ScreenOrientationControllerTestApi test_api( |
| Shell::Get()->screen_orientation_controller()); |
| |
| // Set the screen orientation to LANDSCAPE_PRIMARY. |
| test_api.SetDisplayRotation(display::Display::ROTATE_0, |
| display::Display::RotationSource::ACTIVE); |
| EXPECT_EQ(test_api.GetCurrentOrientation(), |
| OrientationLockType::kLandscapePrimary); |
| |
| const gfx::Rect bounds(0, 0, 200, 200); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| gfx::Rect bounds_window1 = window1->GetBoundsInScreen(); |
| gfx::Rect bounds_window2 = window2->GetBoundsInScreen(); |
| gfx::Rect bounds_divider = |
| split_view_divider()->GetDividerBoundsInScreen(false /* is_dragging */); |
| |
| // Test |window1|, divider and |window2| are aligned horizontally. |
| // |window1| is on the left, then the divider, and then |window2|. |
| EXPECT_EQ(bounds_divider.x(), bounds_window1.x() + bounds_window1.width()); |
| EXPECT_EQ(bounds_window2.x(), bounds_divider.x() + bounds_divider.width()); |
| EXPECT_EQ(bounds_window1.height(), bounds_divider.height()); |
| EXPECT_EQ(bounds_window1.height(), bounds_window2.height()); |
| |
| // Rotate the screen by 270 degree. |
| test_api.SetDisplayRotation(display::Display::ROTATE_270, |
| display::Display::RotationSource::ACTIVE); |
| EXPECT_EQ(test_api.GetCurrentOrientation(), |
| OrientationLockType::kPortraitPrimary); |
| |
| bounds_window1 = window1->GetBoundsInScreen(); |
| bounds_window2 = window2->GetBoundsInScreen(); |
| bounds_divider = |
| split_view_divider()->GetDividerBoundsInScreen(false /* is_dragging */); |
| |
| // Test that |window1|, divider, |window2| are now aligned vertically. |
| // |window1| is on the top, then the divider, and then |window2|. |
| EXPECT_EQ(bounds_divider.y(), bounds_window1.y() + bounds_window1.height()); |
| EXPECT_EQ(bounds_window2.y(), bounds_divider.y() + bounds_divider.height()); |
| EXPECT_EQ(bounds_window1.width(), bounds_divider.width()); |
| EXPECT_EQ(bounds_window1.width(), bounds_window2.width()); |
| |
| // Rotate the screen by 180 degree. |
| test_api.SetDisplayRotation(display::Display::ROTATE_180, |
| display::Display::RotationSource::ACTIVE); |
| EXPECT_EQ(test_api.GetCurrentOrientation(), |
| OrientationLockType::kLandscapeSecondary); |
| |
| bounds_window1 = window1->GetBoundsInScreen(); |
| bounds_window2 = window2->GetBoundsInScreen(); |
| bounds_divider = |
| split_view_divider()->GetDividerBoundsInScreen(false /* is_dragging */); |
| |
| // Test that |window1|, divider, |window2| are now aligned horizontally. |
| // |window2| is on the left, then the divider, and then |window1|. |
| EXPECT_EQ(bounds_divider.x(), bounds_window2.x() + bounds_window2.width()); |
| EXPECT_EQ(bounds_window1.x(), bounds_divider.x() + bounds_divider.width()); |
| EXPECT_EQ(bounds_window1.height(), bounds_divider.height()); |
| EXPECT_EQ(bounds_window1.height(), bounds_window2.height()); |
| |
| // Rotate the screen by 90 degree. |
| test_api.SetDisplayRotation(display::Display::ROTATE_90, |
| display::Display::RotationSource::ACTIVE); |
| EXPECT_EQ(test_api.GetCurrentOrientation(), |
| OrientationLockType::kPortraitSecondary); |
| bounds_window1 = window1->GetBoundsInScreen(); |
| bounds_window2 = window2->GetBoundsInScreen(); |
| bounds_divider = |
| split_view_divider()->GetDividerBoundsInScreen(false /* is_dragging */); |
| |
| // Test that |window1|, divider, |window2| are now aligned vertically. |
| // |window2| is on the top, then the divider, and then |window1|. |
| EXPECT_EQ(bounds_divider.y(), bounds_window2.y() + bounds_window2.height()); |
| EXPECT_EQ(bounds_window1.y(), bounds_divider.y() + bounds_divider.height()); |
| EXPECT_EQ(bounds_window1.width(), bounds_divider.width()); |
| EXPECT_EQ(bounds_window1.width(), bounds_window2.width()); |
| |
| // Rotate the screen back to 0 degree. |
| test_api.SetDisplayRotation(display::Display::ROTATE_0, |
| display::Display::RotationSource::ACTIVE); |
| EXPECT_EQ(test_api.GetCurrentOrientation(), |
| OrientationLockType::kLandscapePrimary); |
| bounds_window1 = window1->GetBoundsInScreen(); |
| bounds_window2 = window2->GetBoundsInScreen(); |
| bounds_divider = |
| split_view_divider()->GetDividerBoundsInScreen(false /* is_dragging */); |
| |
| // Test |window1|, divider and |window2| are aligned horizontally. |
| // |window1| is on the left, then the divider, and then |window2|. |
| EXPECT_EQ(bounds_divider.x(), bounds_window1.x() + bounds_window1.width()); |
| EXPECT_EQ(bounds_window2.x(), bounds_divider.x() + bounds_divider.width()); |
| EXPECT_EQ(bounds_window1.height(), bounds_divider.height()); |
| EXPECT_EQ(bounds_window1.height(), bounds_window2.height()); |
| } |
| |
| // Test that if the split view mode is active when exiting tablet mode, we |
| // should also end split view mode. |
| TEST_F(SplitViewControllerTest, ExitTabletModeEndSplitView) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| EXPECT_TRUE(split_view_controller()->IsSplitViewModeActive()); |
| |
| Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(false); |
| EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive()); |
| } |
| |
| // Tests that if a window's minimum size is larger than half of the display work |
| // area's size, it can't be snapped. |
| TEST_F(SplitViewControllerTest, SnapWindowWithMinimumSizeTest) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| EXPECT_TRUE(CanSnapInSplitview(window1.get())); |
| |
| const gfx::Rect display_bounds = |
| screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer( |
| window1.get()); |
| aura::test::TestWindowDelegate* delegate = |
| static_cast<aura::test::TestWindowDelegate*>(window1->delegate()); |
| delegate->set_minimum_size( |
| gfx::Size(display_bounds.width() * 0.5f, display_bounds.height())); |
| EXPECT_TRUE(CanSnapInSplitview(window1.get())); |
| |
| delegate->set_minimum_size( |
| gfx::Size(display_bounds.width() * 0.67f, display_bounds.height())); |
| EXPECT_FALSE(CanSnapInSplitview(window1.get())); |
| } |
| |
| // Tests that the snapped window can not be moved outside of work area when its |
| // minimum size is larger than its current desired resizing bounds. |
| TEST_F(SplitViewControllerTest, ResizingSnappedWindowWithMinimumSizeTest) { |
| int64_t display_id = display::Screen::GetScreen()->GetPrimaryDisplay().id(); |
| display::DisplayManager* display_manager = Shell::Get()->display_manager(); |
| display::test::ScopedSetInternalDisplayId set_internal(display_manager, |
| display_id); |
| ScreenOrientationControllerTestApi test_api( |
| Shell::Get()->screen_orientation_controller()); |
| |
| const gfx::Rect bounds(0, 0, 300, 200); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| aura::test::TestWindowDelegate* delegate1 = |
| static_cast<aura::test::TestWindowDelegate*>(window1->delegate()); |
| |
| // Set the screen orientation to LANDSCAPE_PRIMARY |
| test_api.SetDisplayRotation(display::Display::ROTATE_0, |
| display::Display::RotationSource::ACTIVE); |
| EXPECT_EQ(test_api.GetCurrentOrientation(), |
| OrientationLockType::kLandscapePrimary); |
| |
| gfx::Rect display_bounds = |
| screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer( |
| window1.get()); |
| EXPECT_TRUE(CanSnapInSplitview(window1.get())); |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| delegate1->set_minimum_size( |
| gfx::Size(display_bounds.width() * 0.4f, display_bounds.height())); |
| EXPECT_TRUE(window1->layer()->GetTargetTransform().IsIdentity()); |
| |
| gfx::Rect divider_bounds = |
| split_view_divider()->GetDividerBoundsInScreen(false); |
| split_view_controller()->StartResize(divider_bounds.CenterPoint()); |
| gfx::Point resize_point(display_bounds.width() * 0.33f, 0); |
| split_view_controller()->Resize(resize_point); |
| |
| gfx::Rect snapped_window_bounds = |
| split_view_controller()->GetSnappedWindowBoundsInScreen( |
| window1.get(), SplitViewController::LEFT); |
| // The snapped window bounds can't be pushed outside of the display area. |
| EXPECT_EQ(snapped_window_bounds.x(), display_bounds.x()); |
| EXPECT_EQ(snapped_window_bounds.width(), |
| window1->delegate()->GetMinimumSize().width()); |
| EXPECT_FALSE(window1->layer()->GetTargetTransform().IsIdentity()); |
| split_view_controller()->EndResize(resize_point); |
| SkipDividerSnapAnimation(); |
| EndSplitView(); |
| |
| // Rotate the screen by 270 degree. |
| test_api.SetDisplayRotation(display::Display::ROTATE_270, |
| display::Display::RotationSource::ACTIVE); |
| EXPECT_EQ(test_api.GetCurrentOrientation(), |
| OrientationLockType::kPortraitPrimary); |
| |
| display_bounds = |
| screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer( |
| window1.get()); |
| delegate1->set_minimum_size( |
| gfx::Size(display_bounds.width(), display_bounds.height() * 0.4f)); |
| EXPECT_TRUE(CanSnapInSplitview(window1.get())); |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| EXPECT_TRUE(window1->layer()->GetTargetTransform().IsIdentity()); |
| divider_bounds = split_view_divider()->GetDividerBoundsInScreen(false); |
| split_view_controller()->StartResize(divider_bounds.CenterPoint()); |
| resize_point.SetPoint(0, display_bounds.height() * 0.33f); |
| split_view_controller()->Resize(resize_point); |
| |
| snapped_window_bounds = |
| split_view_controller()->GetSnappedWindowBoundsInScreen( |
| window1.get(), SplitViewController::LEFT); |
| EXPECT_EQ(snapped_window_bounds.y(), display_bounds.y()); |
| EXPECT_EQ(snapped_window_bounds.height(), |
| window1->delegate()->GetMinimumSize().height()); |
| EXPECT_FALSE(window1->layer()->GetTargetTransform().IsIdentity()); |
| split_view_controller()->EndResize(resize_point); |
| SkipDividerSnapAnimation(); |
| EndSplitView(); |
| |
| // Rotate the screen by 180 degree. |
| test_api.SetDisplayRotation(display::Display::ROTATE_180, |
| display::Display::RotationSource::ACTIVE); |
| EXPECT_EQ(test_api.GetCurrentOrientation(), |
| OrientationLockType::kLandscapeSecondary); |
| |
| display_bounds = |
| screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer( |
| window1.get()); |
| delegate1->set_minimum_size( |
| gfx::Size(display_bounds.width() * 0.4f, display_bounds.height())); |
| EXPECT_TRUE(CanSnapInSplitview(window1.get())); |
| split_view_controller()->SnapWindow(window1.get(), |
| SplitViewController::RIGHT); |
| EXPECT_TRUE(window1->layer()->GetTargetTransform().IsIdentity()); |
| |
| divider_bounds = split_view_divider()->GetDividerBoundsInScreen(false); |
| split_view_controller()->StartResize(divider_bounds.CenterPoint()); |
| resize_point.SetPoint(display_bounds.width() * 0.33f, 0); |
| split_view_controller()->Resize(resize_point); |
| |
| snapped_window_bounds = |
| split_view_controller()->GetSnappedWindowBoundsInScreen( |
| window1.get(), SplitViewController::RIGHT); |
| EXPECT_EQ(snapped_window_bounds.x(), display_bounds.x()); |
| EXPECT_EQ(snapped_window_bounds.width(), |
| window1->delegate()->GetMinimumSize().width()); |
| EXPECT_FALSE(window1->layer()->GetTargetTransform().IsIdentity()); |
| split_view_controller()->EndResize(resize_point); |
| SkipDividerSnapAnimation(); |
| EndSplitView(); |
| |
| // Rotate the screen by 90 degree. |
| test_api.SetDisplayRotation(display::Display::ROTATE_90, |
| display::Display::RotationSource::ACTIVE); |
| EXPECT_EQ(test_api.GetCurrentOrientation(), |
| OrientationLockType::kPortraitSecondary); |
| |
| display_bounds = |
| screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer( |
| window1.get()); |
| delegate1->set_minimum_size( |
| gfx::Size(display_bounds.width(), display_bounds.height() * 0.4f)); |
| EXPECT_TRUE(CanSnapInSplitview(window1.get())); |
| split_view_controller()->SnapWindow(window1.get(), |
| SplitViewController::RIGHT); |
| EXPECT_TRUE(window1->layer()->GetTargetTransform().IsIdentity()); |
| |
| divider_bounds = split_view_divider()->GetDividerBoundsInScreen(false); |
| split_view_controller()->StartResize(divider_bounds.CenterPoint()); |
| resize_point.SetPoint(0, display_bounds.height() * 0.33f); |
| split_view_controller()->Resize(resize_point); |
| |
| snapped_window_bounds = |
| split_view_controller()->GetSnappedWindowBoundsInScreen( |
| window1.get(), SplitViewController::RIGHT); |
| EXPECT_EQ(snapped_window_bounds.y(), display_bounds.y()); |
| EXPECT_EQ(snapped_window_bounds.height(), |
| window1->delegate()->GetMinimumSize().height()); |
| EXPECT_FALSE(window1->layer()->GetTargetTransform().IsIdentity()); |
| split_view_controller()->EndResize(resize_point); |
| SkipDividerSnapAnimation(); |
| EndSplitView(); |
| } |
| |
| // Tests that the divider should not be moved to a position that is smaller than |
| // the snapped window's minimum size after resizing. |
| TEST_F(SplitViewControllerTest, |
| DividerPositionOnResizingSnappedWindowWithMinimumSizeTest) { |
| const gfx::Rect bounds(0, 0, 200, 200); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| aura::test::TestWindowDelegate* delegate1 = |
| static_cast<aura::test::TestWindowDelegate*>(window1->delegate()); |
| ui::test::EventGenerator* generator = GetEventGenerator(); |
| EXPECT_EQ(OrientationLockType::kLandscapePrimary, |
| GetCurrentScreenOrientation()); |
| gfx::Rect workarea_bounds = |
| screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer( |
| window1.get()); |
| |
| // Snap the divider to one third position when there is only left window with |
| // minimum size larger than one third of the display's width. The divider |
| // should be snapped to the middle position after dragging. |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| delegate1->set_minimum_size( |
| gfx::Size(workarea_bounds.width() * 0.4f, workarea_bounds.height())); |
| gfx::Rect divider_bounds = |
| split_view_divider()->GetDividerBoundsInScreen(false); |
| generator->set_current_screen_location(divider_bounds.CenterPoint()); |
| generator->DragMouseTo(gfx::Point(workarea_bounds.width() * 0.33f, 0)); |
| SkipDividerSnapAnimation(); |
| EXPECT_GT(divider_position(), 0.33f * workarea_bounds.width()); |
| EXPECT_LE(divider_position(), 0.5f * workarea_bounds.width()); |
| |
| // Snap the divider to two third position, it should be kept at there after |
| // dragging. |
| generator->set_current_screen_location(divider_bounds.CenterPoint()); |
| generator->DragMouseTo(gfx::Point(workarea_bounds.width() * 0.67f, 0)); |
| SkipDividerSnapAnimation(); |
| EXPECT_GT(divider_position(), 0.5f * workarea_bounds.width()); |
| EXPECT_LE(divider_position(), 0.67f * workarea_bounds.width()); |
| EndSplitView(); |
| |
| // Snap the divider to two third position when there is only right window with |
| // minium size larger than one third of the display's width. The divider |
| // should be snapped to the middle position after dragging. |
| delegate1->set_minimum_size( |
| gfx::Size(workarea_bounds.width() * 0.4f, workarea_bounds.height())); |
| split_view_controller()->SnapWindow(window1.get(), |
| SplitViewController::RIGHT); |
| divider_bounds = split_view_divider()->GetDividerBoundsInScreen(false); |
| generator->set_current_screen_location(divider_bounds.CenterPoint()); |
| generator->DragMouseTo(gfx::Point(workarea_bounds.width() * 0.67f, 0)); |
| SkipDividerSnapAnimation(); |
| EXPECT_GT(divider_position(), 0.33f * workarea_bounds.width()); |
| EXPECT_LE(divider_position(), 0.5f * workarea_bounds.width()); |
| |
| // Snap the divider to one third position, it should be kept at there after |
| // dragging. |
| generator->set_current_screen_location(divider_bounds.CenterPoint()); |
| generator->DragMouseTo(gfx::Point(workarea_bounds.width() * 0.33f, 0)); |
| SkipDividerSnapAnimation(); |
| EXPECT_GT(divider_position(), 0); |
| EXPECT_LE(divider_position(), 0.33f * workarea_bounds.width()); |
| EndSplitView(); |
| |
| // Snap the divider to one third position when there are both left and right |
| // snapped windows with the same minimum size larger than one third of the |
| // display's width. The divider should be snapped to the middle position after |
| // dragging. |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| aura::test::TestWindowDelegate* delegate2 = |
| static_cast<aura::test::TestWindowDelegate*>(window2->delegate()); |
| delegate2->set_minimum_size( |
| gfx::Size(workarea_bounds.width() * 0.4f, workarea_bounds.height())); |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| divider_bounds = split_view_divider()->GetDividerBoundsInScreen(false); |
| generator->set_current_screen_location(divider_bounds.CenterPoint()); |
| generator->DragMouseTo(gfx::Point(workarea_bounds.width() * 0.33f, 0)); |
| SkipDividerSnapAnimation(); |
| EXPECT_GT(divider_position(), 0.33f * workarea_bounds.width()); |
| EXPECT_LE(divider_position(), 0.5f * workarea_bounds.width()); |
| |
| // Snap the divider to two third position, it should be snapped to the middle |
| // position after dragging. |
| generator->set_current_screen_location(divider_bounds.CenterPoint()); |
| generator->DragMouseTo(gfx::Point(workarea_bounds.width() * 0.67f, 0)); |
| SkipDividerSnapAnimation(); |
| EXPECT_GT(divider_position(), 0.33f * workarea_bounds.width()); |
| EXPECT_LE(divider_position(), 0.5f * workarea_bounds.width()); |
| EndSplitView(); |
| } |
| |
| // Tests that the divider and snapped windows bounds should be updated if |
| // snapping a new window with minimum size, which is larger than the bounds |
| // of its snap position. |
| TEST_F(SplitViewControllerTest, |
| DividerPositionWithWindowMinimumSizeOnSnapTest) { |
| const gfx::Rect bounds(0, 0, 200, 300); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| const gfx::Rect workarea_bounds = |
| screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer( |
| window1.get()); |
| |
| // Divider should be moved to the middle at the beginning. |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| ASSERT_TRUE(split_view_divider()); |
| EXPECT_GT(divider_position(), 0.33f * workarea_bounds.width()); |
| EXPECT_LE(divider_position(), 0.5f * workarea_bounds.width()); |
| |
| // Drag the divider to two-third position. |
| ui::test::EventGenerator* generator = GetEventGenerator(); |
| gfx::Rect divider_bounds = |
| split_view_divider()->GetDividerBoundsInScreen(false); |
| generator->set_current_screen_location(divider_bounds.CenterPoint()); |
| generator->DragMouseTo(gfx::Point(workarea_bounds.width() * 0.67f, 0)); |
| SkipDividerSnapAnimation(); |
| EXPECT_GT(divider_position(), 0.5f * workarea_bounds.width()); |
| EXPECT_LE(divider_position(), 0.67f * workarea_bounds.width()); |
| |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| aura::test::TestWindowDelegate* delegate2 = |
| static_cast<aura::test::TestWindowDelegate*>(window2->delegate()); |
| delegate2->set_minimum_size( |
| gfx::Size(workarea_bounds.width() * 0.4f, workarea_bounds.height())); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| EXPECT_GT(divider_position(), 0.33f * workarea_bounds.width()); |
| EXPECT_LE(divider_position(), 0.5f * workarea_bounds.width()); |
| } |
| |
| // Test that if display configuration changes in lock screen, the split view |
| // mode doesn't end. |
| TEST_F(SplitViewControllerTest, DoNotEndSplitViewInLockScreen) { |
| display::test::DisplayManagerTestApi(display_manager()) |
| .SetFirstDisplayAsInternalDisplay(); |
| UpdateDisplay("800x400"); |
| const gfx::Rect bounds(0, 0, 200, 300); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| EXPECT_TRUE(split_view_controller()->IsSplitViewModeActive()); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::BOTH_SNAPPED); |
| |
| // Now lock the screen. |
| Shell::Get()->session_controller()->LockScreenAndFlushForTest(); |
| // Change display configuration. Split view mode is still active. |
| UpdateDisplay("400x800"); |
| EXPECT_TRUE(split_view_controller()->IsSplitViewModeActive()); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::BOTH_SNAPPED); |
| |
| // Now unlock the screen. |
| GetSessionControllerClient()->UnlockScreen(); |
| EXPECT_TRUE(split_view_controller()->IsSplitViewModeActive()); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::BOTH_SNAPPED); |
| } |
| |
| // Test that when split view and overview are both active when a new window is |
| // added to the window hierarchy, overview is not ended. |
| TEST_F(SplitViewControllerTest, NewWindowTest) { |
| const gfx::Rect bounds(0, 0, 200, 300); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| ToggleOverview(); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::LEFT_SNAPPED); |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| |
| // Now new a window. Test it won't end the overview mode |
| std::unique_ptr<aura::Window> window3(CreateWindow(bounds)); |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| } |
| |
| // Tests that when split view ends because of a transition from tablet mode to |
| // laptop mode during a resize operation, drags are properly completed. |
| TEST_F(SplitViewControllerTest, ExitTabletModeDuringResizeCompletesDrags) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| auto* w1_state = wm::GetWindowState(window1.get()); |
| auto* w2_state = wm::GetWindowState(window2.get()); |
| |
| // Setup delegates |
| auto* window_state_delegate1 = new TestWindowStateDelegate(); |
| auto* window_state_delegate2 = new TestWindowStateDelegate(); |
| w1_state->SetDelegate(base::WrapUnique(window_state_delegate1)); |
| w2_state->SetDelegate(base::WrapUnique(window_state_delegate2)); |
| |
| // Set up windows. |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| |
| // Start a drag but don't release the mouse button. |
| gfx::Rect divider_bounds = |
| split_view_divider()->GetDividerBoundsInScreen(false /* is_dragging */); |
| const int screen_width = |
| screen_util::GetDisplayWorkAreaBoundsInParent(window1.get()).width(); |
| GetEventGenerator()->set_current_screen_location( |
| divider_bounds.CenterPoint()); |
| GetEventGenerator()->PressLeftButton(); |
| GetEventGenerator()->MoveMouseTo(screen_width * 0.67f, 0); |
| |
| // Drag is started for both windows. |
| EXPECT_TRUE(window_state_delegate1->drag_in_progress()); |
| EXPECT_TRUE(window_state_delegate2->drag_in_progress()); |
| EXPECT_NE(nullptr, w1_state->drag_details()); |
| EXPECT_NE(nullptr, w2_state->drag_details()); |
| |
| // End tablet mode. |
| Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(false); |
| |
| // Drag is ended for both windows. |
| EXPECT_EQ(nullptr, w1_state->drag_details()); |
| EXPECT_EQ(nullptr, w2_state->drag_details()); |
| EXPECT_FALSE(window_state_delegate1->drag_in_progress()); |
| EXPECT_FALSE(window_state_delegate2->drag_in_progress()); |
| } |
| |
| // Tests that when a single window is present in split view mode is minimized |
| // during a resize operation, then drags are properly completed. |
| TEST_F(SplitViewControllerTest, |
| MinimizeSingleWindowDuringResizeCompletesDrags) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| auto* w1_state = wm::GetWindowState(window1.get()); |
| |
| // Setup delegate |
| auto* window_state_delegate1 = new TestWindowStateDelegate(); |
| w1_state->SetDelegate(base::WrapUnique(window_state_delegate1)); |
| |
| // Set up window. |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| |
| // Start a drag but don't release the mouse button. |
| gfx::Rect divider_bounds = |
| split_view_divider()->GetDividerBoundsInScreen(false /* is_dragging */); |
| const int screen_width = |
| screen_util::GetDisplayWorkAreaBoundsInParentForDefaultContainer( |
| window1.get()) |
| .width(); |
| GetEventGenerator()->set_current_screen_location( |
| divider_bounds.CenterPoint()); |
| GetEventGenerator()->PressLeftButton(); |
| GetEventGenerator()->MoveMouseTo(screen_width * 0.67f, 0); |
| |
| // Drag is started. |
| EXPECT_TRUE(window_state_delegate1->drag_in_progress()); |
| EXPECT_NE(nullptr, w1_state->drag_details()); |
| |
| // Minimize the window. |
| wm::WMEvent minimize_event(wm::WM_EVENT_MINIMIZE); |
| wm::GetWindowState(window1.get())->OnWMEvent(&minimize_event); |
| |
| // Drag is ended. |
| EXPECT_FALSE(window_state_delegate1->drag_in_progress()); |
| EXPECT_EQ(nullptr, w1_state->drag_details()); |
| } |
| |
| // Tests that when two windows are present in split view mode and one of them |
| // is minimized during a resize, then drags are properly completed. |
| TEST_F(SplitViewControllerTest, |
| MinimizeOneOfTwoWindowsDuringResizeCompletesDrags) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| auto* w1_state = wm::GetWindowState(window1.get()); |
| auto* w2_state = wm::GetWindowState(window2.get()); |
| |
| // Setup delegates |
| auto* window_state_delegate1 = new TestWindowStateDelegate(); |
| auto* window_state_delegate2 = new TestWindowStateDelegate(); |
| w1_state->SetDelegate(base::WrapUnique(window_state_delegate1)); |
| w2_state->SetDelegate(base::WrapUnique(window_state_delegate2)); |
| |
| // Set up windows. |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| |
| // Start a drag but don't release the mouse button. |
| gfx::Rect divider_bounds = |
| split_view_divider()->GetDividerBoundsInScreen(false /* is_dragging */); |
| const int screen_width = |
| screen_util::GetDisplayWorkAreaBoundsInParent(window1.get()).width(); |
| GetEventGenerator()->set_current_screen_location( |
| divider_bounds.CenterPoint()); |
| GetEventGenerator()->PressLeftButton(); |
| GetEventGenerator()->MoveMouseTo(screen_width * 0.67f, 0); |
| |
| // Drag is started for both windows. |
| EXPECT_TRUE(window_state_delegate1->drag_in_progress()); |
| EXPECT_TRUE(window_state_delegate2->drag_in_progress()); |
| EXPECT_NE(nullptr, w1_state->drag_details()); |
| EXPECT_NE(nullptr, w2_state->drag_details()); |
| |
| // Minimize the left window. |
| wm::WMEvent minimize_event(wm::WM_EVENT_MINIMIZE); |
| wm::GetWindowState(window1.get())->OnWMEvent(&minimize_event); |
| |
| // Drag is ended just for the left window. |
| EXPECT_FALSE(window_state_delegate1->drag_in_progress()); |
| EXPECT_TRUE(window_state_delegate2->drag_in_progress()); |
| EXPECT_EQ(nullptr, w1_state->drag_details()); |
| EXPECT_NE(nullptr, w2_state->drag_details()); |
| } |
| |
| // Test that when a snapped window's resizablity property change from resizable |
| // to unresizable, the split view mode is ended. |
| TEST_F(SplitViewControllerTest, ResizabilityChangeTest) { |
| const gfx::Rect bounds(0, 0, 200, 300); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| EXPECT_TRUE(split_view_controller()->IsSplitViewModeActive()); |
| |
| window1->SetProperty(aura::client::kResizeBehaviorKey, |
| ws::mojom::kResizeBehaviorNone); |
| EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive()); |
| } |
| |
| // Tests that shadows on windows disappear when the window is snapped, and |
| // reappear when unsnapped. |
| TEST_F(SplitViewControllerTest, ShadowDisappearsWhenSnapped) { |
| const gfx::Rect bounds(200, 200); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window3(CreateWindow(bounds)); |
| |
| ::wm::ShadowController* shadow_controller = Shell::Get()->shadow_controller(); |
| EXPECT_TRUE(shadow_controller->IsShadowVisibleForWindow(window1.get())); |
| EXPECT_TRUE(shadow_controller->IsShadowVisibleForWindow(window2.get())); |
| EXPECT_TRUE(shadow_controller->IsShadowVisibleForWindow(window3.get())); |
| |
| // Snap |window1| to the left. Its shadow should disappear. |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| EXPECT_FALSE(shadow_controller->IsShadowVisibleForWindow(window1.get())); |
| EXPECT_TRUE(shadow_controller->IsShadowVisibleForWindow(window2.get())); |
| EXPECT_TRUE(shadow_controller->IsShadowVisibleForWindow(window3.get())); |
| |
| // Snap |window2| to the right. Its shadow should also disappear. |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| EXPECT_FALSE(shadow_controller->IsShadowVisibleForWindow(window1.get())); |
| EXPECT_FALSE(shadow_controller->IsShadowVisibleForWindow(window2.get())); |
| EXPECT_TRUE(shadow_controller->IsShadowVisibleForWindow(window3.get())); |
| |
| // Snap |window3| to the right. Its shadow should disappear and |window2|'s |
| // shadow should reappear. |
| split_view_controller()->SnapWindow(window3.get(), |
| SplitViewController::RIGHT); |
| EXPECT_FALSE(shadow_controller->IsShadowVisibleForWindow(window1.get())); |
| EXPECT_TRUE(shadow_controller->IsShadowVisibleForWindow(window2.get())); |
| EXPECT_FALSE(shadow_controller->IsShadowVisibleForWindow(window3.get())); |
| } |
| |
| // Tests that if snapping a window causes overview to end (e.g., select two |
| // windows in overview mode to snap to both side of the screen), or toggle |
| // overview to end overview causes a window to snap, we should not have the |
| // exiting animation. |
| TEST_F(SplitViewControllerTest, OverviewExitAnimationTest) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window3(CreateWindow(bounds)); |
| |
| // 1) For normal toggle overview case, we should have animation when |
| // exiting overview. |
| std::unique_ptr<OverviewStatesObserver> overview_observer = |
| std::make_unique<OverviewStatesObserver>(window1->GetRootWindow()); |
| ToggleOverview(); |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| ToggleOverview(); |
| EXPECT_FALSE(Shell::Get()->overview_controller()->IsSelecting()); |
| EXPECT_TRUE(overview_observer->overview_animate_when_exiting()); |
| |
| // 2) If overview is ended because of activating a window: |
| ToggleOverview(); |
| // It will end overview. |
| wm::ActivateWindow(window1.get()); |
| EXPECT_FALSE(Shell::Get()->overview_controller()->IsSelecting()); |
| EXPECT_TRUE(overview_observer->overview_animate_when_exiting()); |
| |
| // 3) If overview is ended because of snapping a window: |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| // Reset the observer as we'll need the OverviewStatesObserver to be added to |
| // to ShellObserver list after SplitViewController. |
| overview_observer.reset(new OverviewStatesObserver(window1->GetRootWindow())); |
| ToggleOverview(); // Start overview. |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| // Test |overview_animate_when_exiting_| has been properly reset. |
| EXPECT_TRUE(overview_observer->overview_animate_when_exiting()); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| EXPECT_FALSE(Shell::Get()->overview_controller()->IsSelecting()); |
| EXPECT_FALSE(overview_observer->overview_animate_when_exiting()); |
| |
| // 4) If ending overview causes a window to snap: |
| ToggleOverview(); |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| // Test |overview_animate_when_exiting_| has been properly reset. |
| EXPECT_TRUE(overview_observer->overview_animate_when_exiting()); |
| ToggleOverview(); |
| EXPECT_FALSE(Shell::Get()->overview_controller()->IsSelecting()); |
| EXPECT_FALSE(overview_observer->overview_animate_when_exiting()); |
| } |
| |
| // Test the window state is normally maximized on splitview end, except when we |
| // end it from home launcher. |
| TEST_F(SplitViewControllerTest, WindowStateOnExit) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| |
| using svc = SplitViewController; |
| // Tests that normally, window will maximize on splitview ended. |
| split_view_controller()->SnapWindow(window1.get(), svc::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), svc::RIGHT); |
| split_view_controller()->EndSplitView(); |
| EXPECT_TRUE(wm::GetWindowState(window1.get())->IsMaximized()); |
| EXPECT_TRUE(wm::GetWindowState(window2.get())->IsMaximized()); |
| |
| // Tests that if we end splitview from home launcher, the windows do not get |
| // maximized. |
| split_view_controller()->SnapWindow(window1.get(), svc::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), svc::RIGHT); |
| split_view_controller()->EndSplitView( |
| SplitViewController::EndReason::kHomeLauncherPressed); |
| EXPECT_FALSE(wm::GetWindowState(window1.get())->IsMaximized()); |
| EXPECT_FALSE(wm::GetWindowState(window2.get())->IsMaximized()); |
| } |
| |
| // Test that if overview and splitview are both active at the same time, |
| // activiate an unsnappable window should end both overview and splitview mode. |
| TEST_F(SplitViewControllerTest, ActivateNonSnappableWindow) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window3(CreateNonSnappableWindow(bounds)); |
| |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| ToggleOverview(); |
| EXPECT_TRUE(split_view_controller()->IsSplitViewModeActive()); |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| |
| wm::ActivateWindow(window3.get()); |
| EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive()); |
| EXPECT_FALSE(Shell::Get()->overview_controller()->IsSelecting()); |
| } |
| |
| // Tests that if a snapped window has a bubble transient child, the bubble's |
| // bounds should always align with the snapped window's bounds. |
| TEST_F(SplitViewControllerTest, AdjustTransientChildBounds) { |
| std::unique_ptr<views::Widget> widget(CreateTestWidget()); |
| aura::Window* window = widget->GetNativeWindow(); |
| window->SetProperty(aura::client::kResizeBehaviorKey, |
| ws::mojom::kResizeBehaviorCanResize | |
| ws::mojom::kResizeBehaviorCanMaximize); |
| split_view_controller()->SnapWindow(window, SplitViewController::LEFT); |
| const gfx::Rect window_bounds = window->GetBoundsInScreen(); |
| |
| // Create a bubble widget that's anchored to |widget|. |
| views::Widget* bubble_widget = views::BubbleDialogDelegateView::CreateBubble( |
| new TestBubbleDialogDelegateView(widget->GetContentsView())); |
| aura::Window* bubble_window = bubble_widget->GetNativeWindow(); |
| EXPECT_TRUE(::wm::HasTransientAncestor(bubble_window, window)); |
| // Test that the bubble is created inside its anchor widget. |
| EXPECT_TRUE(window_bounds.Contains(bubble_window->GetBoundsInScreen())); |
| |
| // Now try to manually move the bubble out of the snapped window. |
| bubble_window->SetBoundsInScreen( |
| split_view_controller()->GetSnappedWindowBoundsInScreen( |
| window, SplitViewController::RIGHT), |
| display::Screen::GetScreen()->GetDisplayNearestWindow(window)); |
| // Test that the bubble can't be moved outside of its anchor widget. |
| EXPECT_TRUE(window_bounds.Contains(bubble_window->GetBoundsInScreen())); |
| EndSplitView(); |
| } |
| |
| // Tests the divider closest position ratio if work area is not starts from the |
| // top of the display. |
| TEST_F(SplitViewControllerTest, DividerClosestRatioOnWorkArea) { |
| UpdateDisplay("1200x800"); |
| // Docked magnifier will put a view port window on the top of the display. |
| Shell::Get()->docked_magnifier_controller()->SetEnabled(true); |
| |
| int64_t display_id = display::Screen::GetScreen()->GetPrimaryDisplay().id(); |
| display::DisplayManager* display_manager = Shell::Get()->display_manager(); |
| display::test::ScopedSetInternalDisplayId set_internal(display_manager, |
| display_id); |
| ScreenOrientationControllerTestApi test_api( |
| Shell::Get()->screen_orientation_controller()); |
| ui::test::EventGenerator* generator = GetEventGenerator(); |
| ASSERT_EQ(OrientationLockType::kLandscapePrimary, |
| test_api.GetCurrentOrientation()); |
| |
| const gfx::Rect bounds(0, 0, 200, 200); |
| std::unique_ptr<aura::Window> window(CreateWindow(bounds)); |
| split_view_controller()->SnapWindow(window.get(), SplitViewController::LEFT); |
| |
| test_api.SetDisplayRotation(display::Display::ROTATE_90, |
| display::Display::RotationSource::ACTIVE); |
| EXPECT_EQ(OrientationLockType::kPortraitSecondary, |
| test_api.GetCurrentOrientation()); |
| EXPECT_EQ(divider_closest_ratio(), 0.5f); |
| |
| test_api.SetDisplayRotation(display::Display::ROTATE_0, |
| display::Display::RotationSource::ACTIVE); |
| EXPECT_EQ(OrientationLockType::kLandscapePrimary, |
| test_api.GetCurrentOrientation()); |
| EXPECT_EQ(divider_closest_ratio(), 0.5f); |
| gfx::Rect divider_bounds = |
| split_view_divider()->GetDividerBoundsInScreen(false); |
| gfx::Rect workarea_bounds = |
| screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer( |
| window.get()); |
| generator->set_current_screen_location(divider_bounds.CenterPoint()); |
| // Drag the divider to one third position of the work area's width. |
| generator->DragMouseTo( |
| gfx::Point(workarea_bounds.width() * 0.33f, workarea_bounds.y())); |
| SkipDividerSnapAnimation(); |
| EXPECT_EQ(divider_closest_ratio(), 0.33f); |
| |
| // Divider closest position ratio changed from one third to two thirds if |
| // left/top window changes. |
| test_api.SetDisplayRotation(display::Display::ROTATE_90, |
| display::Display::RotationSource::ACTIVE); |
| EXPECT_EQ(OrientationLockType::kPortraitSecondary, |
| test_api.GetCurrentOrientation()); |
| EXPECT_EQ(divider_closest_ratio(), 0.67f); |
| |
| // Divider closest position ratio is kept as one third if left/top window |
| // doesn't changes. |
| test_api.SetDisplayRotation(display::Display::ROTATE_270, |
| display::Display::RotationSource::ACTIVE); |
| EXPECT_EQ(OrientationLockType::kPortraitPrimary, |
| test_api.GetCurrentOrientation()); |
| EXPECT_EQ(divider_closest_ratio(), 0.33f); |
| } |
| |
| // Test that if we snap an always on top window in splitscreen, there should be |
| // no crash and the window should stay always on top. |
| TEST_F(SplitViewControllerTest, AlwaysOnTopWindow) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> always_on_top_window(CreateWindow(bounds)); |
| always_on_top_window->SetProperty(aura::client::kAlwaysOnTopKey, true); |
| std::unique_ptr<aura::Window> normal_window(CreateWindow(bounds)); |
| |
| split_view_controller()->SnapWindow(always_on_top_window.get(), |
| SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(normal_window.get(), |
| SplitViewController::RIGHT); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::BOTH_SNAPPED); |
| EXPECT_TRUE(always_on_top_window->GetProperty(aura::client::kAlwaysOnTopKey)); |
| |
| wm::ActivateWindow(always_on_top_window.get()); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::BOTH_SNAPPED); |
| EXPECT_TRUE(always_on_top_window->GetProperty(aura::client::kAlwaysOnTopKey)); |
| |
| wm::ActivateWindow(normal_window.get()); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::BOTH_SNAPPED); |
| EXPECT_TRUE(always_on_top_window->GetProperty(aura::client::kAlwaysOnTopKey)); |
| } |
| |
| // Test that pinning a window ends split view mode. |
| TEST_F(SplitViewControllerTest, PinningWindowEndsSplitView) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| EXPECT_TRUE(split_view_controller()->IsSplitViewModeActive()); |
| |
| wm::PinWindow(window1.get(), true); |
| EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive()); |
| } |
| |
| // Test that split view mode is disallowed while we're in pinned mode (there is |
| // a pinned window). |
| TEST_F(SplitViewControllerTest, PinnedWindowDisallowsSplitView) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| |
| EXPECT_TRUE(ShouldAllowSplitView()); |
| |
| wm::PinWindow(window1.get(), true); |
| EXPECT_FALSE(ShouldAllowSplitView()); |
| } |
| |
| // Test that if split view ends while the divider is dragged to where a snapped |
| // window is sliding off the screen because it has reached minimum size, then |
| // the offset is cleared. |
| TEST_F(SplitViewControllerTest, EndSplitViewWhileResizingBeyondMinimum) { |
| int64_t display_id = display::Screen::GetScreen()->GetPrimaryDisplay().id(); |
| display::DisplayManager* display_manager = Shell::Get()->display_manager(); |
| display::test::ScopedSetInternalDisplayId set_internal(display_manager, |
| display_id); |
| ScreenOrientationControllerTestApi test_api( |
| Shell::Get()->screen_orientation_controller()); |
| |
| const gfx::Rect bounds(0, 0, 300, 200); |
| std::unique_ptr<aura::Window> window(CreateWindow(bounds)); |
| aura::test::TestWindowDelegate* delegate = |
| static_cast<aura::test::TestWindowDelegate*>(window->delegate()); |
| |
| // Set the screen orientation to LANDSCAPE_PRIMARY |
| test_api.SetDisplayRotation(display::Display::ROTATE_0, |
| display::Display::RotationSource::ACTIVE); |
| |
| gfx::Rect display_bounds = |
| screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer( |
| window.get()); |
| split_view_controller()->SnapWindow(window.get(), SplitViewController::LEFT); |
| delegate->set_minimum_size( |
| gfx::Size(display_bounds.width() * 0.4f, display_bounds.height())); |
| |
| gfx::Rect divider_bounds = |
| split_view_divider()->GetDividerBoundsInScreen(false); |
| split_view_controller()->StartResize(divider_bounds.CenterPoint()); |
| gfx::Point resize_point(display_bounds.width() * 0.33f, 0); |
| split_view_controller()->Resize(resize_point); |
| ASSERT_FALSE(window->layer()->GetTargetTransform().IsIdentity()); |
| EndSplitView(); |
| EXPECT_TRUE(window->layer()->GetTargetTransform().IsIdentity()); |
| } |
| |
| // Test that if split view ends during the divider snap animation while a |
| // snapped window is sliding off the screen because it has reached minimum size, |
| // then the animation is ended and the window offset is cleared. |
| TEST_F(SplitViewControllerTest, EndSplitViewDuringDividerSnapAnimation) { |
| int64_t display_id = display::Screen::GetScreen()->GetPrimaryDisplay().id(); |
| display::DisplayManager* display_manager = Shell::Get()->display_manager(); |
| display::test::ScopedSetInternalDisplayId set_internal(display_manager, |
| display_id); |
| ScreenOrientationControllerTestApi test_api( |
| Shell::Get()->screen_orientation_controller()); |
| |
| const gfx::Rect bounds(0, 0, 300, 200); |
| std::unique_ptr<aura::Window> window(CreateWindow(bounds)); |
| aura::test::TestWindowDelegate* delegate = |
| static_cast<aura::test::TestWindowDelegate*>(window->delegate()); |
| |
| // Set the screen orientation to LANDSCAPE_PRIMARY |
| test_api.SetDisplayRotation(display::Display::ROTATE_0, |
| display::Display::RotationSource::ACTIVE); |
| |
| gfx::Rect display_bounds = |
| screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer( |
| window.get()); |
| split_view_controller()->SnapWindow(window.get(), SplitViewController::LEFT); |
| delegate->set_minimum_size( |
| gfx::Size(display_bounds.width() * 0.4f, display_bounds.height())); |
| |
| gfx::Rect divider_bounds = |
| split_view_divider()->GetDividerBoundsInScreen(false); |
| split_view_controller()->StartResize(divider_bounds.CenterPoint()); |
| gfx::Point resize_point((int)(display_bounds.width() * 0.33f) + 20, 0); |
| split_view_controller()->Resize(resize_point); |
| split_view_controller()->EndResize(resize_point); |
| ASSERT_TRUE(IsDividerAnimating()); |
| ASSERT_FALSE(window->layer()->GetTargetTransform().IsIdentity()); |
| EndSplitView(); |
| EXPECT_FALSE(IsDividerAnimating()); |
| EXPECT_TRUE(window->layer()->GetTargetTransform().IsIdentity()); |
| } |
| |
| // TestOverviewObserver which tracks how many overview items there are when |
| // overview mode is about to end. |
| class TestOverviewItemsOnOverviewModeEndObserver : public OverviewObserver { |
| public: |
| TestOverviewItemsOnOverviewModeEndObserver() { |
| Shell::Get()->overview_controller()->AddObserver(this); |
| } |
| ~TestOverviewItemsOnOverviewModeEndObserver() override { |
| Shell::Get()->overview_controller()->RemoveObserver(this); |
| } |
| void OnOverviewModeEnding(OverviewSession* overview_session) override { |
| items_on_last_overview_end_ = overview_session->num_items_for_testing(); |
| } |
| int items_on_last_overview_end() const { return items_on_last_overview_end_; } |
| |
| private: |
| int items_on_last_overview_end_ = 0; |
| |
| DISALLOW_COPY_AND_ASSIGN(TestOverviewItemsOnOverviewModeEndObserver); |
| }; |
| |
| TEST_F(SplitViewControllerTest, ItemsRemovedFromOverviewOnSnap) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); |
| |
| ToggleOverview(); |
| ASSERT_EQ(2u, Shell::Get() |
| ->overview_controller() |
| ->overview_session() |
| ->num_items_for_testing()); |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| ASSERT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| EXPECT_EQ(1u, Shell::Get() |
| ->overview_controller() |
| ->overview_session() |
| ->num_items_for_testing()); |
| |
| // Create |observer| after splitview is entered so that it gets notified after |
| // splitview does, and so will notice the changes splitview made to overview |
| // on overview end. |
| TestOverviewItemsOnOverviewModeEndObserver observer; |
| ToggleOverview(); |
| EXPECT_EQ(0, observer.items_on_last_overview_end()); |
| } |
| |
| // Test that resizing ends properly if split view ends during divider dragging. |
| TEST_F(SplitViewControllerTest, EndSplitViewWhileDragging) { |
| // Enter split view mode. |
| std::unique_ptr<aura::Window> window = CreateTestWindow(); |
| split_view_controller()->SnapWindow(window.get(), SplitViewController::LEFT); |
| |
| // Start resizing. |
| gfx::Rect divider_bounds = |
| split_view_divider()->GetDividerBoundsInScreen(false); |
| split_view_controller()->StartResize(divider_bounds.CenterPoint()); |
| |
| // Verify the setup. |
| ASSERT_TRUE(split_view_controller()->IsSplitViewModeActive()); |
| ASSERT_TRUE(split_view_controller()->is_resizing()); |
| |
| // End split view and check that resizing has ended properly. |
| split_view_controller()->EndSplitView(); |
| EXPECT_FALSE(split_view_controller()->is_resizing()); |
| } |
| |
| // Test the tab-dragging related functionalities in tablet mode. Tab(s) can be |
| // dragged out of a window and then put in split view mode or merge into another |
| // window. |
| class SplitViewTabDraggingTest : public SplitViewControllerTest { |
| public: |
| SplitViewTabDraggingTest() = default; |
| ~SplitViewTabDraggingTest() override = default; |
| |
| protected: |
| aura::Window* CreateWindowWithType( |
| const gfx::Rect& bounds, |
| AppType app_type, |
| aura::client::WindowType window_type = aura::client::WINDOW_TYPE_NORMAL) { |
| aura::Window* window = CreateTestWindowInShellWithDelegateAndType( |
| new SplitViewTestWindowDelegate, window_type, -1, bounds); |
| window->SetProperty(aura::client::kAppType, static_cast<int>(app_type)); |
| wm::GetWindowState(window)->Maximize(); |
| return window; |
| } |
| |
| // Starts tab dragging on |dragged_window|. |source_window| indicates which |
| // window the drag originates from. Returns the newly created WindowResizer |
| // for the |dragged_window|. |
| std::unique_ptr<WindowResizer> StartDrag(aura::Window* dragged_window, |
| aura::Window* source_window) { |
| SetIsInTabDragging(dragged_window, /*is_dragging=*/true, source_window); |
| std::unique_ptr<WindowResizer> resizer = CreateResizerForTest( |
| dragged_window, dragged_window->bounds().origin(), HTCAPTION); |
| GetBrowserWindowDragController(resizer.get()) |
| ->drag_delegate_for_testing() |
| ->set_drag_start_deadline_for_testing(base::Time::Now()); |
| return resizer; |
| } |
| |
| // Drags the window to |end_position|. |
| void DragWindowTo(WindowResizer* resizer, const gfx::Point& end_position) { |
| ASSERT_TRUE(resizer); |
| resizer->Drag(end_position, 0); |
| } |
| |
| // Drags the window with offest (delta_x, delta_y) to its initial position. |
| void DragWindowWithOffset(WindowResizer* resizer, int delta_x, int delta_y) { |
| ASSERT_TRUE(resizer); |
| gfx::Point location = resizer->GetInitialLocation(); |
| location.set_x(location.x() + delta_x); |
| location.set_y(location.y() + delta_y); |
| resizer->Drag(location, 0); |
| } |
| |
| // Ends the drag. |resizer| will be deleted after exiting this function. |
| void CompleteDrag(std::unique_ptr<WindowResizer> resizer) { |
| ASSERT_TRUE(resizer.get()); |
| resizer->CompleteDrag(); |
| SetIsInTabDragging(resizer->GetTarget(), /*is_dragging=*/false); |
| } |
| |
| // Fling to end the drag. |resizer| will be deleted after exiting this |
| // function. |
| void Fling(std::unique_ptr<WindowResizer> resizer, float velocity_y) { |
| ASSERT_TRUE(resizer.get()); |
| aura::Window* target_window = resizer->GetTarget(); |
| base::TimeTicks timestamp = base::TimeTicks::Now(); |
| ui::GestureEventDetails details = |
| ui::GestureEventDetails(ui::ET_SCROLL_FLING_START, 0.f, velocity_y); |
| ui::GestureEvent event = ui::GestureEvent( |
| target_window->bounds().origin().x(), |
| target_window->bounds().origin().y(), ui::EF_NONE, timestamp, details); |
| ui::Event::DispatcherApi(&event).set_target(target_window); |
| resizer->FlingOrSwipe(&event); |
| SetIsInTabDragging(resizer->GetTarget(), /*is_dragging=*/false); |
| } |
| |
| std::unique_ptr<WindowResizer> CreateResizerForTest( |
| aura::Window* window, |
| const gfx::Point& point_in_parent, |
| int window_component, |
| ::wm::WindowMoveSource source = ::wm::WINDOW_MOVE_SOURCE_TOUCH) { |
| return CreateWindowResizer(window, point_in_parent, window_component, |
| source); |
| } |
| |
| // Sets if |dragged_window| is currently in tab-dragging process. |
| // |source_window| is the window that the drag originates from. This method is |
| // used to simulate the start/stop of a window's tab-dragging by setting the |
| // two window properties, which are usually set in TabDragController:: |
| // UpdateTabDraggingInfo() function. |
| void SetIsInTabDragging(aura::Window* dragged_window, |
| bool is_dragging, |
| aura::Window* source_window = nullptr) { |
| if (!is_dragging) { |
| dragged_window->ClearProperty(kIsDraggingTabsKey); |
| dragged_window->ClearProperty(kTabDraggingSourceWindowKey); |
| } else { |
| dragged_window->SetProperty(kIsDraggingTabsKey, is_dragging); |
| if (source_window != dragged_window) |
| dragged_window->SetProperty(kTabDraggingSourceWindowKey, source_window); |
| } |
| } |
| |
| TabletModeWindowDragController* GetBrowserWindowDragController( |
| WindowResizer* resizer) { |
| WindowResizer* real_window_resizer; |
| // TODO(xdai): This piece of codes seems knowing too much impl details about |
| // WindowResizer. Revisit the logic here later to see if there is anything |
| // we can do to simplify the logic and hide impl details. |
| real_window_resizer = static_cast<DragWindowResizer*>(resizer) |
| ->next_window_resizer_for_testing(); |
| return static_cast<TabletModeWindowDragController*>(real_window_resizer); |
| } |
| |
| IndicatorState GetIndicatorState(WindowResizer* resizer) { |
| return GetBrowserWindowDragController(resizer) |
| ->drag_delegate_for_testing() |
| ->split_view_drag_indicators_for_testing() |
| ->current_indicator_state(); |
| } |
| |
| int GetIndicatorsThreshold(aura::Window* dragged_window) { |
| static const float kIndicatorsThresholdRatio = 0.1f; |
| const gfx::Rect work_area_bounds = |
| display::Screen::GetScreen() |
| ->GetDisplayNearestWindow(dragged_window) |
| .work_area(); |
| return work_area_bounds.y() + |
| work_area_bounds.height() * kIndicatorsThresholdRatio; |
| } |
| |
| gfx::Rect GetDropTargetBoundsDuringDrag(aura::Window* window) const { |
| OverviewSession* overview_session = |
| Shell::Get()->overview_controller()->overview_session(); |
| DCHECK(overview_session); |
| OverviewGrid* current_grid = |
| overview_session->GetGridWithRootWindow(window->GetRootWindow()); |
| DCHECK(current_grid); |
| |
| OverviewItem* overview_item = current_grid->GetDropTarget(); |
| return gfx::ToEnclosedRect(overview_item->GetTransformedBounds()); |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(SplitViewTabDraggingTest); |
| }; |
| |
| // Test that in tablet mode, we only allow dragging on browser or chrome app |
| // window's caption area. |
| TEST_F(SplitViewTabDraggingTest, OnlyAllowDraggingOnBrowserOrChromeAppWindow) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| std::unique_ptr<aura::Window> window2( |
| CreateWindowWithType(bounds, AppType::CHROME_APP)); |
| std::unique_ptr<aura::Window> window3(CreateWindow(bounds)); |
| std::unique_ptr<aura::Window> window4( |
| CreateWindowWithType(bounds, AppType::ARC_APP)); |
| |
| std::unique_ptr<WindowResizer> resizer = |
| CreateResizerForTest(window1.get(), gfx::Point(), HTCAPTION); |
| EXPECT_TRUE(resizer.get()); |
| resizer->CompleteDrag(); |
| resizer.reset(); |
| |
| resizer = CreateResizerForTest(window2.get(), gfx::Point(), HTCAPTION); |
| EXPECT_TRUE(resizer.get()); |
| resizer->CompleteDrag(); |
| resizer.reset(); |
| |
| resizer = CreateResizerForTest(window3.get(), gfx::Point(), HTCAPTION); |
| EXPECT_FALSE(resizer.get()); |
| |
| resizer = CreateResizerForTest(window4.get(), gfx::Point(), HTCAPTION); |
| EXPECT_FALSE(resizer.get()); |
| } |
| |
| // Test that in tablet mode, we only allow dragging that happens on window |
| // caption or top area. |
| TEST_F(SplitViewTabDraggingTest, OnlyAllowDraggingOnCaptionOrTopArea) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| |
| // Only dragging on HTCAPTION or HTTOP area is allowed. |
| std::unique_ptr<WindowResizer> resizer = |
| CreateResizerForTest(window.get(), gfx::Point(), HTLEFT); |
| EXPECT_FALSE(resizer.get()); |
| resizer = CreateResizerForTest(window.get(), gfx::Point(), HTRIGHT); |
| EXPECT_FALSE(resizer.get()); |
| resizer = CreateResizerForTest(window.get(), gfx::Point(), HTCAPTION); |
| EXPECT_TRUE(resizer.get()); |
| resizer->CompleteDrag(); |
| resizer.reset(); |
| resizer = CreateResizerForTest(window.get(), gfx::Point(), HTTOP); |
| EXPECT_TRUE(resizer.get()); |
| resizer->CompleteDrag(); |
| resizer.reset(); |
| |
| // No matter if we're in tab-dragging process, as long as the drag happens on |
| // the caption or top area, it should be able to drag the window. |
| SetIsInTabDragging(window.get(), /*is_dragging=*/true); |
| resizer = CreateResizerForTest(window.get(), gfx::Point(), HTCAPTION); |
| EXPECT_TRUE(resizer.get()); |
| resizer->CompleteDrag(); |
| resizer.reset(); |
| |
| SetIsInTabDragging(window.get(), /*is_dragging=*/false); |
| resizer = CreateResizerForTest(window.get(), gfx::Point(), HTCAPTION); |
| EXPECT_TRUE(resizer.get()); |
| resizer->CompleteDrag(); |
| resizer.reset(); |
| } |
| |
| // Test that in tablet mode, if the dragging is from mouse event, the mouse |
| // cursor should be properly locked. |
| TEST_F(SplitViewTabDraggingTest, LockCursor) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| SetIsInTabDragging(window.get(), /*is_dragging=*/true); |
| EXPECT_FALSE(Shell::Get()->cursor_manager()->IsCursorLocked()); |
| |
| std::unique_ptr<WindowResizer> resizer = CreateResizerForTest( |
| window.get(), gfx::Point(), HTCAPTION, ::wm::WINDOW_MOVE_SOURCE_MOUSE); |
| ASSERT_TRUE(resizer.get()); |
| EXPECT_TRUE(Shell::Get()->cursor_manager()->IsCursorLocked()); |
| |
| resizer->CompleteDrag(); |
| resizer.reset(); |
| EXPECT_FALSE(Shell::Get()->cursor_manager()->IsCursorLocked()); |
| } |
| |
| // Test that in tablet mode, if a window is in tab-dragging process, its |
| // backdrop is disabled during dragging process. |
| TEST_F(SplitViewTabDraggingTest, NoBackDropDuringDragging) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| EXPECT_EQ(window->GetProperty(kBackdropWindowMode), |
| BackdropWindowMode::kAuto); |
| |
| std::unique_ptr<WindowResizer> resizer = |
| StartDrag(window.get(), window.get()); |
| ASSERT_TRUE(resizer.get()); |
| EXPECT_EQ(window->GetProperty(kBackdropWindowMode), |
| BackdropWindowMode::kDisabled); |
| |
| resizer->Drag(gfx::Point(), 0); |
| EXPECT_EQ(window->GetProperty(kBackdropWindowMode), |
| BackdropWindowMode::kDisabled); |
| |
| resizer->CompleteDrag(); |
| EXPECT_EQ(window->GetProperty(kBackdropWindowMode), |
| BackdropWindowMode::kAuto); |
| } |
| |
| // Test that in tablet mode, the window that is in tab-dragging process should |
| // not be shown in overview mode. |
| TEST_F(SplitViewTabDraggingTest, DoNotShowDraggedWindowInOverview) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| std::unique_ptr<aura::Window> window2( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| |
| Shell::Get()->overview_controller()->ToggleOverview(); |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| OverviewSession* overview_session = |
| Shell::Get()->overview_controller()->overview_session(); |
| EXPECT_TRUE(overview_session->IsWindowInOverview(window1.get())); |
| EXPECT_TRUE(overview_session->IsWindowInOverview(window2.get())); |
| Shell::Get()->overview_controller()->ToggleOverview(); |
| |
| std::unique_ptr<WindowResizer> resizer = |
| StartDrag(window1.get(), window1.get()); |
| |
| // Since the source window is the dragged window, the overview should have |
| // been opened. |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| overview_session = Shell::Get()->overview_controller()->overview_session(); |
| EXPECT_FALSE(overview_session->IsWindowInOverview(window1.get())); |
| EXPECT_TRUE(overview_session->IsWindowInOverview(window2.get())); |
| |
| CompleteDrag(std::move(resizer)); |
| EXPECT_FALSE(Shell::Get()->overview_controller()->IsSelecting()); |
| } |
| |
| // Test that if a window is in tab-dragging process, the split divider is placed |
| // below the current dragged window. |
| TEST_F(SplitViewTabDraggingTest, DividerIsBelowDraggedWindow) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| std::unique_ptr<aura::Window> window2( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| views::Widget* split_divider_widget = |
| split_view_controller()->split_view_divider()->divider_widget(); |
| EXPECT_TRUE(split_divider_widget->IsAlwaysOnTop()); |
| |
| std::unique_ptr<WindowResizer> resizer = |
| StartDrag(window1.get(), window1.get()); |
| ASSERT_TRUE(resizer.get()); |
| EXPECT_FALSE(split_divider_widget->IsAlwaysOnTop()); |
| |
| resizer->Drag(gfx::Point(), 0); |
| EXPECT_FALSE(split_divider_widget->IsAlwaysOnTop()); |
| |
| CompleteDrag(std::move(resizer)); |
| EXPECT_TRUE(split_divider_widget->IsAlwaysOnTop()); |
| } |
| |
| // Test the functionalities that are related to dragging a maximized window's |
| // tabs. See the expected behaviors described in go/tab-dragging-in-tablet-mode. |
| TEST_F(SplitViewTabDraggingTest, DragMaximizedWindow) { |
| UpdateDisplay("600x600"); |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| std::unique_ptr<aura::Window> window2( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| wm::GetWindowState(window1.get())->Maximize(); |
| wm::GetWindowState(window2.get())->Maximize(); |
| EXPECT_TRUE(wm::GetWindowState(window1.get())->IsMaximized()); |
| EXPECT_TRUE(wm::GetWindowState(window2.get())->IsMaximized()); |
| |
| // 1. If the dragged window is the source window: |
| std::unique_ptr<WindowResizer> resizer = |
| StartDrag(window1.get(), window1.get()); |
| ASSERT_TRUE(resizer.get()); |
| // Overview should have been opened because the dragged window is the source |
| // window. |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| |
| // 1.a. Drag the window to move a small amount of distance will maximize the |
| // window again. |
| DragWindowWithOffset(resizer.get(), 10, 10); |
| EXPECT_EQ(GetIndicatorState(resizer.get()), IndicatorState::kNone); |
| // Drag the window past the indicators threshold to show the indicators. |
| DragWindowTo(resizer.get(), |
| gfx::Point(200, GetIndicatorsThreshold(window1.get()))); |
| EXPECT_EQ(GetIndicatorState(resizer.get()), IndicatorState::kDragArea); |
| CompleteDrag(std::move(resizer)); |
| EXPECT_TRUE(wm::GetWindowState(window1.get())->IsMaximized()); |
| EXPECT_TRUE(wm::GetWindowState(window2.get())->IsMaximized()); |
| EXPECT_FALSE(Shell::Get()->overview_controller()->IsSelecting()); |
| |
| // 1.b. Drag the window long enough (pass one fourth of the screen vertical |
| // height) to snap the window to splitscreen. |
| resizer = StartDrag(window1.get(), window1.get()); |
| ASSERT_TRUE(resizer.get()); |
| DragWindowTo(resizer.get(), gfx::Point(0, 300)); |
| EXPECT_EQ(GetIndicatorState(resizer.get()), IndicatorState::kPreviewAreaLeft); |
| resizer->CompleteDrag(); |
| EXPECT_EQ(window1->GetProperty(kTabDroppedWindowStateTypeKey), |
| mojom::WindowStateType::LEFT_SNAPPED); |
| EXPECT_NE(split_view_controller()->left_window(), window1.get()); |
| SetIsInTabDragging(window1.get(), false); |
| resizer.reset(); |
| EXPECT_TRUE(split_view_controller()->IsSplitViewModeActive()); |
| EXPECT_EQ(split_view_controller()->left_window(), window1.get()); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::LEFT_SNAPPED); |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| |
| // Maximize the snapped window to end split view mode and overview mode. |
| wm::GetWindowState(window1.get())->Maximize(); |
| EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive()); |
| EXPECT_FALSE(Shell::Get()->overview_controller()->IsSelecting()); |
| |
| // 2. If the dragged window is not the source window: |
| resizer = StartDrag(window1.get(), window2.get()); |
| ASSERT_TRUE(resizer.get()); |
| // Overview is not opened for this case. |
| EXPECT_FALSE(Shell::Get()->overview_controller()->IsSelecting()); |
| // When the drag starts, the source window's bounds are the same with the |
| // work area's bounds. |
| const display::Display display = |
| display::Screen::GetScreen()->GetDisplayNearestWindow(window1.get()); |
| const gfx::Rect work_area_bounds = display.work_area(); |
| EXPECT_EQ(window2->GetBoundsInScreen(), work_area_bounds); |
| EXPECT_TRUE(window1->GetProperty(kCanAttachToAnotherWindowKey)); |
| |
| // 2.a. Drag the window a small amount of distance and release will maximize |
| // the window. |
| DragWindowWithOffset(resizer.get(), 10, 10); |
| EXPECT_EQ(GetIndicatorState(resizer.get()), IndicatorState::kNone); |
| // Drag the window past the indicators threshold to show the indicators. |
| DragWindowTo(resizer.get(), |
| gfx::Point(200, GetIndicatorsThreshold(window1.get()))); |
| EXPECT_EQ(GetIndicatorState(resizer.get()), IndicatorState::kDragArea); |
| // The source window should also have been scaled. |
| EXPECT_NE(window2->GetBoundsInScreen(), work_area_bounds); |
| EXPECT_FALSE(window1->GetProperty(kCanAttachToAnotherWindowKey)); |
| CompleteDrag(std::move(resizer)); |
| EXPECT_TRUE(wm::GetWindowState(window1.get())->IsMaximized()); |
| EXPECT_TRUE(wm::GetWindowState(window2.get())->IsMaximized()); |
| // The source window should have restored its bounds. |
| EXPECT_EQ(window2->GetBoundsInScreen(), work_area_bounds); |
| EXPECT_TRUE(window1->GetProperty(kCanAttachToAnotherWindowKey)); |
| |
| // 2.b. Drag the window long enough to snap the window. The source window will |
| // snap to the other side of the splitscreen. |
| resizer = StartDrag(window1.get(), window2.get()); |
| ASSERT_TRUE(resizer.get()); |
| DragWindowTo(resizer.get(), gfx::Point(600, 300)); |
| EXPECT_EQ(GetIndicatorState(resizer.get()), |
| IndicatorState::kPreviewAreaRight); |
| // The source window's bounds should be the same as the left snapped window |
| // bounds as it's to be snapped to LEFT. |
| EXPECT_EQ(window2->GetBoundsInScreen(), |
| split_view_controller()->GetSnappedWindowBoundsInScreen( |
| window2.get(), SplitViewController::LEFT)); |
| EXPECT_FALSE(window1->GetProperty(kCanAttachToAnotherWindowKey)); |
| DragWindowTo(resizer.get(), gfx::Point(0, 300)); |
| EXPECT_EQ(GetIndicatorState(resizer.get()), IndicatorState::kPreviewAreaLeft); |
| // The source window's bounds should be the same as the right snapped window |
| // bounds as it's to be snapped to RIGHT. |
| EXPECT_EQ(window2->GetBoundsInScreen(), |
| split_view_controller()->GetSnappedWindowBoundsInScreen( |
| window2.get(), SplitViewController::RIGHT)); |
| EXPECT_FALSE(window1->GetProperty(kCanAttachToAnotherWindowKey)); |
| |
| resizer->CompleteDrag(); |
| EXPECT_EQ(window1->GetProperty(kTabDroppedWindowStateTypeKey), |
| mojom::WindowStateType::LEFT_SNAPPED); |
| EXPECT_NE(split_view_controller()->left_window(), window1.get()); |
| SetIsInTabDragging(window1.get(), false); |
| resizer.reset(); |
| EXPECT_TRUE(split_view_controller()->IsSplitViewModeActive()); |
| EXPECT_EQ(split_view_controller()->left_window(), window1.get()); |
| EXPECT_EQ(split_view_controller()->right_window(), window2.get()); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::BOTH_SNAPPED); |
| EXPECT_FALSE(Shell::Get()->overview_controller()->IsSelecting()); |
| EXPECT_TRUE(window1->GetProperty(kCanAttachToAnotherWindowKey)); |
| |
| EndSplitView(); |
| EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive()); |
| |
| // 3. If the dragged window is destroyed during dragging (may happen due to |
| // all its tabs are attached into another window), nothing changes. |
| resizer = StartDrag(window1.get(), window2.get()); |
| ASSERT_TRUE(resizer.get()); |
| resizer->Drag(gfx::Point(0, 300), 0); |
| resizer->CompleteDrag(); |
| resizer.reset(); |
| window1.reset(); |
| EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive()); |
| EXPECT_FALSE(Shell::Get()->overview_controller()->IsSelecting()); |
| EXPECT_TRUE(wm::GetWindowState(window2.get())->IsMaximized()); |
| |
| // 4. If the dragged window can't be snapped, then the source window should |
| // not be put to the snapped position during drag. |
| const gfx::Rect display_bounds = display.bounds(); |
| window1 = std::unique_ptr<aura::Window>( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| aura::test::TestWindowDelegate* delegate = |
| static_cast<aura::test::TestWindowDelegate*>(window1->delegate()); |
| delegate->set_minimum_size( |
| gfx::Size(display_bounds.width() * 0.67f, display_bounds.height())); |
| EXPECT_FALSE(CanSnapInSplitview(window1.get())); |
| resizer = StartDrag(window1.get(), window2.get()); |
| EXPECT_FALSE(Shell::Get()->overview_controller()->IsSelecting()); |
| DragWindowTo(resizer.get(), |
| gfx::Point(0, GetIndicatorsThreshold(window1.get()) + 10)); |
| EXPECT_EQ(GetIndicatorState(resizer.get()), IndicatorState::kCannotSnap); |
| // The souce window should has been scaled but not put to the right snapped |
| // window's position. |
| EXPECT_NE(window2->GetBoundsInScreen(), work_area_bounds); |
| EXPECT_NE(window2->GetBoundsInScreen(), |
| split_view_controller()->GetSnappedWindowBoundsInScreen( |
| window2.get(), SplitViewController::RIGHT)); |
| CompleteDrag(std::move(resizer)); |
| EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive()); |
| } |
| |
| // Test the functionalities that are related to dragging a snapped window in |
| // splitscreen. There are always two snapped window when the drag starts (i.e., |
| // the overview mode is not active). See the expected behaviors described in |
| // go/tab-dragging-in-tablet-mode. |
| TEST_F(SplitViewTabDraggingTest, DragSnappedWindow) { |
| UpdateDisplay("600x600"); |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| std::unique_ptr<aura::Window> window2( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| std::unique_ptr<aura::Window> window3( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::BOTH_SNAPPED); |
| EXPECT_EQ(split_view_controller()->left_window(), window1.get()); |
| EXPECT_EQ(split_view_controller()->right_window(), window2.get()); |
| |
| // 1. If the dragged window is the source window: |
| std::unique_ptr<WindowResizer> resizer = |
| StartDrag(window1.get(), window1.get()); |
| ASSERT_TRUE(resizer.get()); |
| EXPECT_EQ(GetIndicatorState(resizer.get()), IndicatorState::kNone); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::RIGHT_SNAPPED); |
| // In this case overview grid will be opened, containing |window3|. |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| OverviewSession* overview_session = |
| Shell::Get()->overview_controller()->overview_session(); |
| EXPECT_FALSE(overview_session->IsWindowInOverview(window1.get())); |
| EXPECT_FALSE(overview_session->IsWindowInOverview(window2.get())); |
| EXPECT_TRUE(overview_session->IsWindowInOverview(window3.get())); |
| |
| // 1.a. If the window is only dragged for a small distance, the window will |
| // be put back to its original position. Overview mode will be ended. |
| DragWindowWithOffset(resizer.get(), 10, 10); |
| CompleteDrag(std::move(resizer)); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::BOTH_SNAPPED); |
| EXPECT_EQ(split_view_controller()->left_window(), window1.get()); |
| EXPECT_EQ(split_view_controller()->right_window(), window2.get()); |
| EXPECT_FALSE(Shell::Get()->overview_controller()->IsSelecting()); |
| |
| // 1.b. If the window is dragged long enough, it can replace the other split |
| // window. |
| resizer = StartDrag(window1.get(), window1.get()); |
| ASSERT_TRUE(resizer.get()); |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| DragWindowTo(resizer.get(), gfx::Point(600, 300)); |
| // No preview window shows up on overview side of the screen. |
| EXPECT_EQ(GetIndicatorState(resizer.get()), IndicatorState::kNone); |
| CompleteDrag(std::move(resizer)); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::RIGHT_SNAPPED); |
| EXPECT_EQ(split_view_controller()->right_window(), window1.get()); |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| overview_session = Shell::Get()->overview_controller()->overview_session(); |
| EXPECT_FALSE(overview_session->IsWindowInOverview(window1.get())); |
| EXPECT_TRUE(overview_session->IsWindowInOverview(window2.get())); |
| EXPECT_TRUE(overview_session->IsWindowInOverview(window3.get())); |
| // Snap |window2| again to test 1.c. |
| split_view_controller()->SnapWindow(window2.get(), SplitViewController::LEFT); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::BOTH_SNAPPED); |
| |
| // 1.c. If the dragged window is destroyed during dragging (may happen due to |
| // all its tabs are attached into another window), nothing changes. |
| resizer = StartDrag(window1.get(), window1.get()); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::LEFT_SNAPPED); |
| ASSERT_TRUE(resizer.get()); |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| resizer->Drag(gfx::Point(100, 100), 0); |
| resizer->CompleteDrag(); |
| resizer.reset(); |
| window1.reset(); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::LEFT_SNAPPED); |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| |
| // Recreate |window1| and snap it to test the following senarioes. |
| window1.reset(CreateWindowWithType(bounds, AppType::BROWSER)); |
| split_view_controller()->SnapWindow(window1.get(), |
| SplitViewController::RIGHT); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::BOTH_SNAPPED); |
| EXPECT_EQ(split_view_controller()->left_window(), window2.get()); |
| EXPECT_EQ(split_view_controller()->right_window(), window1.get()); |
| |
| // 2. If the dragged window is not the source window: |
| // In this case, |window3| can be regarded as a window that originates from |
| // |window2|. |
| resizer = StartDrag(window3.get(), window2.get()); |
| ASSERT_TRUE(resizer.get()); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::BOTH_SNAPPED); |
| EXPECT_EQ(split_view_controller()->left_window(), window2.get()); |
| EXPECT_EQ(split_view_controller()->right_window(), window1.get()); |
| EXPECT_FALSE(Shell::Get()->overview_controller()->IsSelecting()); |
| EXPECT_EQ(GetIndicatorState(resizer.get()), IndicatorState::kNone); |
| EXPECT_EQ(window2->GetBoundsInScreen(), |
| split_view_controller()->GetSnappedWindowBoundsInScreen( |
| window2.get(), SplitViewController::LEFT)); |
| |
| // 2.a. If the window is only dragged for a small amount of distance, it will |
| // replace the same side of the split window that it originates from. |
| DragWindowWithOffset(resizer.get(), 10, 10); |
| EXPECT_EQ(GetIndicatorState(resizer.get()), IndicatorState::kNone); |
| // Even though the window is dragged past the indicators threshold, the |
| // indicators should not show up for this case. |
| DragWindowTo(resizer.get(), |
| gfx::Point(200, GetIndicatorsThreshold(window1.get()))); |
| EXPECT_EQ(GetIndicatorState(resizer.get()), IndicatorState::kNone); |
| // The source window's bounds should remain the same. |
| EXPECT_EQ(window2->GetBoundsInScreen(), |
| split_view_controller()->GetSnappedWindowBoundsInScreen( |
| window2.get(), SplitViewController::LEFT)); |
| CompleteDrag(std::move(resizer)); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::BOTH_SNAPPED); |
| EXPECT_EQ(split_view_controller()->left_window(), window3.get()); |
| EXPECT_EQ(split_view_controller()->right_window(), window1.get()); |
| EXPECT_FALSE(Shell::Get()->overview_controller()->IsSelecting()); |
| |
| // 2.b. If the window is dragged long enough, it can replace the other side of |
| // the split window. |
| resizer = StartDrag(window2.get(), window1.get()); |
| ASSERT_TRUE(resizer.get()); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::BOTH_SNAPPED); |
| EXPECT_EQ(split_view_controller()->left_window(), window3.get()); |
| EXPECT_EQ(split_view_controller()->right_window(), window1.get()); |
| EXPECT_FALSE(Shell::Get()->overview_controller()->IsSelecting()); |
| DragWindowTo(resizer.get(), gfx::Point(0, 300)); |
| // No preview window shows up on overview side of the screen. |
| EXPECT_EQ(GetIndicatorState(resizer.get()), IndicatorState::kNone); |
| CompleteDrag(std::move(resizer)); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::BOTH_SNAPPED); |
| EXPECT_EQ(split_view_controller()->left_window(), window2.get()); |
| EXPECT_EQ(split_view_controller()->right_window(), window1.get()); |
| EXPECT_FALSE(Shell::Get()->overview_controller()->IsSelecting()); |
| } |
| |
| // Test the functionalities that are related to dragging a snapped window while |
| // overview grid is open on the other side of the screen. See the expected |
| // behaviors described in go/tab-dragging-in-tablet-mode. |
| TEST_F(SplitViewTabDraggingTest, DragSnappedWindowWhileOverviewOpen) { |
| UpdateDisplay("600x600"); |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| std::unique_ptr<aura::Window> window2( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| std::unique_ptr<aura::Window> window3( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| |
| // Prepare the testing senario: |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| ToggleOverview(); |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::LEFT_SNAPPED); |
| EXPECT_EQ(split_view_controller()->left_window(), window1.get()); |
| |
| // 1. If the dragged window is the source window: |
| std::unique_ptr<WindowResizer> resizer = |
| StartDrag(window1.get(), window1.get()); |
| ASSERT_TRUE(resizer.get()); |
| // Overivew mode is still active, but split view mode is ended due to dragging |
| // the only snapped window. |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive()); |
| |
| // 1.a. If the window is only dragged for a small amount of distance |
| DragWindowWithOffset(resizer.get(), 10, 10); |
| EXPECT_EQ(GetIndicatorState(resizer.get()), IndicatorState::kNone); |
| // Drag the window past the indicators threshold should show the indicators. |
| DragWindowTo(resizer.get(), |
| gfx::Point(200, GetIndicatorsThreshold(window1.get()))); |
| EXPECT_EQ(GetIndicatorState(resizer.get()), IndicatorState::kDragArea); |
| CompleteDrag(std::move(resizer)); |
| EXPECT_TRUE(wm::GetWindowState(window1.get())->IsMaximized()); |
| EXPECT_FALSE(Shell::Get()->overview_controller()->IsSelecting()); |
| EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive()); |
| |
| // 1.b. If the window is dragged long enough, it can be snappped again. |
| // Prepare the testing senario first. |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| ToggleOverview(); |
| resizer = StartDrag(window1.get(), window1.get()); |
| ASSERT_TRUE(resizer.get()); |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive()); |
| DragWindowTo(resizer.get(), gfx::Point(0, 300)); |
| EXPECT_EQ(GetIndicatorState(resizer.get()), IndicatorState::kPreviewAreaLeft); |
| CompleteDrag(std::move(resizer)); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::LEFT_SNAPPED); |
| EXPECT_EQ(split_view_controller()->left_window(), window1.get()); |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| OverviewSession* overview_session = |
| Shell::Get()->overview_controller()->overview_session(); |
| EXPECT_TRUE(overview_session->IsWindowInOverview(window2.get())); |
| EXPECT_TRUE(overview_session->IsWindowInOverview(window3.get())); |
| |
| // 2. If the dragged window is not the source window: |
| // Prepare the testing senario first. Remove |window2| from overview first |
| // before tab-dragging. |
| OverviewGrid* current_grid = |
| overview_session->GetGridWithRootWindow(window2->GetRootWindow()); |
| ASSERT_TRUE(current_grid); |
| overview_session->RemoveOverviewItem( |
| current_grid->GetOverviewItemContaining(window2.get()), |
| /*reposition=*/false); |
| resizer = StartDrag(window2.get(), window1.get()); |
| ASSERT_TRUE(resizer.get()); |
| EXPECT_EQ(GetIndicatorState(resizer.get()), IndicatorState::kNone); |
| // Drag a samll amount of distance. |
| DragWindowWithOffset(resizer.get(), 10, 10); |
| EXPECT_EQ(GetIndicatorState(resizer.get()), IndicatorState::kNone); |
| // Drag the window past the indicators threshold should not show the drag |
| // indicators as Splitscreen is still active. |
| DragWindowTo(resizer.get(), |
| gfx::Point(200, GetIndicatorsThreshold(window1.get()))); |
| EXPECT_EQ(GetIndicatorState(resizer.get()), IndicatorState::kNone); |
| // The source window still remains the same bounds. |
| EXPECT_EQ(window1->GetBoundsInScreen(), |
| split_view_controller()->GetSnappedWindowBoundsInScreen( |
| window1.get(), SplitViewController::LEFT)); |
| |
| // 2.a. The dragged window can replace the only snapped window in the split |
| // screen. After that, the old snapped window will be put back in overview. |
| DragWindowTo(resizer.get(), gfx::Point(0, 500)); |
| // No preview window shows up on overview side of the screen. |
| EXPECT_EQ(GetIndicatorState(resizer.get()), IndicatorState::kNone); |
| CompleteDrag(std::move(resizer)); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::LEFT_SNAPPED); |
| EXPECT_EQ(split_view_controller()->left_window(), window2.get()); |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| overview_session = Shell::Get()->overview_controller()->overview_session(); |
| EXPECT_TRUE(overview_session->IsWindowInOverview(window1.get())); |
| EXPECT_TRUE(overview_session->IsWindowInOverview(window3.get())); |
| |
| // 2.b. The dragged window can snap to the other side of the splitscreen, |
| // causing overview mode to end. |
| // Remove |window1| from overview first before tab dragging. |
| overview_session->RemoveOverviewItem( |
| current_grid->GetOverviewItemContaining(window1.get()), |
| /*reposition=*/false); |
| resizer = StartDrag(window1.get(), window2.get()); |
| ASSERT_TRUE(resizer.get()); |
| DragWindowTo(resizer.get(), gfx::Point(600, 500)); |
| // No preview window shows up on overview side of the screen. |
| EXPECT_EQ(GetIndicatorState(resizer.get()), IndicatorState::kNone); |
| CompleteDrag(std::move(resizer)); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::BOTH_SNAPPED); |
| EXPECT_EQ(split_view_controller()->left_window(), window2.get()); |
| EXPECT_EQ(split_view_controller()->right_window(), window1.get()); |
| EXPECT_FALSE(Shell::Get()->overview_controller()->IsSelecting()); |
| } |
| |
| // Test that if a window is in tab-dragging process when overview is open, the |
| // new window item widget shows up when the drag starts, and is destroyed after |
| // the drag ends. |
| TEST_F(SplitViewTabDraggingTest, ShowNewWindowItemWhenDragStarts) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| std::unique_ptr<aura::Window> window2( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| std::unique_ptr<aura::Window> window3( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::BOTH_SNAPPED); |
| EXPECT_FALSE(Shell::Get()->overview_controller()->IsSelecting()); |
| |
| // Now drags |window1|. |
| std::unique_ptr<WindowResizer> resizer = |
| StartDrag(window1.get(), window1.get()); |
| // Overview should have been opened. |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::RIGHT_SNAPPED); |
| |
| // Test that the new window item widget shows up as the first one of the |
| // windows in the grid. |
| OverviewSession* overview_session = |
| Shell::Get()->overview_controller()->overview_session(); |
| OverviewGrid* current_grid = |
| overview_session->GetGridWithRootWindow(window1->GetRootWindow()); |
| ASSERT_TRUE(current_grid); |
| views::Widget* drop_target_widget = |
| current_grid->drop_target_widget_for_testing(); |
| EXPECT_TRUE(drop_target_widget); |
| |
| OverviewItem* drop_target = current_grid->GetOverviewItemContaining( |
| drop_target_widget->GetNativeWindow()); |
| ASSERT_TRUE(drop_target); |
| EXPECT_EQ(drop_target, current_grid->window_list().front().get()); |
| const gfx::Rect drop_target_bounds = |
| gfx::ToEnclosingRect(drop_target->target_bounds()); |
| DragWindowTo(resizer.get(), drop_target_bounds.CenterPoint()); |
| CompleteDrag(std::move(resizer)); |
| |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::RIGHT_SNAPPED); |
| // Test that the dragged window has been added to the overview mode, and it is |
| // added at the front of the grid. |
| EXPECT_EQ(current_grid->window_list().size(), 2u); |
| OverviewItem* first_overview_item = |
| current_grid->GetOverviewItemContaining(window1.get()); |
| EXPECT_EQ(first_overview_item, current_grid->window_list().front().get()); |
| EXPECT_TRUE(overview_session->IsWindowInOverview(window1.get())); |
| EXPECT_TRUE(overview_session->IsWindowInOverview(window3.get())); |
| // Test that the new window item widget has been destroyed. |
| EXPECT_FALSE(current_grid->drop_target_widget_for_testing()); |
| } |
| |
| // Tests that if overview is ended because of releasing the dragged window, we |
| // should not do animation when exiting overview. |
| TEST_F(SplitViewTabDraggingTest, OverviewExitAnimationTest) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| std::unique_ptr<aura::Window> window2( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| std::unique_ptr<aura::Window> window3( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| std::unique_ptr<OverviewStatesObserver> overview_observer = |
| std::make_unique<OverviewStatesObserver>(window1->GetRootWindow()); |
| |
| // 1) If dragging a maximized window: |
| std::unique_ptr<WindowResizer> resizer = |
| StartDrag(window1.get(), window1.get()); |
| ASSERT_TRUE(resizer.get()); |
| // Overview should have been opened because the dragged window is the source |
| // window. |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| // The value should be properly initialized. |
| EXPECT_TRUE(overview_observer->overview_animate_when_exiting()); |
| |
| // Now release the dragged window. There should be no animation when exiting |
| // overview. |
| CompleteDrag(std::move(resizer)); |
| EXPECT_FALSE(Shell::Get()->overview_controller()->IsSelecting()); |
| EXPECT_FALSE(overview_observer->overview_animate_when_exiting()); |
| |
| // 2) If dragging a snapped window: |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| overview_observer.reset(new OverviewStatesObserver(window1->GetRootWindow())); |
| resizer = StartDrag(window1.get(), window1.get()); |
| ASSERT_TRUE(resizer.get()); |
| // Overview should have been opened behind the dragged window. |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| // Split view should still be active. |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::RIGHT_SNAPPED); |
| // The value should be properly initialized. |
| EXPECT_TRUE(overview_observer->overview_animate_when_exiting()); |
| |
| CompleteDrag(std::move(resizer)); |
| EXPECT_FALSE(Shell::Get()->overview_controller()->IsSelecting()); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::BOTH_SNAPPED); |
| EXPECT_FALSE(overview_observer->overview_animate_when_exiting()); |
| } |
| |
| // Tests that there is no top drag indicator if drag a window in portrait screen |
| // orientation. |
| TEST_F(SplitViewTabDraggingTest, DragIndicatorsInPortraitOrientationTest) { |
| UpdateDisplay("800x600"); |
| int64_t display_id = display::Screen::GetScreen()->GetPrimaryDisplay().id(); |
| display::DisplayManager* display_manager = Shell::Get()->display_manager(); |
| display::test::ScopedSetInternalDisplayId set_internal(display_manager, |
| display_id); |
| ScreenOrientationControllerTestApi test_api( |
| Shell::Get()->screen_orientation_controller()); |
| ASSERT_EQ(test_api.GetCurrentOrientation(), |
| OrientationLockType::kLandscapePrimary); |
| |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| wm::GetWindowState(window.get())->Maximize(); |
| EXPECT_TRUE(wm::GetWindowState(window.get())->IsMaximized()); |
| std::unique_ptr<WindowResizer> resizer = |
| StartDrag(window.get(), window.get()); |
| ASSERT_TRUE(resizer.get()); |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| // Drag the window past the indicators threshold to show the indicators. |
| DragWindowTo(resizer.get(), |
| gfx::Point(200, GetIndicatorsThreshold(window.get()))); |
| EXPECT_EQ(GetIndicatorState(resizer.get()), IndicatorState::kDragArea); |
| CompleteDrag(std::move(resizer)); |
| EXPECT_TRUE(wm::GetWindowState(window.get())->IsMaximized()); |
| |
| // Rotate the screen by 270 degree to portrait primary orientation. |
| test_api.SetDisplayRotation(display::Display::ROTATE_270, |
| display::Display::RotationSource::ACTIVE); |
| EXPECT_EQ(test_api.GetCurrentOrientation(), |
| OrientationLockType::kPortraitPrimary); |
| resizer = StartDrag(window.get(), window.get()); |
| ASSERT_TRUE(resizer.get()); |
| // Drag the window past the indicators threshold to show the indicators. |
| DragWindowTo(resizer.get(), |
| gfx::Point(200, GetIndicatorsThreshold(window.get()))); |
| EXPECT_EQ(GetIndicatorState(resizer.get()), IndicatorState::kDragAreaRight); |
| CompleteDrag(std::move(resizer)); |
| EXPECT_TRUE(wm::GetWindowState(window.get())->IsMaximized()); |
| } |
| |
| // Tests that if dragging a window into the preview split area, overivew bounds |
| // should be adjusted accordingly. |
| TEST_F(SplitViewTabDraggingTest, AdjustOverviewBoundsDuringDragging) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| std::unique_ptr<aura::Window> window2( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| std::unique_ptr<aura::Window> window3( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| |
| OverviewController* selector_controller = Shell::Get()->overview_controller(); |
| EXPECT_FALSE(selector_controller->IsSelecting()); |
| |
| // Start dragging |window1|. |
| std::unique_ptr<WindowResizer> resizer = |
| StartDrag(window1.get(), window1.get()); |
| // Overview should have been opened. |
| EXPECT_TRUE(selector_controller->IsSelecting()); |
| |
| // Test that the drop target shows up as the first item in overview. |
| OverviewGrid* current_grid = |
| selector_controller->overview_session()->GetGridWithRootWindow( |
| window1->GetRootWindow()); |
| EXPECT_TRUE(current_grid->GetDropTarget()); |
| const gfx::Rect work_area_bounds = |
| screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer( |
| window1.get()); |
| EXPECT_EQ(current_grid->bounds(), work_area_bounds); |
| // The drop target should be visible. |
| views::Widget* drop_target_widget = |
| current_grid->drop_target_widget_for_testing(); |
| EXPECT_TRUE(drop_target_widget); |
| // Drop target's bounds has been set when added it into overview, which is not |
| // equals to the window's bounds. |
| EXPECT_EQ(drop_target_widget->GetNativeWindow()->bounds(), |
| GetDropTargetBoundsDuringDrag(window1.get())); |
| EXPECT_NE(drop_target_widget->GetNativeWindow()->bounds(), window1->bounds()); |
| EXPECT_TRUE(drop_target_widget->IsVisible()); |
| |
| // Now drag |window1| to the left preview split area. |
| DragWindowTo(resizer.get(), |
| gfx::Point(0, work_area_bounds.CenterPoint().y())); |
| EXPECT_EQ(current_grid->bounds(), |
| split_view_controller()->GetSnappedWindowBoundsInScreen( |
| window1.get(), SplitViewController::RIGHT)); |
| EXPECT_FALSE(drop_target_widget->IsVisible()); |
| |
| // Drag it to middle. |
| DragWindowTo(resizer.get(), work_area_bounds.CenterPoint()); |
| EXPECT_EQ(current_grid->bounds(), work_area_bounds); |
| EXPECT_TRUE(drop_target_widget->IsVisible()); |
| |
| // Drag |window1| to the right preview split area. |
| DragWindowTo(resizer.get(), gfx::Point(work_area_bounds.right(), |
| work_area_bounds.CenterPoint().y())); |
| EXPECT_EQ(current_grid->bounds(), |
| split_view_controller()->GetSnappedWindowBoundsInScreen( |
| window1.get(), SplitViewController::LEFT)); |
| EXPECT_FALSE(drop_target_widget->IsVisible()); |
| |
| CompleteDrag(std::move(resizer)); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::RIGHT_SNAPPED); |
| EXPECT_TRUE(selector_controller->IsSelecting()); |
| |
| // Snap another window should end overview. |
| split_view_controller()->SnapWindow(window2.get(), SplitViewController::LEFT); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::BOTH_SNAPPED); |
| EXPECT_FALSE(selector_controller->IsSelecting()); |
| |
| // Now drag |window1| again. Overview and splitview should be both active at |
| // the same time during dragging. |
| resizer = StartDrag(window1.get(), window1.get()); |
| EXPECT_TRUE(selector_controller->IsSelecting()); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::LEFT_SNAPPED); |
| |
| current_grid = selector_controller->overview_session()->GetGridWithRootWindow( |
| window1->GetRootWindow()); |
| // The drop target should be visible. |
| drop_target_widget = current_grid->drop_target_widget_for_testing(); |
| EXPECT_TRUE(drop_target_widget); |
| EXPECT_TRUE(drop_target_widget->IsVisible()); |
| EXPECT_EQ(drop_target_widget->GetNativeWindow()->bounds(), |
| GetDropTargetBoundsDuringDrag(window1.get())); |
| EXPECT_NE(drop_target_widget->GetNativeWindow()->bounds(), window1->bounds()); |
| EXPECT_EQ(current_grid->bounds(), |
| split_view_controller()->GetSnappedWindowBoundsInScreen( |
| window1.get(), SplitViewController::RIGHT)); |
| |
| // Drag |window1| to the right preview split area. |
| DragWindowTo(resizer.get(), gfx::Point(work_area_bounds.right(), |
| work_area_bounds.CenterPoint().y())); |
| // Overview bounds stays the same. |
| EXPECT_EQ(current_grid->bounds(), |
| split_view_controller()->GetSnappedWindowBoundsInScreen( |
| window1.get(), SplitViewController::RIGHT)); |
| EXPECT_TRUE(drop_target_widget->IsVisible()); |
| |
| // Drag |window1| to the left preview split area. |
| DragWindowTo(resizer.get(), |
| gfx::Point(0, work_area_bounds.CenterPoint().y())); |
| EXPECT_EQ(current_grid->bounds(), |
| split_view_controller()->GetSnappedWindowBoundsInScreen( |
| window1.get(), SplitViewController::RIGHT)); |
| EXPECT_TRUE(drop_target_widget->IsVisible()); |
| |
| CompleteDrag(std::move(resizer)); |
| |
| // |window1| should now snap to left. |window2| is put back in overview. |
| EXPECT_EQ(split_view_controller()->left_window(), window1.get()); |
| EXPECT_TRUE(selector_controller->IsSelecting()); |
| EXPECT_TRUE(selector_controller->overview_session()->IsWindowInOverview( |
| window2.get())); |
| |
| // Now drag |window1| again. |
| resizer = StartDrag(window1.get(), window1.get()); |
| // Splitview should end now, but overview should still active. |
| EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive()); |
| EXPECT_TRUE(selector_controller->IsSelecting()); |
| // The size of drop target should still not be the same as the dragged |
| // window's size. |
| current_grid = selector_controller->overview_session()->GetGridWithRootWindow( |
| window1->GetRootWindow()); |
| drop_target_widget = current_grid->drop_target_widget_for_testing(); |
| EXPECT_TRUE(drop_target_widget); |
| EXPECT_TRUE(drop_target_widget->IsVisible()); |
| EXPECT_EQ(drop_target_widget->GetNativeWindow()->bounds(), |
| GetDropTargetBoundsDuringDrag(window1.get())); |
| EXPECT_NE(drop_target_widget->GetNativeWindow()->bounds(), window1->bounds()); |
| CompleteDrag(std::move(resizer)); |
| } |
| |
| // Tests that a dragged window's bounds should be updated before dropping onto |
| // the drop target to add into overview. |
| TEST_F(SplitViewTabDraggingTest, WindowBoundsUpdatedBeforeAddingToOverview) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| gfx::Rect tablet_mode_bounds = window1->bounds(); |
| EXPECT_NE(bounds, tablet_mode_bounds); |
| |
| // Drag |window1|. Overview should open behind the dragged window. |
| std::unique_ptr<WindowResizer> resizer = |
| StartDrag(window1.get(), window1.get()); |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| |
| // Change the |window1|'s bounds to simulate what might happen in reality. |
| window1->SetBounds(bounds); |
| EXPECT_EQ(bounds, window1->bounds()); |
| |
| // Drop |window1| to the drop target in overview. |
| OverviewController* overview_controller = Shell::Get()->overview_controller(); |
| OverviewSession* overview_session = overview_controller->overview_session(); |
| OverviewGrid* current_grid = |
| overview_session->GetGridWithRootWindow(window1->GetRootWindow()); |
| ASSERT_TRUE(current_grid); |
| EXPECT_EQ(1u, current_grid->window_list().size()); |
| |
| OverviewItem* overview_item = current_grid->GetDropTarget(); |
| ASSERT_TRUE(overview_item); |
| gfx::Rect drop_target_bounds = |
| gfx::ToEnclosingRect(overview_item->target_bounds()); |
| DragWindowTo(resizer.get(), drop_target_bounds.CenterPoint()); |
| |
| CompleteDrag(std::move(resizer)); |
| // |window1| should have been merged into overview. |
| EXPECT_EQ(current_grid->window_list().size(), 1u); |
| EXPECT_TRUE(overview_session->IsWindowInOverview(window1.get())); |
| // |window1|'s bounds should have been updated to its tablet mode bounds. |
| EXPECT_EQ(tablet_mode_bounds, window1->bounds()); |
| overview_item = current_grid->window_list().front().get(); |
| // The new overview item's bounds should be the same during drag and after |
| // drag. |
| EXPECT_EQ(drop_target_bounds, |
| gfx::ToEnclosingRect(overview_item->target_bounds())); |
| ToggleOverview(); |
| EXPECT_FALSE(overview_controller->IsSelecting()); |
| |
| // Drag |window1|. Overview should open behind the dragged window. |
| resizer = StartDrag(window1.get(), window1.get()); |
| EXPECT_TRUE(overview_controller->IsSelecting()); |
| |
| // Change the |window1|'s bounds to simulate what might happen in reality. |
| window1->SetBounds(bounds); |
| EXPECT_EQ(bounds, window1->bounds()); |
| |
| // Drag the window to right bottom outside the drop target, the window's |
| // bounds should also be updated before being dropped into overview. |
| drop_target_bounds = GetDropTargetBoundsDuringDrag(window1.get()); |
| DragWindowTo(resizer.get(), |
| drop_target_bounds.bottom_right() + gfx::Vector2d(10, 10)); |
| CompleteDrag(std::move(resizer)); |
| // |window1| should have been merged into overview. |
| EXPECT_TRUE(overview_controller->overview_session()->IsWindowInOverview( |
| window1.get())); |
| // |window1|'s bounds should have been updated to its tablet mode bounds. |
| EXPECT_EQ(tablet_mode_bounds, window1->bounds()); |
| } |
| |
| // Tests that window should be dropped into overview if has been dragged further |
| // than half of the distance from top of display to the top of drop target. |
| TEST_F(SplitViewTabDraggingTest, DropWindowIntoOverviewOnDragPositionTest) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> browser_window1( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| wm::GetWindowState(browser_window1.get())->Maximize(); |
| gfx::Rect work_area_bounds = |
| display::Screen::GetScreen() |
| ->GetDisplayNearestWindow(browser_window1.get()) |
| .work_area(); |
| std::unique_ptr<WindowResizer> resizer = |
| StartDrag(browser_window1.get(), browser_window1.get()); |
| |
| // Restore window back to maximized if it has been dragged less than the |
| // distance threshold. |
| gfx::Rect drop_target_bounds = |
| GetDropTargetBoundsDuringDrag(browser_window1.get()); |
| DragWindowTo( |
| resizer.get(), |
| gfx::Point( |
| 200, work_area_bounds.y() + |
| TabletModeWindowDragDelegate::kDragPositionToOverviewRatio * |
| (drop_target_bounds.y() - work_area_bounds.y()) - |
| 10)); |
| CompleteDrag(std::move(resizer)); |
| EXPECT_TRUE(wm::GetWindowState(browser_window1.get())->IsMaximized()); |
| EXPECT_FALSE(Shell::Get()->overview_controller()->IsSelecting()); |
| |
| // Drop window into overview if it has beenn dragged further than the distance |
| // threshold. |
| resizer = StartDrag(browser_window1.get(), browser_window1.get()); |
| drop_target_bounds = GetDropTargetBoundsDuringDrag(browser_window1.get()); |
| DragWindowTo( |
| resizer.get(), |
| gfx::Point( |
| 200, work_area_bounds.y() + |
| TabletModeWindowDragDelegate::kDragPositionToOverviewRatio * |
| (drop_target_bounds.y() - work_area_bounds.y()) + |
| 10)); |
| CompleteDrag(std::move(resizer)); |
| OverviewSession* overview_session = |
| Shell::Get()->overview_controller()->overview_session(); |
| EXPECT_TRUE(overview_session->IsWindowInOverview(browser_window1.get())); |
| ToggleOverview(); |
| |
| // Do not consider the drag position if preview area is shown. Window should |
| // to be snapped in this case. |
| resizer = StartDrag(browser_window1.get(), browser_window1.get()); |
| drop_target_bounds = GetDropTargetBoundsDuringDrag(browser_window1.get()); |
| DragWindowTo(resizer.get(), gfx::Point(0, drop_target_bounds.y() + 10)); |
| EXPECT_EQ(IndicatorState::kPreviewAreaLeft, GetIndicatorState(resizer.get())); |
| CompleteDrag(std::move(resizer)); |
| EXPECT_TRUE(wm::GetWindowState(browser_window1.get())->IsSnapped()); |
| EXPECT_EQ(SplitViewController::LEFT_SNAPPED, |
| split_view_controller()->state()); |
| |
| // Should not consider the drag position if splitview is active. Window should |
| // still back to be snapped. |
| std::unique_ptr<aura::Window> browser_window2( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| split_view_controller()->SnapWindow(browser_window2.get(), |
| SplitViewController::RIGHT); |
| EXPECT_EQ(SplitViewController::BOTH_SNAPPED, |
| split_view_controller()->state()); |
| resizer = StartDrag(browser_window1.get(), browser_window1.get()); |
| drop_target_bounds = GetDropTargetBoundsDuringDrag(browser_window1.get()); |
| DragWindowTo(resizer.get(), gfx::Point(0, drop_target_bounds.y() + 10)); |
| EXPECT_TRUE(split_view_controller()->IsSplitViewModeActive()); |
| CompleteDrag(std::move(resizer)); |
| EXPECT_EQ(SplitViewController::BOTH_SNAPPED, |
| split_view_controller()->state()); |
| EXPECT_FALSE(Shell::Get()->overview_controller()->IsSelecting()); |
| EndSplitView(); |
| EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive()); |
| |
| // Restore window back to maximized if it has been dragged less than the |
| // distance threshold when dock magnifier is enabled. |
| Shell::Get()->docked_magnifier_controller()->SetEnabled(true); |
| work_area_bounds = display::Screen::GetScreen() |
| ->GetDisplayNearestWindow(browser_window1.get()) |
| .work_area(); |
| resizer = StartDrag(browser_window1.get(), browser_window1.get()); |
| drop_target_bounds = GetDropTargetBoundsDuringDrag(browser_window1.get()); |
| DragWindowTo( |
| resizer.get(), |
| gfx::Point( |
| 200, work_area_bounds.y() + |
| TabletModeWindowDragDelegate::kDragPositionToOverviewRatio * |
| (drop_target_bounds.y() - work_area_bounds.y()) - |
| 10)); |
| CompleteDrag(std::move(resizer)); |
| EXPECT_TRUE(wm::GetWindowState(browser_window1.get())->IsMaximized()); |
| EXPECT_FALSE(Shell::Get()->overview_controller()->IsSelecting()); |
| } |
| |
| // Tests that a dragged window should have the active window shadow during |
| // dragging. |
| TEST_F(SplitViewTabDraggingTest, DraggedWindowShouldHaveActiveWindowShadow) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| std::unique_ptr<aura::Window> window2( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| wm::ActivateWindow(window1.get()); |
| wm::ActivateWindow(window2.get()); |
| window1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); |
| window2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); |
| |
| // 1) Start dragging |window2|. |window2| is the source window. |
| std::unique_ptr<WindowResizer> resizer = |
| StartDrag(window2.get(), window2.get()); |
| // |window2| should have the active window shadow. |
| ::wm::ShadowController* shadow_controller = Shell::Get()->shadow_controller(); |
| ui::Shadow* shadow = shadow_controller->GetShadowForWindow(window2.get()); |
| ASSERT_TRUE(shadow); |
| EXPECT_EQ(shadow->desired_elevation(), ::wm::kShadowElevationActiveWindow); |
| EXPECT_TRUE(shadow_controller->IsShadowVisibleForWindow(window2.get())); |
| |
| CompleteDrag(std::move(resizer)); |
| Shell::Get()->shadow_controller()->UpdateShadowForWindow(window2.get()); |
| shadow = shadow_controller->GetShadowForWindow(window2.get()); |
| ASSERT_TRUE(shadow); |
| EXPECT_FALSE(shadow_controller->IsShadowVisibleForWindow(window2.get())); |
| |
| // 2) Start dragging |window2|, but |window2| is not the source window. |
| resizer = StartDrag(window2.get(), window1.get()); |
| shadow = shadow_controller->GetShadowForWindow(window2.get()); |
| ASSERT_TRUE(shadow); |
| EXPECT_EQ(shadow->desired_elevation(), ::wm::kShadowElevationActiveWindow); |
| EXPECT_TRUE(shadow_controller->IsShadowVisibleForWindow(window2.get())); |
| |
| CompleteDrag(std::move(resizer)); |
| Shell::Get()->shadow_controller()->UpdateShadowForWindow(window2.get()); |
| shadow = shadow_controller->GetShadowForWindow(window2.get()); |
| ASSERT_TRUE(shadow); |
| EXPECT_FALSE(shadow_controller->IsShadowVisibleForWindow(window2.get())); |
| } |
| |
| // Test that if the source window needs to be scaled up/down because of dragging |
| // a tab window out of it, other windows' visibilities and the home launcher's |
| // visibility should change accordingly. |
| TEST_F(SplitViewTabDraggingTest, SourceWindowBackgroundTest) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| std::unique_ptr<aura::Window> window2( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| std::unique_ptr<aura::Window> window3( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| std::unique_ptr<aura::Window> window4( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| EXPECT_TRUE(window1->IsVisible()); |
| EXPECT_TRUE(window2->IsVisible()); |
| EXPECT_TRUE(window3->IsVisible()); |
| EXPECT_TRUE(window4->IsVisible()); |
| |
| if (IsTabletMode()) |
| EXPECT_TRUE(Shell::Get()->app_list_controller()->IsVisible()); |
| |
| // 1) Start dragging |window1|. |window2| is the source window. |
| std::unique_ptr<WindowResizer> resizer = |
| StartDrag(window1.get(), window2.get()); |
| DragWindowWithOffset(resizer.get(), 10, 10); |
| |
| // Test that |window3| should be hidden now. |window1| and |window2| should |
| // stay visible during dragging. |
| EXPECT_TRUE(window1->IsVisible()); |
| EXPECT_TRUE(window2->IsVisible()); |
| EXPECT_FALSE(window3->IsVisible()); |
| EXPECT_FALSE(window4->IsVisible()); |
| |
| // Test that home launcher should be dismissed. |
| if (IsTabletMode()) |
| EXPECT_FALSE(Shell::Get()->app_list_controller()->IsVisible()); |
| |
| // Test that during dragging, we could not show a hidden window. |
| window3->Show(); |
| EXPECT_FALSE(window3->IsVisible()); |
| |
| // After dragging, the windows' visibilities should have restored. |
| CompleteDrag(std::move(resizer)); |
| EXPECT_TRUE(window1->IsVisible()); |
| EXPECT_TRUE(window2->IsVisible()); |
| EXPECT_TRUE(window3->IsVisible()); |
| EXPECT_TRUE(window4->IsVisible()); |
| |
| // Test that home launcher should be reshown. |
| if (IsTabletMode()) |
| EXPECT_TRUE(Shell::Get()->app_list_controller()->IsVisible()); |
| } |
| |
| // Tests that the dragged window should be the active and top window if overview |
| // ended because of window drag. |
| TEST_F(SplitViewTabDraggingTest, OverviewEndedOnWindowDrag) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| std::unique_ptr<aura::Window> window2( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| |
| // Drags |window2| to overview. |
| std::unique_ptr<WindowResizer> resizer = |
| StartDrag(window2.get(), window2.get()); |
| gfx::Rect drop_target_bounds = GetDropTargetBoundsDuringDrag(window1.get()); |
| DragWindowTo(resizer.get(), drop_target_bounds.CenterPoint()); |
| CompleteDrag(std::move(resizer)); |
| OverviewController* selector_controller = Shell::Get()->overview_controller(); |
| EXPECT_TRUE(selector_controller->IsSelecting()); |
| EXPECT_TRUE(selector_controller->overview_session()->IsWindowInOverview( |
| window2.get())); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::LEFT_SNAPPED); |
| |
| // Drags |window1| by a small distance. Both splitview and overview should be |
| // ended and |window1| is the active window and above |window2|. |
| resizer = StartDrag(window1.get(), window1.get()); |
| DragWindowTo(resizer.get(), gfx::Point(10, 10)); |
| CompleteDrag(std::move(resizer)); |
| EXPECT_FALSE(selector_controller->IsSelecting()); |
| EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive()); |
| EXPECT_TRUE(wm::GetWindowState(window1.get())->IsMaximized()); |
| EXPECT_TRUE(wm::GetWindowState(window2.get())->IsMaximized()); |
| EXPECT_TRUE(wm::GetWindowState(window1.get())->IsActive()); |
| EXPECT_FALSE(wm::GetWindowState(window2.get())->IsActive()); |
| // |window1| should above |window2|. |
| const aura::Window::Windows windows = window1->parent()->children(); |
| auto window1_layer = std::find(windows.begin(), windows.end(), window1.get()); |
| auto window2_layer = std::find(windows.begin(), windows.end(), window2.get()); |
| EXPECT_TRUE(window1_layer > window2_layer); |
| } |
| |
| // When tab dragging a window, the dragged window might need to merge back into |
| // the source window when the drag ends. Tests the related functionalities. |
| TEST_F(SplitViewTabDraggingTest, MergeBackToSourceWindow) { |
| UpdateDisplay("600x600"); |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> dragged_window( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| std::unique_ptr<aura::Window> source_window( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| |
| // 1. If splitview is not active and the dragged window is not the source |
| // window. |
| // a. Drag the window to less than half of the display height, and not in the |
| // snap preview area. |
| std::unique_ptr<WindowResizer> resizer = |
| StartDrag(dragged_window.get(), source_window.get()); |
| ASSERT_TRUE(resizer.get()); |
| EXPECT_FALSE( |
| source_window->GetProperty(kIsDeferredTabDraggingTargetWindowKey)); |
| DragWindowTo(resizer.get(), gfx::Point(300, 200)); |
| CompleteDrag(std::move(resizer)); |
| EXPECT_TRUE( |
| source_window->GetProperty(kIsDeferredTabDraggingTargetWindowKey)); |
| source_window->ClearProperty(kIsDeferredTabDraggingTargetWindowKey); |
| |
| // b. Drag the window to more than half of the display height and not in the |
| // snap preview area. |
| resizer = StartDrag(dragged_window.get(), source_window.get()); |
| ASSERT_TRUE(resizer.get()); |
| EXPECT_FALSE( |
| source_window->GetProperty(kIsDeferredTabDraggingTargetWindowKey)); |
| DragWindowTo(resizer.get(), gfx::Point(300, 500)); |
| CompleteDrag(std::move(resizer)); |
| EXPECT_FALSE( |
| source_window->GetProperty(kIsDeferredTabDraggingTargetWindowKey)); |
| |
| // c. Drag the window to the snap preview area. |
| resizer = StartDrag(dragged_window.get(), source_window.get()); |
| ASSERT_TRUE(resizer.get()); |
| EXPECT_FALSE( |
| source_window->GetProperty(kIsDeferredTabDraggingTargetWindowKey)); |
| DragWindowTo(resizer.get(), gfx::Point(0, 200)); |
| CompleteDrag(std::move(resizer)); |
| EXPECT_FALSE( |
| source_window->GetProperty(kIsDeferredTabDraggingTargetWindowKey)); |
| EndSplitView(); |
| |
| // d. The dragged window is already added into overview before drag ends: |
| resizer = StartDrag(dragged_window.get(), source_window.get()); |
| ASSERT_TRUE(resizer.get()); |
| EXPECT_FALSE( |
| source_window->GetProperty(kIsDeferredTabDraggingTargetWindowKey)); |
| DragWindowTo(resizer.get(), gfx::Point(300, 200)); |
| dragged_window->SetProperty(kIsShowingInOverviewKey, true); |
| CompleteDrag(std::move(resizer)); |
| EXPECT_FALSE( |
| source_window->GetProperty(kIsDeferredTabDraggingTargetWindowKey)); |
| dragged_window->ClearProperty(kIsShowingInOverviewKey); |
| |
| // 2. If splitview is active and the dragged window is not the source window. |
| // a. Drag the window to less than half of the display height, in the same |
| // split of the source window, and not in the snap preview area. |
| split_view_controller()->SnapWindow(source_window.get(), |
| SplitViewController::LEFT); |
| resizer = StartDrag(dragged_window.get(), source_window.get()); |
| EXPECT_FALSE( |
| source_window->GetProperty(kIsDeferredTabDraggingTargetWindowKey)); |
| DragWindowTo(resizer.get(), gfx::Point(0, 200)); |
| CompleteDrag(std::move(resizer)); |
| EXPECT_TRUE( |
| source_window->GetProperty(kIsDeferredTabDraggingTargetWindowKey)); |
| EndSplitView(); |
| source_window->ClearProperty(kIsDeferredTabDraggingTargetWindowKey); |
| |
| // b. Drag the window to less than half of the display height, in the |
| // different split of the source window, and not in the snap preview area. |
| split_view_controller()->SnapWindow(source_window.get(), |
| SplitViewController::LEFT); |
| resizer = StartDrag(dragged_window.get(), source_window.get()); |
| EXPECT_FALSE( |
| source_window->GetProperty(kIsDeferredTabDraggingTargetWindowKey)); |
| DragWindowTo(resizer.get(), gfx::Point(500, 200)); |
| CompleteDrag(std::move(resizer)); |
| EXPECT_FALSE( |
| source_window->GetProperty(kIsDeferredTabDraggingTargetWindowKey)); |
| EndSplitView(); |
| |
| // c. Drag the window to move a small distance, but is still in the different |
| // split of the source window, and not in the snap preview area. |
| split_view_controller()->SnapWindow(source_window.get(), |
| SplitViewController::LEFT); |
| resizer = StartDrag(dragged_window.get(), source_window.get()); |
| EXPECT_FALSE( |
| source_window->GetProperty(kIsDeferredTabDraggingTargetWindowKey)); |
| DragWindowTo(resizer.get(), gfx::Point(500, 20)); |
| CompleteDrag(std::move(resizer)); |
| EXPECT_FALSE( |
| source_window->GetProperty(kIsDeferredTabDraggingTargetWindowKey)); |
| EndSplitView(); |
| |
| // d. The dragged window was added to overview before the drag ends. |
| split_view_controller()->SnapWindow(source_window.get(), |
| SplitViewController::LEFT); |
| resizer = StartDrag(dragged_window.get(), source_window.get()); |
| EXPECT_FALSE( |
| source_window->GetProperty(kIsDeferredTabDraggingTargetWindowKey)); |
| DragWindowTo(resizer.get(), gfx::Point(0, 200)); |
| dragged_window->SetProperty(kIsShowingInOverviewKey, true); |
| CompleteDrag(std::move(resizer)); |
| EXPECT_FALSE( |
| source_window->GetProperty(kIsDeferredTabDraggingTargetWindowKey)); |
| dragged_window->ClearProperty(kIsShowingInOverviewKey); |
| } |
| |
| // Tests that if window being dragged into drop target when preview area is |
| // shown, window should go to be snapped instead of being dropped into overview. |
| TEST_F(SplitViewTabDraggingTest, DragWindowIntoPreviewAreaAndDropTarget) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> browser_window( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| wm::GetWindowState(browser_window.get())->Maximize(); |
| |
| std::unique_ptr<WindowResizer> resizer = |
| StartDrag(browser_window.get(), browser_window.get()); |
| gfx::Rect drop_target_bounds = |
| GetDropTargetBoundsDuringDrag(browser_window.get()); |
| // Drag window to inside the drop target. |
| DragWindowTo(resizer.get(), gfx::Point(drop_target_bounds.x() + 5, |
| drop_target_bounds.y() + 5)); |
| EXPECT_EQ(GetIndicatorState(resizer.get()), IndicatorState::kPreviewAreaLeft); |
| CompleteDrag(std::move(resizer)); |
| EXPECT_EQ(SplitViewController::LEFT_SNAPPED, |
| split_view_controller()->state()); |
| } |
| |
| // Tests that if a fling event happens on a tab, the tab might or might not |
| // merge back into the source window depending on the fling event velocity. |
| TEST_F(SplitViewTabDraggingTest, FlingTest) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> dragged_window( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| std::unique_ptr<aura::Window> source_window( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| |
| std::unique_ptr<WindowResizer> resizer = |
| StartDrag(dragged_window.get(), source_window.get()); |
| ASSERT_TRUE(resizer.get()); |
| Fling(std::move(resizer), /*velocity_y=*/3000.f); |
| EXPECT_FALSE( |
| source_window->GetProperty(kIsDeferredTabDraggingTargetWindowKey)); |
| |
| resizer = StartDrag(dragged_window.get(), source_window.get()); |
| ASSERT_TRUE(resizer.get()); |
| Fling(std::move(resizer), /*velocity_y=*/1000.f); |
| EXPECT_TRUE( |
| source_window->GetProperty(kIsDeferredTabDraggingTargetWindowKey)); |
| } |
| |
| // Tests that in various cases, after the tab drag ends, the dragged window and |
| // the source window should have correct bounds. |
| TEST_F(SplitViewTabDraggingTest, BoundsTest) { |
| UpdateDisplay("600x600"); |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| std::unique_ptr<aura::Window> window2( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| std::unique_ptr<aura::Window> window3( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| const gfx::Rect bounds1 = window1->bounds(); |
| const gfx::Rect bounds2 = window2->bounds(); |
| EXPECT_EQ(bounds1, bounds2); |
| |
| // 1. If splitview is not active and the dragged window is the source window. |
| std::unique_ptr<WindowResizer> resizer = |
| StartDrag(window1.get(), window1.get()); |
| // Drag for a small distance. |
| DragWindowWithOffset(resizer.get(), 10, 10); |
| EXPECT_NE(window1->bounds(), bounds1); |
| CompleteDrag(std::move(resizer)); |
| // The window should be maximized again and the bounds should restore to its |
| // maximized window size. |
| EXPECT_TRUE(wm::GetWindowState(window1.get())->IsMaximized()); |
| EXPECT_EQ(window1->bounds(), bounds1); |
| |
| // 2. If splitview is not active and the dragged window is not the source |
| // window. |
| resizer = StartDrag(window1.get(), window2.get()); |
| // a). Drag for a small distance. |
| DragWindowWithOffset(resizer.get(), 10, 10); |
| EXPECT_NE(window1->bounds(), bounds1); |
| EXPECT_EQ(window2->bounds(), bounds2); |
| // Now drag for a longer distance so that the source window scales down. |
| DragWindowTo(resizer.get(), gfx::Point(300, 200)); |
| EXPECT_NE(window2->bounds(), bounds2); |
| CompleteDrag(std::move(resizer)); |
| // As in this case the dragged window should merge back to source window, |
| // which we can't test here. We only test the source window's bounds restore |
| // to its maximized window size. |
| EXPECT_TRUE(window2->GetProperty(kIsDeferredTabDraggingTargetWindowKey)); |
| EXPECT_EQ(window2->bounds(), bounds2); |
| window2->ClearProperty(kIsDeferredTabDraggingTargetWindowKey); |
| |
| // b) Drag the window far enough so that the dragged window doesn't merge back |
| // into the source window. |
| resizer = StartDrag(window1.get(), window2.get()); |
| DragWindowTo(resizer.get(), gfx::Point(300, 400)); |
| EXPECT_NE(window1->bounds(), bounds1); |
| EXPECT_NE(window2->bounds(), bounds2); |
| CompleteDrag(std::move(resizer)); |
| EXPECT_FALSE(window2->GetProperty(kIsDeferredTabDraggingTargetWindowKey)); |
| EXPECT_EQ(window1->bounds(), bounds1); |
| EXPECT_EQ(window2->bounds(), bounds2); |
| |
| // 3. If splitview is active and the dragged window is the source window. |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| const gfx::Rect snapped_bounds1 = window1->bounds(); |
| const gfx::Rect snapped_bounds2 = window2->bounds(); |
| resizer = StartDrag(window1.get(), window1.get()); |
| // Drag the window for a small distance and release. |
| DragWindowWithOffset(resizer.get(), 10, 10); |
| EXPECT_NE(window1->bounds(), snapped_bounds1); |
| EXPECT_EQ(window2->bounds(), snapped_bounds2); |
| CompleteDrag(std::move(resizer)); |
| EXPECT_EQ(window1->bounds(), snapped_bounds1); |
| EXPECT_EQ(window2->bounds(), snapped_bounds2); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::BOTH_SNAPPED); |
| |
| // 4. If splitview is active and the dragged window is not the source window. |
| resizer = StartDrag(window3.get(), window1.get()); |
| // a). Drag the window for a small distance and release. |
| DragWindowWithOffset(resizer.get(), 10, 10); |
| EXPECT_EQ(window1->bounds(), snapped_bounds1); |
| EXPECT_EQ(window2->bounds(), snapped_bounds2); |
| // Drag the window for a long distance (but is still in merge-back distance |
| // range), the source window should not scale down. |
| DragWindowTo(resizer.get(), gfx::Point(100, 200)); |
| EXPECT_EQ(window1->bounds(), snapped_bounds1); |
| EXPECT_EQ(window2->bounds(), snapped_bounds2); |
| CompleteDrag(std::move(resizer)); |
| // In this case |window3| is supposed to merge back its source window |
| // |window1|, so we only test the source window's bounds here. |
| EXPECT_EQ(window1->bounds(), snapped_bounds1); |
| EXPECT_EQ(window2->bounds(), snapped_bounds2); |
| EXPECT_TRUE(window1->GetProperty(kIsDeferredTabDraggingTargetWindowKey)); |
| window1->ClearProperty(kIsDeferredTabDraggingTargetWindowKey); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::BOTH_SNAPPED); |
| |
| // b). Drag the window far enough so that the dragged window doesn't merge |
| // back into its source window. |
| resizer = StartDrag(window3.get(), window1.get()); |
| DragWindowTo(resizer.get(), gfx::Point(100, 400)); |
| EXPECT_EQ(window1->bounds(), snapped_bounds1); |
| EXPECT_EQ(window2->bounds(), snapped_bounds2); |
| CompleteDrag(std::move(resizer)); |
| EXPECT_FALSE(window1->GetProperty(kIsDeferredTabDraggingTargetWindowKey)); |
| // |window3| replaced |window1| as the left snapped window. |
| EXPECT_EQ(window3->bounds(), snapped_bounds1); |
| EXPECT_EQ(window2->bounds(), snapped_bounds2); |
| } |
| |
| // Tests that press overview key in keyboard during drag should not put the |
| // dragged window into overview. |
| TEST_F(SplitViewTabDraggingTest, PressOverviewKeyDuringDrag) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> dragged_window( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| wm::GetWindowState(dragged_window.get())->Maximize(); |
| std::unique_ptr<WindowResizer> resizer = |
| StartDrag(dragged_window.get(), dragged_window.get()); |
| DragWindowTo(resizer.get(), gfx::Point(300, 300)); |
| EXPECT_TRUE(wm::GetWindowState(dragged_window.get())->is_dragged()); |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| GetEventGenerator()->PressKey(ui::VKEY_MEDIA_LAUNCH_APP1, ui::EF_NONE); |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| EXPECT_FALSE(Shell::Get() |
| ->overview_controller() |
| ->overview_session() |
| ->IsWindowInOverview(dragged_window.get())); |
| EXPECT_TRUE(wm::GetWindowState(dragged_window.get())->is_dragged()); |
| resizer->CompleteDrag(); |
| } |
| |
| // Tests that if the dragged window is activated after the drag ends, but before |
| // the dragged window gets snapped, the divider bar is placed correctly above |
| // the snapped windows. |
| TEST_F(SplitViewTabDraggingTest, DragActiveWindow) { |
| UpdateDisplay("600x600"); |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> window1( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| std::unique_ptr<aura::Window> window2( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| std::unique_ptr<aura::Window> window3( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| |
| split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT); |
| ToggleOverview(); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), true); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::LEFT_SNAPPED); |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| |
| std::unique_ptr<aura::Window> dragged_window( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), true); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::LEFT_SNAPPED); |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| |
| std::unique_ptr<WindowResizer> resizer = |
| StartDrag(dragged_window.get(), window1.get()); |
| |
| // Drag window to the other side of the split screen. |
| DragWindowTo(resizer.get(), gfx::Point(400, 600)); |
| resizer->CompleteDrag(); |
| |
| // To simulate what might happen in real situation, we activate the dragged |
| // window first before clearing the window's tab dragging properties. |
| wm::ActivateWindow(dragged_window.get()); |
| SetIsInTabDragging(resizer->GetTarget(), /*is_dragging=*/false); |
| |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::BOTH_SNAPPED); |
| EXPECT_FALSE(Shell::Get()->overview_controller()->IsSelecting()); |
| EXPECT_TRUE(split_view_divider()->divider_widget()->IsAlwaysOnTop()); |
| } |
| |
| // Tests that the divider bar should be placed on top after the drag ends, no |
| // matter the dragged window is destroyed during the drag or not. |
| TEST_F(SplitViewTabDraggingTest, DividerBarOnTopAfterDragEnds) { |
| const gfx::Rect bounds(0, 0, 400, 400); |
| std::unique_ptr<aura::Window> dragged_window( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| std::unique_ptr<aura::Window> another_window( |
| CreateWindowWithType(bounds, AppType::BROWSER)); |
| split_view_controller()->SnapWindow(dragged_window.get(), |
| SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(another_window.get(), |
| SplitViewController::RIGHT); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::BOTH_SNAPPED); |
| |
| // If the dragged window stays as a separate window after drag ends: |
| std::unique_ptr<WindowResizer> resizer = |
| StartDrag(dragged_window.get(), dragged_window.get()); |
| DragWindowWithOffset(resizer.get(), 10, 10); |
| CompleteDrag(std::move(resizer)); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::BOTH_SNAPPED); |
| EXPECT_TRUE(split_view_divider()->divider_widget()->IsAlwaysOnTop()); |
| |
| // If the dragged window is destroyed after drag ends: |
| resizer = StartDrag(dragged_window.get(), dragged_window.get()); |
| DragWindowWithOffset(resizer.get(), 10, 10); |
| resizer->CompleteDrag(); |
| resizer.reset(); |
| dragged_window.reset(); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::RIGHT_SNAPPED); |
| EXPECT_TRUE(Shell::Get()->overview_controller()->IsSelecting()); |
| EXPECT_TRUE(split_view_divider()->divider_widget()->IsAlwaysOnTop()); |
| } |
| |
| class TestWindowDelegateWithWidget : public views::WidgetDelegate { |
| public: |
| TestWindowDelegateWithWidget(bool can_activate) |
| : can_activate_(can_activate) {} |
| ~TestWindowDelegateWithWidget() override = default; |
| |
| // views::WidgetDelegate: |
| void DeleteDelegate() override { delete this; } |
| views::Widget* GetWidget() override { return widget_; } |
| const views::Widget* GetWidget() const override { return widget_; } |
| bool CanActivate() const override { return can_activate_; } |
| bool CanResize() const override { return true; } |
| bool CanMaximize() const override { return true; } |
| bool ShouldAdvanceFocusToTopLevelWidget() const override { return true; } |
| |
| void set_widget(views::Widget* widget) { widget_ = widget; } |
| |
| private: |
| bool can_activate_ = false; |
| views::Widget* widget_ = nullptr; |
| |
| DISALLOW_COPY_AND_ASSIGN(TestWindowDelegateWithWidget); |
| }; |
| |
| class SplitViewAppDraggingTest : public SplitViewControllerTest { |
| public: |
| SplitViewAppDraggingTest() = default; |
| ~SplitViewAppDraggingTest() override = default; |
| |
| // SplitViewControllerTest: |
| void TearDown() override { |
| window_.reset(); |
| controller_.reset(); |
| SplitViewControllerTest::TearDown(); |
| } |
| |
| protected: |
| std::unique_ptr<aura::Window> CreateTestWindowWithWidget(bool can_activate) { |
| views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW); |
| params.show_state = ui::SHOW_STATE_MAXIMIZED; |
| views::Widget* widget = new views::Widget; |
| std::unique_ptr<TestWindowDelegateWithWidget> widget_delegate = |
| std::make_unique<TestWindowDelegateWithWidget>(can_activate); |
| widget_delegate->set_widget(widget); |
| params.delegate = widget_delegate.release(); |
| params.context = CurrentContext(); |
| widget->Init(params); |
| widget->Show(); |
| return base::WrapUnique<aura::Window>(widget->GetNativeView()); |
| } |
| |
| void InitializeWindow(bool can_activate = true) { |
| window_ = CreateTestWindowWithWidget(can_activate); |
| } |
| |
| // Sends a gesture scroll sequence to TabletModeAppWindowDragController. |
| void SendGestureEvents(const gfx::Point& location) { |
| SendScrollStartAndUpdate(location); |
| EndScrollSequence(); |
| } |
| |
| void SendScrollStartAndUpdate(const gfx::Point& location) { |
| wm::WindowState* window_state = wm::GetWindowState(window()); |
| window_state->CreateDragDetails(location, HTCAPTION, |
| ::wm::WINDOW_MOVE_SOURCE_TOUCH); |
| controller_ = std::make_unique<TabletModeWindowDragController>( |
| window_state, std::make_unique<TabletModeBrowserWindowDragDelegate>()); |
| controller_->drag_delegate_for_testing() |
| ->set_drag_start_deadline_for_testing(base::Time::Now()); |
| controller_->Drag(location, 0); |
| } |
| |
| void EndScrollSequence() { |
| controller_->CompleteDrag(); |
| wm::GetWindowState(window())->DeleteDragDetails(); |
| } |
| |
| void Fling(const gfx::Point& location, float velocity_y, float velocity_x) { |
| ui::GestureEvent event = ui::GestureEvent( |
| location.x(), location.y(), ui::EF_NONE, base::TimeTicks::Now(), |
| ui::GestureEventDetails(ui::ET_SCROLL_FLING_START, velocity_x, |
| velocity_y)); |
| ui::Event::DispatcherApi(&event).set_target(window()); |
| controller_->FlingOrSwipe(&event); |
| wm::GetWindowState(window())->DeleteDragDetails(); |
| } |
| |
| IndicatorState GetIndicatorState() { |
| return controller_->drag_delegate_for_testing() |
| ->split_view_drag_indicators_for_testing() |
| ->current_indicator_state(); |
| } |
| |
| aura::Window* window() { return window_.get(); } |
| |
| std::unique_ptr<TabletModeWindowDragController> controller_; |
| |
| std::unique_ptr<aura::Window> window_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(SplitViewAppDraggingTest); |
| }; |
| |
| // Tests that drag the window that cannot be snapped from top of the display |
| // will not snap the window into splitscreen. |
| TEST_F(SplitViewAppDraggingTest, DragNonActiveMaximizedWindow) { |
| UpdateDisplay("800x600"); |
| InitializeWindow(false); |
| EXPECT_TRUE(wm::GetWindowState(window())->IsMaximized()); |
| gfx::Rect display_bounds = |
| screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer( |
| window()); |
| const float long_scroll_delta = display_bounds.height() / 4 + 5; |
| |
| const gfx::Point location(0, long_scroll_delta); |
| // Drag the window that cannot be snapped long enough, the window will be |
| // dropped into overview. |
| SendScrollStartAndUpdate(location); |
| OverviewController* overview_controller = Shell::Get()->overview_controller(); |
| EXPECT_TRUE(overview_controller->IsSelecting()); |
| EXPECT_FALSE( |
| overview_controller->overview_session()->IsWindowInOverview(window())); |
| EndScrollSequence(); |
| EXPECT_TRUE(overview_controller->IsSelecting()); |
| EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive()); |
| EXPECT_TRUE( |
| overview_controller->overview_session()->IsWindowInOverview(window())); |
| } |
| |
| // Tests the functionalities that are related to dragging a maximized window |
| // into splitscreen. |
| TEST_F(SplitViewAppDraggingTest, DragActiveMaximizedWindow) { |
| UpdateDisplay("800x600"); |
| InitializeWindow(); |
| EXPECT_TRUE(wm::GetWindowState(window())->IsMaximized()); |
| gfx::Rect display_bounds = |
| screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer( |
| window()); |
| |
| // Move the window by a small amount of distance will maximize the window |
| // again. |
| gfx::Point location(0, 10); |
| SendGestureEvents(location); |
| EXPECT_TRUE(wm::GetWindowState(window())->IsMaximized()); |
| |
| // Drag the window long enough (pass one fourth of the screen vertical |
| // height) to snap the window to splitscreen. |
| const float long_scroll_delta = display_bounds.height() / 4 + 5; |
| location.set_y(long_scroll_delta); |
| SendScrollStartAndUpdate(location); |
| OverviewController* overview_controller = Shell::Get()->overview_controller(); |
| EXPECT_TRUE(overview_controller->IsSelecting()); |
| EXPECT_FALSE( |
| overview_controller->overview_session()->IsWindowInOverview(window())); |
| EndScrollSequence(); |
| EXPECT_TRUE(overview_controller->IsSelecting()); |
| EXPECT_TRUE(split_view_controller()->IsSplitViewModeActive()); |
| EXPECT_EQ(split_view_controller()->left_window(), window()); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::LEFT_SNAPPED); |
| EXPECT_TRUE(wm::GetWindowState(window())->IsSnapped()); |
| |
| // FLING the window with small velocity (smaller than |
| // kFlingToOverviewThreshold) will not able to drop the window into overview. |
| location.set_y(10); |
| SendScrollStartAndUpdate(location); |
| overview_controller = Shell::Get()->overview_controller(); |
| EXPECT_TRUE(overview_controller->IsSelecting()); |
| Fling(location, |
| TabletModeWindowDragDelegate::kFlingToOverviewThreshold - 10.f, 0); |
| EXPECT_FALSE(overview_controller->IsSelecting()); |
| |
| // FLING the window with large velocity (larger than |
| // kFlingToOverviewThreshold) will drop the window into overview. |
| SendScrollStartAndUpdate(location); |
| overview_controller = Shell::Get()->overview_controller(); |
| EXPECT_TRUE(overview_controller->IsSelecting()); |
| Fling(location, |
| TabletModeWindowDragDelegate::kFlingToOverviewThreshold + 10.f, 0); |
| EXPECT_TRUE(overview_controller->IsSelecting()); |
| } |
| |
| // Tests the shelf visibility when a fullscreened window is being dragged. |
| TEST_F(SplitViewAppDraggingTest, ShelfVisibilityIfDraggingFullscreenedWindow) { |
| UpdateDisplay("800x600"); |
| InitializeWindow(); |
| ShelfLayoutManager* shelf_layout_manager = |
| AshTestBase::GetPrimaryShelf()->shelf_layout_manager(); |
| gfx::Rect display_bounds = |
| screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer( |
| window()); |
| |
| // Shelf will be auto-hidden if the window requests to be fullscreened. |
| wm::WindowState* window_state = wm::GetWindowState(window()); |
| const wm::WMEvent fullscreen_event(wm::WM_EVENT_TOGGLE_FULLSCREEN); |
| window_state->OnWMEvent(&fullscreen_event); |
| window_state->SetHideShelfWhenFullscreen(false); |
| window()->SetProperty(kImmersiveIsActive, true); |
| shelf_layout_manager->UpdateVisibilityState(); |
| EXPECT_TRUE(window_state->IsFullscreen()); |
| EXPECT_FALSE(shelf_layout_manager->IsVisible()); |
| |
| // Drag the window by a small amount of distance, the window will back to |
| // fullscreened, and shelf will be hidden again. |
| gfx::Point location(0, 10); |
| SendGestureEvents(location); |
| shelf_layout_manager->UpdateVisibilityState(); |
| EXPECT_TRUE(window_state->IsFullscreen()); |
| EXPECT_FALSE(shelf_layout_manager->IsVisible()); |
| |
| // Shelf is visible during dragging. |
| location.set_y(display_bounds.height() / 4 + 5); |
| SendScrollStartAndUpdate(location); |
| EXPECT_TRUE(shelf_layout_manager->IsVisible()); |
| EndScrollSequence(); |
| EXPECT_TRUE(split_view_controller()->IsSplitViewModeActive()); |
| EXPECT_TRUE(wm::GetWindowState(window())->IsSnapped()); |
| EXPECT_TRUE(shelf_layout_manager->IsVisible()); |
| } |
| |
| // Tests the auto-hide shelf state during window dragging. |
| TEST_F(SplitViewAppDraggingTest, AutoHideShelf) { |
| UpdateDisplay("800x600"); |
| Shelf* shelf = GetPrimaryShelf(); |
| shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER); |
| InitializeWindow(); |
| gfx::Rect display_bounds = |
| display::Screen::GetScreen()->GetPrimaryDisplay().bounds(); |
| ShelfLayoutManager* shelf_layout_manager = |
| AshTestBase::GetPrimaryShelf()->shelf_layout_manager(); |
| shelf_layout_manager->LayoutShelf(); |
| EXPECT_EQ(SHELF_VISIBLE, shelf->GetVisibilityState()); |
| EXPECT_TRUE(wm::GetWindowState(window())->IsMaximized()); |
| |
| shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS); |
| shelf_layout_manager->LayoutShelf(); |
| EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState()); |
| EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->GetAutoHideState()); |
| |
| const gfx::Point location(0, display_bounds.height() / 4 + 5); |
| SendScrollStartAndUpdate(location); |
| EXPECT_EQ(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS, shelf->auto_hide_behavior()); |
| EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState()); |
| // Shelf should be shown during drag. |
| EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState()); |
| EndScrollSequence(); |
| EXPECT_EQ(split_view_controller()->state(), |
| SplitViewController::LEFT_SNAPPED); |
| EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState()); |
| // Shelf should be shown after drag and snapped window should be covered by |
| // the auto-hide-shown shelf. |
| EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState()); |
| EXPECT_EQ( |
| split_view_controller() |
| ->GetSnappedWindowBoundsInScreen(window(), SplitViewController::LEFT) |
| .height(), |
| display_bounds.height()); |
| } |
| |
| // Tests the functionalities that fling the window when preview area is shown. |
| TEST_F(SplitViewAppDraggingTest, FlingWhenPreviewAreaIsShown) { |
| InitializeWindow(); |
| EXPECT_TRUE(wm::GetWindowState(window())->IsMaximized()); |
| gfx::Rect display_bounds = |
| screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer( |
| window()); |
| |
| const float long_scroll_delta = display_bounds.height() / 4 + 5; |
| float large_velocity = |
| TabletModeWindowDragDelegate::kFlingToOverviewFromSnappingAreaThreshold + |
| 10.f; |
| float small_velocity = |
| TabletModeWindowDragDelegate::kFlingToOverviewFromSnappingAreaThreshold - |
| 10.f; |
| |
| // Fling to the right with large enough velocity when trying to snap the |
| // window to the left should drop the window to overview. |
| gfx::Point location(0, long_scroll_delta); |
| SendScrollStartAndUpdate(location); |
| EXPECT_EQ(IndicatorState::kPreviewAreaLeft, GetIndicatorState()); |
| Fling(location, /*velocity_y*/ 0, /*velocity_x=*/large_velocity); |
| OverviewController* overview_controller = Shell::Get()->overview_controller(); |
| OverviewSession* overview_session = overview_controller->overview_session(); |
| EXPECT_TRUE(overview_controller->IsSelecting()); |
| EXPECT_TRUE(overview_session->IsWindowInOverview(window())); |
| ToggleOverview(); |
| EXPECT_TRUE(wm::GetWindowState(window())->IsMaximized()); |
| |
| // Fling to the right with small velocity when trying to snap the |
| // window to the left should still snap the window to left. |
| SendScrollStartAndUpdate(location); |
| EXPECT_EQ(IndicatorState::kPreviewAreaLeft, GetIndicatorState()); |
| Fling(location, /*velocity_y*/ 0, /*velocity_x=*/small_velocity); |
| EXPECT_TRUE(wm::GetWindowState(window())->IsSnapped()); |
| EndSplitView(); |
| EXPECT_TRUE(wm::GetWindowState(window())->IsMaximized()); |
| |
| // Fling to the left with large enough velocity when trying to snap the window |
| // to the right should drop the window to overvie. |
| location = gfx::Point(display_bounds.right(), long_scroll_delta); |
| SendScrollStartAndUpdate(location); |
| EXPECT_EQ(IndicatorState::kPreviewAreaRight, GetIndicatorState()); |
| Fling(location, /*velocity_y*/ 0, /*velocity_x=*/-large_velocity); |
| overview_session = overview_controller->overview_session(); |
| EXPECT_TRUE(overview_controller->IsSelecting()); |
| EXPECT_TRUE(overview_session->IsWindowInOverview(window())); |
| ToggleOverview(); |
| |
| // Fling to the left with small velocity when trying to snap the |
| // window to the right should still snap the window to right. |
| SendScrollStartAndUpdate(location); |
| EXPECT_EQ(IndicatorState::kPreviewAreaRight, GetIndicatorState()); |
| Fling(location, /*velocity_y*/ 0, /*velocity_x=*/-small_velocity); |
| EXPECT_TRUE(wm::GetWindowState(window())->IsSnapped()); |
| } |
| |
| // Tests the functionalities that fling a window when splitview is active. |
| TEST_F(SplitViewAppDraggingTest, FlingWhenSplitViewIsActive) { |
| InitializeWindow(); |
| std::unique_ptr<aura::Window> window2 = CreateTestWindowWithWidget(true); |
| |
| split_view_controller()->SnapWindow(window(), SplitViewController::LEFT); |
| split_view_controller()->SnapWindow(window2.get(), |
| SplitViewController::RIGHT); |
| |
| gfx::Rect display_bounds = |
| screen_util::GetDisplayWorkAreaBoundsInScreenForDefaultContainer( |
| window()); |
| const float long_scroll_y = display_bounds.bottom() - 10; |
| float large_velocity = |
| TabletModeWindowDragDelegate::kFlingToOverviewFromSnappingAreaThreshold + |
| 10.f; |
| |
| // Fling the window in left snapping area to left should still snap the |
| // window. |
| gfx::Point location(0, long_scroll_y); |
| SendScrollStartAndUpdate(location); |
| Fling(location, /*velocity_y=*/0, /*velocity_x=*/-large_velocity); |
| EXPECT_TRUE(wm::GetWindowState(window())->IsSnapped()); |
| EXPECT_EQ(SplitViewController::BOTH_SNAPPED, |
| split_view_controller()->state()); |
| |
| // Fling the window in left snapping area to right should drop the window |
| // into overview. |
| SendScrollStartAndUpdate(location); |
| Fling(location, /*velocity_y=*/0, /*velocity_x=*/large_velocity); |
| OverviewController* selector_controller = Shell::Get()->overview_controller(); |
| EXPECT_TRUE( |
| selector_controller->overview_session()->IsWindowInOverview(window())); |
| EXPECT_EQ(SplitViewController::RIGHT_SNAPPED, |
| split_view_controller()->state()); |
| EXPECT_TRUE(IsTabletMode()); |
| ToggleOverview(); |
| EXPECT_TRUE(IsTabletMode()); |
| |
| // Fling the window in right snapping area to left should drop the window into |
| // overview. |
| EXPECT_EQ(SplitViewController::BOTH_SNAPPED, |
| split_view_controller()->state()); |
| location.set_x(display_bounds.CenterPoint().x() + 10); |
| SendScrollStartAndUpdate(location); |
| Fling(location, /*velocity_y=*/0, /*velocity_x=*/-large_velocity); |
| EXPECT_TRUE( |
| selector_controller->overview_session()->IsWindowInOverview(window())); |
| EXPECT_EQ(SplitViewController::RIGHT_SNAPPED, |
| split_view_controller()->state()); |
| ToggleOverview(); |
| |
| // Fling the window in right snapping area to right should snap the window to |
| // right side. |
| EXPECT_EQ(SplitViewController::BOTH_SNAPPED, |
| split_view_controller()->state()); |
| SendScrollStartAndUpdate(location); |
| Fling(location, /*velocity_y=*/0, /*velocity_x=*/large_velocity); |
| EXPECT_EQ(split_view_controller()->right_window(), window()); |
| EXPECT_TRUE(selector_controller->overview_session()->IsWindowInOverview( |
| window2.get())); |
| EXPECT_EQ(SplitViewController::RIGHT_SNAPPED, |
| split_view_controller()->state()); |
| } |
| |
| } // namespace ash |