#include "ash/wm/pip/pip_window_resizer.h"
#include <string>
#include <utility>
#include "ash/keyboard/ui/keyboard_ui_controller.h"
#include "ash/keyboard/ui/test/keyboard_test_util.h"
#include "ash/public/cpp/keyboard/keyboard_switches.h"
#include "ash/public/cpp/window_properties.h"
#include "ash/root_window_controller.h"
#include "ash/shelf/shelf.h"
#include "ash/shelf/shelf_navigation_widget.h"
#include "ash/shelf/shelf_widget.h"
#include "ash/shell.h"
#include "ash/system/status_area_widget.h"
#include "ash/test/ash_test_base.h"
#include "ash/test/test_window_builder.h"
#include "ash/wm/pip/pip_positioner.h"
#include "ash/wm/pip/pip_test_utils.h"
#include "ash/wm/window_state.h"
#include "ash/wm/wm_event.h"
#include "base/command_line.h"
#include "ui/aura/window.h"
#include "ui/compositor/layer.h"
#include "ui/events/test/event_generator.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"
namespace ash {
class PipTest : public AshTestBase {
public:
PipTest() = default;
PipTest(const PipTest&) = delete;
PipTest& operator=(const PipTest&) = delete;
~PipTest() override = default;
void SetUp() override {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
keyboard::switches::kEnableVirtualKeyboard);
AshTestBase::SetUp();
}
void TearDown() override { AshTestBase::TearDown(); }
static std::unique_ptr<views::Widget> CreateWidget(aura::Window* context) {
std::unique_ptr<views::Widget> widget(new views::Widget);
views::Widget::InitParams params(
views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET);
params.delegate = new views::WidgetDelegateView(
views::WidgetDelegateView::CreatePassKey());
params.context = context;
widget->Init(std::move(params));
return widget;
}
};
TEST_F(PipTest, ShowInactive) {
auto widget = CreateWidget(Shell::GetPrimaryRootWindow());
const WMEvent pip_event(WM_EVENT_PIP);
auto* window_state = WindowState::Get(widget->GetNativeWindow());
window_state->OnWMEvent(&pip_event);
ASSERT_TRUE(window_state->IsPip());
ASSERT_FALSE(widget->IsVisible());
widget->Show();
ASSERT_TRUE(widget->IsVisible());
EXPECT_FALSE(widget->IsActive());
widget->Activate();
EXPECT_FALSE(widget->IsActive());
const WMEvent normal_event(WM_EVENT_NORMAL);
window_state->OnWMEvent(&normal_event);
EXPECT_FALSE(window_state->IsPip());
EXPECT_FALSE(widget->IsActive());
widget->Activate();
EXPECT_TRUE(widget->IsActive());
window_state->OnWMEvent(&pip_event);
EXPECT_FALSE(widget->IsActive());
}
TEST_F(PipTest, ShortcutNavigation) {
auto widget = CreateWidget(Shell::GetPrimaryRootWindow());
auto pip_widget = CreateWidget(Shell::GetPrimaryRootWindow());
widget->Show();
pip_widget->Show();
const WMEvent pip_event(WM_EVENT_PIP);
auto* pip_window_state = WindowState::Get(pip_widget->GetNativeWindow());
pip_window_state->OnWMEvent(&pip_event);
EXPECT_TRUE(pip_window_state->IsPip());
EXPECT_FALSE(pip_widget->IsActive());
ASSERT_TRUE(widget->IsActive());
auto* generator = GetEventGenerator();
generator->PressKey(ui::VKEY_V, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN);
EXPECT_TRUE(pip_widget->IsActive());
EXPECT_FALSE(widget->IsActive());
auto* navigation_widget = AshTestBase::GetPrimaryShelf()->navigation_widget();
auto* hotseat_widget = AshTestBase::GetPrimaryShelf()->hotseat_widget();
auto* status_area =
Shell::GetPrimaryRootWindowController()->GetStatusAreaWidget();
generator->PressKey(ui::VKEY_BROWSER_BACK, ui::EF_CONTROL_DOWN);
EXPECT_TRUE(status_area->IsActive());
generator->PressKey(ui::VKEY_BROWSER_BACK, ui::EF_CONTROL_DOWN);
EXPECT_TRUE(hotseat_widget->IsActive());
generator->PressKey(ui::VKEY_BROWSER_BACK, ui::EF_CONTROL_DOWN);
EXPECT_TRUE(navigation_widget->IsActive());
generator->PressKey(ui::VKEY_BROWSER_BACK, ui::EF_CONTROL_DOWN);
EXPECT_TRUE(widget->IsActive());
generator->PressKey(ui::VKEY_BROWSER_BACK, ui::EF_CONTROL_DOWN);
EXPECT_TRUE(pip_widget->IsActive());
generator->PressKey(ui::VKEY_BROWSER_FORWARD, ui::EF_CONTROL_DOWN);
EXPECT_TRUE(widget->IsActive());
generator->PressKey(ui::VKEY_BROWSER_FORWARD, ui::EF_CONTROL_DOWN);
EXPECT_TRUE(navigation_widget->IsActive());
generator->PressKey(ui::VKEY_BROWSER_FORWARD, ui::EF_CONTROL_DOWN);
EXPECT_TRUE(hotseat_widget->IsActive());
generator->PressKey(ui::VKEY_BROWSER_FORWARD, ui::EF_CONTROL_DOWN);
EXPECT_TRUE(status_area->IsActive());
generator->PressKey(ui::VKEY_BROWSER_FORWARD, ui::EF_CONTROL_DOWN);
EXPECT_TRUE(pip_widget->IsActive());
generator->PressKey(ui::VKEY_BROWSER_FORWARD, ui::EF_CONTROL_DOWN);
EXPECT_TRUE(widget->IsActive());
}
TEST_F(PipTest, PipInitialPositionAvoidsObstacles) {
UpdateDisplay("500x400");
std::unique_ptr<aura::Window> window(
CreateTestWindowInShell({.bounds = {100, 300, 100, 100}}));
WindowState* window_state = WindowState::Get(window.get());
const WMEvent enter_pip(WM_EVENT_PIP);
window_state->OnWMEvent(&enter_pip);
window->Show();
auto* keyboard_controller = keyboard::KeyboardUIController::Get();
keyboard_controller->ShowKeyboard(true);
ASSERT_TRUE(keyboard::test::WaitUntilShown());
aura::Window* keyboard_window = keyboard_controller->GetKeyboardWindow();
keyboard_window->SetBounds(gfx::Rect(0, 300, 400, 100));
EXPECT_TRUE(window_state->IsPip());
EXPECT_TRUE(window->layer()->visible());
EXPECT_EQ(gfx::Rect(100, 192, 100, 100), window->layer()->GetTargetBounds());
}
TEST_F(PipTest, TargetBoundsAffectedByWorkAreaChange) {
UpdateDisplay("500x400");
auto* keyboard_controller = keyboard::KeyboardUIController::Get();
keyboard_controller->ShowKeyboard(true);
ASSERT_TRUE(keyboard::test::WaitUntilShown());
aura::Window* keyboard_window = keyboard_controller->GetKeyboardWindow();
keyboard_window->SetBounds(gfx::Rect(0, 300, 400, 100));
std::unique_ptr<aura::Window> window(
CreateTestWindowInShell({.bounds = {100, 300, 100, 100}}));
WindowState* window_state = WindowState::Get(window.get());
const WMEvent enter_pip(WM_EVENT_PIP);
window_state->OnWMEvent(&enter_pip);
window->Show();
EXPECT_TRUE(window_state->IsPip());
EXPECT_TRUE(window->layer()->visible());
EXPECT_EQ(gfx::Rect(100, 192, 100, 100), window->bounds());
}
TEST_F(PipTest, PipRestoresToPreviousBoundsOnMovementAreaChangeIfTheyExist) {
ForceHideShelvesForTest();
UpdateDisplay("500x400");
std::unique_ptr<aura::Window> window(
CreateTestWindowInShell({.bounds = {200, 200, 100, 100}}));
WindowState* window_state = WindowState::Get(window.get());
const WMEvent enter_pip(WM_EVENT_PIP);
window_state->OnWMEvent(&enter_pip);
window->Show();
const gfx::Rect bounds = gfx::Rect(392, 200, 100, 100);
window->SetBounds(bounds);
PipPositioner::SaveSnapFraction(window_state, window->GetBoundsInScreen());
EXPECT_TRUE(PipPositioner::HasSnapFraction(window_state));
UpdateDisplay("400x200");
ForceHideShelvesForTest();
EXPECT_EQ(gfx::Rect(292, 76, 100, 100), window->GetBoundsInScreen());
UpdateDisplay("500x400");
ForceHideShelvesForTest();
EXPECT_EQ(gfx::Rect(392, 239, 100, 100), window->GetBoundsInScreen());
}
TEST_F(
PipTest,
PipRestoresToPreviousBoundsOnMovementAreaChangeIfTheyExistOnExternalDisplay) {
UpdateDisplay("500x400,500x400");
ForceHideShelvesForTest();
auto* root_window = Shell::GetAllRootWindows()[1].get();
auto widget = CreateWidget(root_window);
auto* window = widget->GetNativeWindow();
WindowState* window_state = WindowState::Get(window);
const WMEvent enter_pip(WM_EVENT_PIP);
window_state->OnWMEvent(&enter_pip);
window->Show();
window->SetBounds(gfx::Rect(8, 292, 100, 100));
PipPositioner::SaveSnapFraction(window_state, window->GetBoundsInScreen());
EXPECT_TRUE(PipPositioner::HasSnapFraction(window_state));
UpdateDisplay("500x400,400x200");
ForceHideShelvesForTest();
EXPECT_EQ(gfx::Rect(508, 92, 100, 100), window->GetBoundsInScreen());
UpdateDisplay("500x400,500x400");
ForceHideShelvesForTest();
EXPECT_EQ(gfx::Rect(508, 292, 100, 100), window->GetBoundsInScreen());
}
TEST_F(PipTest, PipRestoreOnWorkAreaChangeDoesNotChangeWindowSize) {
ForceHideShelvesForTest();
UpdateDisplay("500x400");
std::unique_ptr<aura::Window> pip_window(TestWindowBuilder()
.AllowAllWindowStates()
.SetShow(false)
.Build()
.release());
WindowState* window_state = WindowState::Get(pip_window.get());
const WMEvent enter_pip(WM_EVENT_PIP);
window_state->OnWMEvent(&enter_pip);
pip_window->SetBounds(gfx::Rect(392, 200, 100, 100));
EXPECT_TRUE(window_state->IsPip());
pip_window->Show();
UpdateDisplay("400x200");
ForceHideShelvesForTest();
EXPECT_EQ(gfx::Rect(292, 76, 100, 100), pip_window->GetBoundsInScreen());
}
TEST_F(PipTest, PipSnappedToEdgeWhenSavingSnapFraction) {
ForceHideShelvesForTest();
UpdateDisplay("500x400");
std::unique_ptr<aura::Window> window(
CreateTestWindowInShell({.bounds = {200, 200, 100, 100}}));
WindowState* window_state = WindowState::Get(window.get());
const WMEvent enter_pip(WM_EVENT_PIP);
window_state->OnWMEvent(&enter_pip);
window->Show();
auto* keyboard_controller = keyboard::KeyboardUIController::Get();
keyboard_controller->ShowKeyboardInDisplay(window_state->GetDisplay());
ASSERT_TRUE(keyboard::test::WaitUntilShown());
aura::Window* keyboard_window = keyboard_controller->GetKeyboardWindow();
keyboard_window->SetBounds(gfx::Rect(0, 300, 400, 100));
window->SetBounds(gfx::Rect(100, 192, 100, 100));
PipPositioner::SaveSnapFraction(window_state, window->GetBoundsInScreen());
EXPECT_TRUE(PipPositioner::HasSnapFraction(window_state));
EXPECT_EQ(gfx::Rect(100, 192, 100, 100),
PipPositioner::GetSnapFractionAppliedBounds(window_state));
}
}