chromium/ash/wm/splitview/split_view_controller_clamshell_unittest.cc

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#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 "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "ui/display/test/display_manager_test_api.h"
#include "ui/wm/core/window_util.h"

namespace ash {

namespace {

using chromeos::WindowStateType;

SplitViewController* split_view_controller() {
  return SplitViewController::Get(Shell::GetPrimaryRootWindow());
}

}  // namespace

using SplitViewControllerClamshellTest = AshTestBase;

// Tests we correctly end split view if partial overview is skipped and another
// window is snapped. Regression test for http://b/333600706.
TEST_F(SplitViewControllerClamshellTest, EndSplitView) {
  // Snap `w1` to start partial overview.
  std::unique_ptr<aura::Window> w1(CreateAppWindow());
  std::unique_ptr<aura::Window> w2(CreateAppWindow());
  split_view_controller()->SnapWindow(
      w1.get(), SnapPosition::kPrimary,
      WindowSnapActionSource::kDragWindowToEdgeToSnap);
  OverviewController* overview_controller = OverviewController::Get();
  EXPECT_TRUE(overview_controller->InOverviewSession());
  EXPECT_TRUE(split_view_controller()->primary_window());
  // Skip partial overview.
  PressAndReleaseKey(ui::VKEY_ESCAPE, ui::EF_NONE);
  // Test we cleared the observed window.
  EXPECT_FALSE(split_view_controller()->primary_window());
  EXPECT_FALSE(overview_controller->InOverviewSession());

  // Drag to snap `w2` to the opposite side.
  wm::ActivateWindow(w2.get());
  const gfx::Rect w2_bounds(w2->GetBoundsInScreen());
  const gfx::Point drag_point(w2_bounds.CenterPoint().x(), w2_bounds.y() + 10);
  auto* event_generator = GetEventGenerator();
  event_generator->set_current_screen_location(drag_point);
  const gfx::Rect work_area =
      display::Screen::GetScreen()->GetPrimaryDisplay().work_area();
  event_generator->DragMouseTo(work_area.right_center());
  EXPECT_EQ(WindowStateType::kSecondarySnapped,
            WindowState::Get(w2.get())->GetStateType());
  // Test we cleared the observed window.
  EXPECT_FALSE(split_view_controller()->secondary_window());
  EXPECT_FALSE(overview_controller->InOverviewSession());
}

// Test that tablet <-> clamshell transition works correctly.
TEST_F(SplitViewControllerClamshellTest, ClamshellTabletTransition) {
  // 1. Test 1 snapped window.
  std::unique_ptr<aura::Window> w1(CreateAppWindow());
  std::unique_ptr<aura::Window> w2(CreateAppWindow());
  split_view_controller()->SnapWindow(
      w1.get(), SnapPosition::kPrimary,
      WindowSnapActionSource::kDragWindowToEdgeToSnap);
  OverviewController* overview_controller = OverviewController::Get();
  EXPECT_TRUE(overview_controller->InOverviewSession());
  EXPECT_EQ(split_view_controller()->primary_window(), w1.get());
  EXPECT_FALSE(split_view_controller()->secondary_window());
  EXPECT_TRUE(split_view_controller()->InSplitViewMode());

  // Enter tablet mode.
  Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
  EXPECT_TRUE(overview_controller->InOverviewSession());
  EXPECT_EQ(split_view_controller()->primary_window(), w1.get());
  EXPECT_FALSE(split_view_controller()->secondary_window());
  EXPECT_TRUE(split_view_controller()->InSplitViewMode());

  // Exit tablet mode.
  Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false);
  EXPECT_TRUE(overview_controller->InOverviewSession());
  EXPECT_EQ(split_view_controller()->primary_window(), w1.get());
  EXPECT_FALSE(split_view_controller()->secondary_window());
  EXPECT_TRUE(split_view_controller()->InSplitViewMode());

  // 2. Test 2 snapped windows.
  split_view_controller()->SnapWindow(
      w2.get(), SnapPosition::kSecondary,
      WindowSnapActionSource::kDragWindowToEdgeToSnap);

  // Since we are in clamshell mode, `primary|secondary_window_` are nil.
  EXPECT_FALSE(overview_controller->InOverviewSession());
  EXPECT_FALSE(split_view_controller()->primary_window());
  EXPECT_FALSE(split_view_controller()->secondary_window());
  EXPECT_FALSE(split_view_controller()->InSplitViewMode());

  // Enter tablet mode.
  Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
  EXPECT_FALSE(overview_controller->InOverviewSession());
  EXPECT_EQ(split_view_controller()->primary_window(), w1.get());
  EXPECT_EQ(split_view_controller()->secondary_window(), w2.get());
  EXPECT_TRUE(split_view_controller()->InSplitViewMode());

  // Exit tablet mode.
  Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false);
  EXPECT_FALSE(overview_controller->InOverviewSession());
  EXPECT_FALSE(split_view_controller()->primary_window());
  EXPECT_FALSE(split_view_controller()->secondary_window());
  EXPECT_FALSE(split_view_controller()->InSplitViewMode());
}

// Tests that using the keyboard shortcut never starts split view.
TEST_F(SplitViewControllerClamshellTest, Shortcut) {
  std::unique_ptr<aura::Window> w1(CreateAppWindow());
  std::unique_ptr<aura::Window> w2(CreateAppWindow());
  wm::ActivateWindow(w1.get());
  PressAndReleaseKey(ui::VKEY_OEM_4, ui::EF_ALT_DOWN);
  EXPECT_EQ(WindowStateType::kPrimarySnapped,
            WindowState::Get(w1.get())->GetStateType());
  EXPECT_FALSE(split_view_controller()->InSplitViewMode());

  wm::ActivateWindow(w2.get());
  PressAndReleaseKey(ui::VKEY_OEM_6, ui::EF_ALT_DOWN);
  EXPECT_EQ(WindowStateType::kSecondarySnapped,
            WindowState::Get(w2.get())->GetStateType());
  EXPECT_FALSE(split_view_controller()->InSplitViewMode());
}

}  // namespace ash