#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "ash/wm/overview/overview_controller.h"
#include "ash/wm/overview/overview_item_base.h"
#include "ash/wm/overview/overview_session.h"
#include "ash/wm/overview/overview_test_util.h"
#include "ash/wm/splitview/split_view_controller.h"
#include "ash/wm/splitview/split_view_utils.h"
#include "base/test/scoped_feature_list.h"
#include "ui/display/test/display_manager_test_api.h"
#include "ui/wm/core/window_util.h"
namespace ash {
class SplitViewMultiDisplayClamshellTest : public AshTestBase {
public:
SplitViewMultiDisplayClamshellTest() = default;
SplitViewMultiDisplayClamshellTest(
const SplitViewMultiDisplayClamshellTest&) = delete;
SplitViewMultiDisplayClamshellTest& operator=(
const SplitViewMultiDisplayClamshellTest&) = delete;
~SplitViewMultiDisplayClamshellTest() override = default;
display::Display GetPrimaryDisplay() {
return display::Screen::Get()->GetPrimaryDisplay();
}
display::Display GetSecondaryDisplay() {
return display::test::DisplayManagerTestApi(display_manager())
.GetSecondaryDisplay();
}
gfx::Point GetDragPoint(aura::Window* window) {
gfx::Point drag_point;
if (auto* overview_session = OverviewController::Get()->overview_session();
overview_session && overview_session->IsWindowInOverview(window)) {
drag_point = gfx::ToRoundedPoint(GetOverviewItemForWindow(window)
->GetTransformedBounds()
.CenterPoint());
} else {
drag_point = window->GetBoundsInScreen().top_center();
drag_point.Offset(0, 10);
}
return drag_point;
}
std::pair<gfx::Rect, gfx::Rect> GetExpectedSnappedBounds(
const display::Display& display) {
const gfx::Rect work_area(display.work_area());
gfx::Rect primary_bounds, secondary_bounds;
if (IsLayoutHorizontal(display)) {
work_area.SplitVertically(primary_bounds, secondary_bounds);
} else {
work_area.SplitHorizontally(primary_bounds, secondary_bounds);
}
return std::make_pair(primary_bounds, secondary_bounds);
}
};
TEST_F(SplitViewMultiDisplayClamshellTest, MoveWindowToDisplayShortcut) {
UpdateDisplay("1200x900,800x600");
display::test::DisplayManagerTestApi display_manager_test(display_manager());
std::unique_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 200, 200)));
std::unique_ptr<aura::Window> w2(CreateTestWindow(gfx::Rect(0, 0, 200, 200)));
std::unique_ptr<aura::Window> w3(CreateTestWindow(gfx::Rect(0, 0, 200, 200)));
std::unique_ptr<aura::Window> w4(
CreateTestWindow(gfx::Rect(1200, 0, 200, 200)));
auto* split_view_controller1 =
SplitViewController::Get(Shell::GetPrimaryRootWindow());
auto* split_view_controller2 =
SplitViewController::Get(Shell::GetRootWindowForDisplayId(
display_manager_test.GetSecondaryDisplay().id()));
auto* overview_controller = OverviewController::Get();
enum class TestCase { kFasterSplitScreenSetup, kOverviewDragToSnap };
const auto kTestCases = {TestCase::kFasterSplitScreenSetup,
TestCase::kOverviewDragToSnap};
for (const auto kTestCase : kTestCases) {
if (kTestCase == TestCase::kOverviewDragToSnap) {
ToggleOverview();
ASSERT_TRUE(overview_controller->InOverviewSession());
}
split_view_controller1->SnapWindow(
w1.get(), SnapPosition::kPrimary,
WindowSnapActionSource::kDragWindowToEdgeToSnap);
EXPECT_TRUE(overview_controller->InOverviewSession());
EXPECT_TRUE(split_view_controller1->InSplitViewMode());
EXPECT_FALSE(split_view_controller2->InSplitViewMode());
wm::ActivateWindow(w1.get());
PressAndReleaseKey(ui::VKEY_M, ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN);
const gfx::Rect work_area2 =
display_manager_test.GetSecondaryDisplay().work_area();
EXPECT_EQ(gfx::Rect(work_area2.x(), 0, work_area2.width() / 2,
work_area2.height()),
w1->GetBoundsInScreen());
EXPECT_FALSE(OverviewController::Get()->InOverviewSession());
EXPECT_FALSE(split_view_controller1->InSplitViewMode());
EXPECT_FALSE(split_view_controller2->InSplitViewMode());
if (kTestCase == TestCase::kOverviewDragToSnap) {
ToggleOverview();
ASSERT_TRUE(overview_controller->InOverviewSession());
}
split_view_controller2->SnapWindow(
w1.get(), SnapPosition::kSecondary,
WindowSnapActionSource::kDragWindowToEdgeToSnap);
EXPECT_TRUE(OverviewController::Get()->InOverviewSession());
EXPECT_FALSE(split_view_controller1->InSplitViewMode());
EXPECT_TRUE(split_view_controller2->InSplitViewMode());
wm::ActivateWindow(w1.get());
PressAndReleaseKey(ui::VKEY_M, ui::EF_COMMAND_DOWN | ui::EF_ALT_DOWN);
const gfx::Rect work_area1 =
display::Screen::Get()->GetPrimaryDisplay().work_area();
EXPECT_EQ(gfx::Rect(work_area1.width() / 2, 0, work_area1.width() / 2,
work_area1.height()),
w1->GetBoundsInScreen());
EXPECT_FALSE(OverviewController::Get()->InOverviewSession());
EXPECT_FALSE(split_view_controller2->InSplitViewMode());
EXPECT_FALSE(split_view_controller1->InSplitViewMode());
}
}
TEST_F(SplitViewMultiDisplayClamshellTest, SnapToCorrectDisplay) {
UpdateDisplay("800x600,800x600");
std::unique_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 400, 400)));
std::unique_ptr<aura::Window> w2(
CreateTestWindow(gfx::Rect(400, 0, 400, 400)));
auto* event_generator = GetEventGenerator();
event_generator->set_current_screen_location(GetDragPoint(w1.get()));
event_generator->DragMouseTo(0, 100);
EXPECT_EQ(GetExpectedSnappedBounds(GetPrimaryDisplay()).first,
w1->GetBoundsInScreen());
PressAndReleaseKey(ui::VKEY_ESCAPE, ui::EF_NONE);
event_generator->set_current_screen_location(GetDragPoint(w2.get()));
event_generator->DragMouseTo(799, 100);
EXPECT_EQ(GetExpectedSnappedBounds(GetPrimaryDisplay()).second,
w2->GetBoundsInScreen());
PressAndReleaseKey(ui::VKEY_ESCAPE, ui::EF_NONE);
event_generator->set_current_screen_location(GetDragPoint(w2.get()));
event_generator->DragMouseTo(800, 100);
EXPECT_EQ(GetExpectedSnappedBounds(GetSecondaryDisplay()).first,
w2->GetBoundsInScreen());
PressAndReleaseKey(ui::VKEY_ESCAPE, ui::EF_NONE);
event_generator->set_current_screen_location(GetDragPoint(w2.get()));
event_generator->DragMouseTo(799, 100);
EXPECT_EQ(GetExpectedSnappedBounds(GetPrimaryDisplay()).second,
w2->GetBoundsInScreen());
PressAndReleaseKey(ui::VKEY_ESCAPE, ui::EF_NONE);
event_generator->set_current_screen_location(GetDragPoint(w2.get()));
event_generator->DragMouseTo(800, 100);
EXPECT_EQ(GetExpectedSnappedBounds(GetSecondaryDisplay()).first,
w2->GetBoundsInScreen());
}
TEST_F(SplitViewMultiDisplayClamshellTest, SnapDifferentDisplaySizes) {
std::unique_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 200, 200)));
std::unique_ptr<aura::Window> w2(
CreateTestWindow(gfx::Rect(400, 0, 200, 200)));
auto* event_generator = GetEventGenerator();
const auto test_display_specs = {"800x600,1200x900", "1200x900,800x600",
"1024x768,1920x1080"};
for (const auto kTestDisplays : test_display_specs) {
UpdateDisplay(kTestDisplays);
SCOPED_TRACE(kTestDisplays);
wm::ActivateWindow(w1.get());
event_generator->MoveMouseTo(GetDragPoint(w1.get()));
const display::Display display1 = GetPrimaryDisplay();
const gfx::Rect work_area1 = display1.work_area();
event_generator->DragMouseTo(work_area1.origin());
EXPECT_EQ(GetExpectedSnappedBounds(display1).first,
w1->GetBoundsInScreen());
PressAndReleaseKey(ui::VKEY_ESCAPE, ui::EF_NONE);
wm::ActivateWindow(w2.get());
event_generator->MoveMouseTo(GetDragPoint(w2.get()));
event_generator->DragMouseTo(work_area1.right() - 1, work_area1.y());
EXPECT_EQ(GetExpectedSnappedBounds(display1).second,
w2->GetBoundsInScreen());
PressAndReleaseKey(ui::VKEY_ESCAPE, ui::EF_NONE);
event_generator->set_current_screen_location(GetDragPoint(w2.get()));
const display::Display display2 = GetSecondaryDisplay();
const gfx::Rect work_area2 = display2.work_area();
event_generator->DragMouseTo(work_area2.origin());
EXPECT_EQ(GetExpectedSnappedBounds(display2).first,
w2->GetBoundsInScreen());
PressAndReleaseKey(ui::VKEY_ESCAPE, ui::EF_NONE);
event_generator->set_current_screen_location(GetDragPoint(w2.get()));
event_generator->DragMouseTo(work_area1.right() - 1, work_area1.y());
EXPECT_EQ(GetExpectedSnappedBounds(display1).second,
w2->GetBoundsInScreen());
PressAndReleaseKey(ui::VKEY_ESCAPE, ui::EF_NONE);
event_generator->set_current_screen_location(GetDragPoint(w2.get()));
event_generator->DragMouseTo(work_area2.origin());
EXPECT_EQ(GetExpectedSnappedBounds(display2).first,
w2->GetBoundsInScreen());
PressAndReleaseKey(ui::VKEY_ESCAPE, ui::EF_NONE);
wm::ActivateWindow(w1.get());
event_generator->MoveMouseTo(GetDragPoint(w1.get()));
event_generator->DragMouseTo(work_area2.right() - 1, work_area2.y());
EXPECT_EQ(GetExpectedSnappedBounds(display2).second,
w1->GetBoundsInScreen());
PressAndReleaseKey(ui::VKEY_ESCAPE, ui::EF_NONE);
}
}
TEST_F(SplitViewMultiDisplayClamshellTest, LandscapeAndPortrait) {
UpdateDisplay("800x600,600x800");
std::unique_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 200, 200)));
std::unique_ptr<aura::Window> w2(CreateTestWindow(gfx::Rect(0, 0, 200, 200)));
std::unique_ptr<aura::Window> w3(CreateTestWindow(gfx::Rect(0, 0, 200, 200)));
std::unique_ptr<aura::Window> w4(
CreateTestWindow(gfx::Rect(800, 0, 200, 200)));
auto* event_generator = GetEventGenerator();
wm::ActivateWindow(w1.get());
WindowState* window_state1 = WindowState::Get(w1.get());
event_generator->set_current_screen_location(GetDragPoint(w1.get()));
event_generator->DragMouseTo(0, 100);
const display::Display display1 = GetPrimaryDisplay();
const gfx::Rect work_area1 = display1.work_area();
const gfx::Rect primary_bounds1 = GetExpectedSnappedBounds(display1).first;
EXPECT_EQ(chromeos::WindowStateType::kPrimarySnapped,
window_state1->GetStateType());
EXPECT_EQ(primary_bounds1, w1->GetBoundsInScreen());
event_generator->set_current_screen_location(GetDragPoint(w1.get()));
const display::Display display2 = GetSecondaryDisplay();
const gfx::Rect work_area2 = display2.work_area();
event_generator->DragMouseTo(work_area1.bottom_right());
event_generator->DragMouseTo(work_area2.top_center());
const gfx::Rect primary_bounds2 = GetExpectedSnappedBounds(display2).first;
EXPECT_EQ(chromeos::WindowStateType::kPrimarySnapped,
window_state1->GetStateType());
EXPECT_EQ(primary_bounds2, w1->GetBoundsInScreen());
event_generator->set_current_screen_location(GetDragPoint(w1.get()));
event_generator->DragMouseTo(work_area1.right() - 1, work_area1.y());
const gfx::Rect secondary_bounds1 = GetExpectedSnappedBounds(display1).second;
EXPECT_EQ(chromeos::WindowStateType::kSecondarySnapped,
WindowState::Get(w1.get())->GetStateType());
EXPECT_EQ(secondary_bounds1, w1->GetBoundsInScreen());
event_generator->set_current_screen_location(GetDragPoint(w1.get()));
event_generator->DragMouseTo(work_area2.bottom_center());
const gfx::Rect secondary_bounds2 = GetExpectedSnappedBounds(display2).second;
EXPECT_EQ(chromeos::WindowStateType::kSecondarySnapped,
WindowState::Get(w1.get())->GetStateType());
EXPECT_EQ(secondary_bounds2, w1->GetBoundsInScreen());
}
}