#include "ash/wm/splitview/split_view_controller.h"
#include <memory>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
#include "ash/accessibility/magnifier/docked_magnifier_controller.h"
#include "ash/app_list/app_list_controller_impl.h"
#include "ash/constants/app_types.h"
#include "ash/constants/ash_features.h"
#include "ash/display/screen_orientation_controller.h"
#include "ash/display/screen_orientation_controller_test_api.h"
#include "ash/keyboard/ui/keyboard_ui_controller.h"
#include "ash/keyboard/ui/test/keyboard_test_util.h"
#include "ash/public/cpp/fps_counter.h"
#include "ash/public/cpp/shelf_config.h"
#include "ash/public/cpp/window_backdrop.h"
#include "ash/public/cpp/window_properties.h"
#include "ash/root_window_controller.h"
#include "ash/screen_util.h"
#include "ash/session/session_controller_impl.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/test/ash_test_util.h"
#include "ash/test/test_window_builder.h"
#include "ash/wallpaper/wallpaper_widget_controller.h"
#include "ash/wm/desks/desks_util.h"
#include "ash/wm/drag_window_resizer.h"
#include "ash/wm/float/float_controller.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/overview/overview_test_util.h"
#include "ash/wm/splitview/split_view_constants.h"
#include "ash/wm/splitview/split_view_divider.h"
#include "ash/wm/splitview/split_view_drag_indicators.h"
#include "ash/wm/splitview/split_view_metrics_controller.h"
#include "ash/wm/splitview/split_view_utils.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "ash/wm/window_properties.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/containers/contains.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/ranges/algorithm.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "chromeos/ui/base/window_properties.h"
#include "chromeos/ui/frame/caption_buttons/snap_controller.h"
#include "chromeos/ui/wm/features.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/base/ime/dummy_text_input_client.h"
#include "ui/compositor/presentation_time_recorder.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
#include "ui/compositor/test/test_utils.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 {
constexpr int kCaretHeightForTest = 8;
class OverviewStatesObserver : public OverviewObserver {
public:
explicit OverviewStatesObserver(aura::Window* root_window)
: root_window_(root_window) {
Shell::Get()->overview_controller()->AddObserver(this);
}
OverviewStatesObserver(const OverviewStatesObserver&) = delete;
OverviewStatesObserver& operator=(const OverviewStatesObserver&) = delete;
~OverviewStatesObserver() override {
Shell::Get()->overview_controller()->RemoveObserver(this);
}
void OnOverviewModeStarting() override {
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;
raw_ptr<aura::Window, ExperimentalAsh> root_window_;
};
class TestBubbleDialogDelegateView : public views::BubbleDialogDelegateView {
public:
explicit TestBubbleDialogDelegateView(views::View* anchor_view)
: BubbleDialogDelegateView(anchor_view, views::BubbleBorder::NONE) {}
TestBubbleDialogDelegateView(const TestBubbleDialogDelegateView&) = delete;
TestBubbleDialogDelegateView& operator=(const TestBubbleDialogDelegateView&) =
delete;
~TestBubbleDialogDelegateView() override {}
};
class TestTextInputClient : public ui::DummyTextInputClient {
public:
explicit TestTextInputClient(aura::Window* window)
: ui::DummyTextInputClient(ui::TEXT_INPUT_TYPE_TEXT), window_(window) {
DCHECK(window_);
}
TestTextInputClient(const TestTextInputClient&) = delete;
TestTextInputClient& operator=(const TestTextInputClient&) = delete;
~TestTextInputClient() override {
auto* ime = keyboard::KeyboardUIController::Get()->GetInputMethodForTest();
ime->DetachTextInputClient(this);
}
gfx::Rect GetCaretBounds() const override { return caret_bounds_; }
void set_caret_bounds(gfx::Rect caret_bounds) {
caret_bounds_ = caret_bounds;
}
void Focus() {
auto* ime = keyboard::KeyboardUIController::Get()->GetInputMethodForTest();
ime->SetFocusedTextInputClient(this);
if (window_) {
window_->Focus();
}
ime->SetVirtualKeyboardVisibilityIfEnabled(true);
ASSERT_TRUE(keyboard::WaitUntilShown());
}
void UnFocus() {
auto* ime = keyboard::KeyboardUIController::Get()->GetInputMethodForTest();
ime->DetachTextInputClient(this);
keyboard::KeyboardUIController::Get()->HideKeyboardExplicitlyBySystem();
}
private:
raw_ptr<aura::Window, ExperimentalAsh> window_;
gfx::Rect caret_bounds_;
};
}
class SplitViewControllerTest : public AshTestBase {
public:
SplitViewControllerTest() = default;
SplitViewControllerTest(const SplitViewControllerTest&) = delete;
SplitViewControllerTest& operator=(const SplitViewControllerTest&) = delete;
~SplitViewControllerTest() override = default;
void SetUp() override {
AshTestBase::SetUp();
base::RunLoop().RunUntilIdle();
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
FpsCounter::SetForceReportZeroAnimationForTest(true);
ui::PresentationTimeRecorder::SetReportPresentationTimeImmediatelyForTest(
true);
}
void TearDown() override {
ui::PresentationTimeRecorder::SetReportPresentationTimeImmediatelyForTest(
false);
FpsCounter::SetForceReportZeroAnimationForTest(false);
trace_names_.clear();
AshTestBase::TearDown();
}
std::unique_ptr<aura::Window> CreateWindow(
const gfx::Rect& bounds,
aura::client::WindowType type = aura::client::WINDOW_TYPE_NORMAL) {
std::unique_ptr<aura::Window> window =
TestWindowBuilder()
.SetBounds(bounds)
.SetDelegate(new SplitViewTestWindowDelegate)
.SetWindowType(type)
.Build();
window->SetProperty(aura::client::kResizeBehaviorKey,
aura::client::kResizeBehaviorCanMaximize |
aura::client::kResizeBehaviorCanMinimize |
aura::client::kResizeBehaviorCanResize);
return window;
}
std::unique_ptr<aura::Window> CreateNonSnappableWindow(
const gfx::Rect& bounds) {
return TestWindowBuilder()
.SetWindowProperty(aura::client::kResizeBehaviorKey,
aura::client::kResizeBehaviorNone)
.SetBounds(bounds)
.SetDelegate(new SplitViewTestWindowDelegate)
.Build();
}
bool IsDividerAnimating() {
return split_view_controller()->IsDividerAnimating();
}
void SkipDividerSnapAnimation() {
if (!IsDividerAnimating()) {
return;
}
split_view_controller()->StopAndShoveAnimatedDivider();
split_view_controller()->EndResizeWithDividerImpl();
split_view_controller()->EndSplitViewAfterResizingAtEdgeIfAppropriate();
}
void EndSplitView() { split_view_controller()->EndSplitView(); }
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 SplitViewController::Get(Shell::GetPrimaryRootWindow());
}
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;
void OnWindowDestroying(aura::Window* window) override { window->Hide(); }
void OnWindowDestroyed(aura::Window* window) override { delete this; }
};
void CheckForDuplicateTraceName(const char* trace) {
DCHECK(!base::Contains(trace_names_, trace)) << trace;
trace_names_.push_back(trace);
}
void CheckOverviewEnterExitHistogram(const char* trace,
std::vector<int>&& enter_counts,
std::vector<int>&& exit_counts) {
CheckForDuplicateTraceName(trace);
std::ignore = ui::WaitForNextFrameToBePresented(
Shell::GetPrimaryRootWindow()->layer()->GetCompositor(),
base::Milliseconds(100));
{
SCOPED_TRACE(trace + std::string(".Enter"));
CheckOverviewHistogram("Ash.Overview.AnimationSmoothness.Enter",
std::move(enter_counts));
}
{
SCOPED_TRACE(trace + std::string(".Exit"));
CheckOverviewHistogram("Ash.Overview.AnimationSmoothness.Exit",
std::move(exit_counts));
}
}
const base::HistogramTester& histograms() const { return histograms_; }
private:
void CheckOverviewHistogram(const char* histogram, std::vector<int> counts) {
histograms_.ExpectTotalCount(histogram + std::string(".ClamshellMode"), 0);
histograms_.ExpectTotalCount(
histogram + std::string(".SingleClamshellMode"), 0);
histograms_.ExpectTotalCount(histogram + std::string(".TabletMode"),
counts[0]);
histograms_.ExpectTotalCount(histogram + std::string(".SplitView"),
counts[1]);
}
std::vector<std::string> trace_names_;
base::HistogramTester histograms_;
};
class TestWindowStateDelegate : public WindowStateDelegate {
public:
TestWindowStateDelegate() = default;
TestWindowStateDelegate(const TestWindowStateDelegate&) = delete;
TestWindowStateDelegate& operator=(const TestWindowStateDelegate&) = delete;
~TestWindowStateDelegate() override = default;
std::unique_ptr<PresentationTimeRecorder> OnDragStarted(
int component) override {
drag_in_progress_ = true;
return nullptr;
}
void OnDragFinished(bool cancel, const gfx::PointF& location) override {
drag_in_progress_ = false;
}
bool drag_in_progress() { return drag_in_progress_; }
private:
bool drag_in_progress_ = false;
};
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::State::kNoSnap);
EXPECT_EQ(split_view_controller()->InSplitViewMode(), false);
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kPrimary);
EXPECT_EQ(split_view_controller()->state(),
SplitViewController::State::kPrimarySnapped);
EXPECT_EQ(split_view_controller()->primary_window(), window1.get());
EXPECT_NE(split_view_controller()->primary_window(), window2.get());
EXPECT_EQ(split_view_controller()->InSplitViewMode(), true);
EXPECT_EQ(window1->GetBoundsInScreen(),
split_view_controller()->GetSnappedWindowBoundsInScreen(
SplitViewController::SnapPosition::kPrimary, window1.get()));
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kSecondary);
EXPECT_EQ(split_view_controller()->state(),
SplitViewController::State::kBothSnapped);
EXPECT_EQ(split_view_controller()->secondary_window(), window2.get());
EXPECT_NE(split_view_controller()->secondary_window(), window1.get());
EXPECT_EQ(split_view_controller()->InSplitViewMode(), true);
EXPECT_EQ(window2->GetBoundsInScreen(),
split_view_controller()->GetSnappedWindowBoundsInScreen(
SplitViewController::SnapPosition::kSecondary, window2.get()));
EndSplitView();
EXPECT_EQ(split_view_controller()->state(),
SplitViewController::State::kNoSnap);
EXPECT_EQ(split_view_controller()->InSplitViewMode(), false);
}
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::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kSecondary);
EXPECT_EQ(window1.get(), split_view_controller()->GetDefaultSnappedWindow());
EndSplitView();
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kSecondary);
EXPECT_EQ(window2.get(), split_view_controller()->GetDefaultSnappedWindow());
}
TEST_F(SplitViewControllerTest, WindowCloseTest) {
const gfx::Rect bounds(0, 0, 400, 400);
std::unique_ptr<aura::Window> window0(CreateWindow(bounds));
EXPECT_EQ(split_view_controller()->InSplitViewMode(), false);
split_view_controller()->SnapWindow(
window0.get(), SplitViewController::SnapPosition::kPrimary);
EXPECT_EQ(split_view_controller()->InSplitViewMode(), true);
window0.reset();
EXPECT_EQ(split_view_controller()->InSplitViewMode(), false);
std::unique_ptr<aura::Window> window1(CreateWindow(bounds));
std::unique_ptr<aura::Window> window2(CreateWindow(bounds));
EXPECT_EQ(split_view_controller()->InSplitViewMode(), false);
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kSecondary);
EXPECT_EQ(split_view_controller()->InSplitViewMode(), true);
EXPECT_EQ(split_view_controller()->state(),
SplitViewController::State::kBothSnapped);
EXPECT_EQ(split_view_controller()->default_snap_position(),
SplitViewController::SnapPosition::kPrimary);
window1.reset();
EXPECT_EQ(split_view_controller()->InSplitViewMode(), true);
EXPECT_EQ(split_view_controller()->state(),
SplitViewController::State::kSecondarySnapped);
EXPECT_EQ(split_view_controller()->default_snap_position(),
SplitViewController::SnapPosition::kSecondary);
EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
window2.reset();
EXPECT_EQ(split_view_controller()->InSplitViewMode(), false);
EXPECT_EQ(split_view_controller()->state(),
SplitViewController::State::kNoSnap);
EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
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::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
window4.get(), SplitViewController::SnapPosition::kSecondary);
EXPECT_EQ(split_view_controller()->InSplitViewMode(), true);
EXPECT_EQ(split_view_controller()->state(),
SplitViewController::State::kBothSnapped);
EXPECT_EQ(split_view_controller()->default_snap_position(),
SplitViewController::SnapPosition::kPrimary);
window4.reset();
EXPECT_EQ(split_view_controller()->InSplitViewMode(), true);
EXPECT_EQ(split_view_controller()->state(),
SplitViewController::State::kPrimarySnapped);
EXPECT_EQ(split_view_controller()->default_snap_position(),
SplitViewController::SnapPosition::kPrimary);
EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
window3.reset();
EXPECT_EQ(split_view_controller()->InSplitViewMode(), false);
EXPECT_EQ(split_view_controller()->state(),
SplitViewController::State::kNoSnap);
EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
}
TEST_F(SplitViewControllerTest, MinimizeWindowTest) {
const gfx::Rect bounds(0, 0, 400, 400);
std::unique_ptr<aura::Window> window0(CreateWindow(bounds));
EXPECT_EQ(split_view_controller()->InSplitViewMode(), false);
split_view_controller()->SnapWindow(
window0.get(), SplitViewController::SnapPosition::kPrimary);
EXPECT_EQ(split_view_controller()->InSplitViewMode(), true);
WMEvent minimize_event(WM_EVENT_MINIMIZE);
WindowState::Get(window0.get())->OnWMEvent(&minimize_event);
EXPECT_EQ(split_view_controller()->InSplitViewMode(), false);
std::unique_ptr<aura::Window> window1(CreateWindow(bounds));
std::unique_ptr<aura::Window> window2(CreateWindow(bounds));
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kSecondary);
EXPECT_EQ(split_view_controller()->InSplitViewMode(), true);
EXPECT_EQ(split_view_controller()->state(),
SplitViewController::State::kBothSnapped);
EXPECT_EQ(split_view_controller()->default_snap_position(),
SplitViewController::SnapPosition::kPrimary);
WindowState::Get(window1.get())->OnWMEvent(&minimize_event);
EXPECT_EQ(split_view_controller()->InSplitViewMode(), true);
EXPECT_EQ(split_view_controller()->state(),
SplitViewController::State::kSecondarySnapped);
EXPECT_EQ(split_view_controller()->default_snap_position(),
SplitViewController::SnapPosition::kSecondary);
EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
WindowState::Get(window2.get())->OnWMEvent(&minimize_event);
EXPECT_EQ(split_view_controller()->InSplitViewMode(), false);
EXPECT_EQ(split_view_controller()->state(),
SplitViewController::State::kNoSnap);
EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
}
TEST_F(SplitViewControllerTest, WindowStateChangeTest) {
const gfx::Rect bounds(0, 0, 400, 400);
std::unique_ptr<aura::Window> window1(CreateWindow(bounds));
EXPECT_EQ(split_view_controller()->InSplitViewMode(), false);
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kPrimary);
EXPECT_EQ(split_view_controller()->InSplitViewMode(), true);
WMEvent maximize_event(WM_EVENT_MAXIMIZE);
WindowState::Get(window1.get())->OnWMEvent(&maximize_event);
EXPECT_EQ(split_view_controller()->InSplitViewMode(), false);
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kPrimary);
EXPECT_EQ(split_view_controller()->InSplitViewMode(), true);
WMEvent fullscreen_event(WM_EVENT_FULLSCREEN);
WindowState::Get(window1.get())->OnWMEvent(&fullscreen_event);
EXPECT_EQ(split_view_controller()->InSplitViewMode(), false);
std::unique_ptr<aura::Window> window2(CreateWindow(bounds));
wm::ActivateWindow(window1.get());
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kSecondary);
EXPECT_EQ(split_view_controller()->InSplitViewMode(), true);
WindowState::Get(window1.get())->OnWMEvent(&maximize_event);
EXPECT_EQ(split_view_controller()->InSplitViewMode(), false);
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kSecondary);
EXPECT_EQ(split_view_controller()->InSplitViewMode(), true);
WindowState::Get(window1.get())->OnWMEvent(&fullscreen_event);
EXPECT_EQ(split_view_controller()->InSplitViewMode(), false);
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kPrimary);
EXPECT_EQ(split_view_controller()->InSplitViewMode(), true);
ToggleOverview();
EXPECT_EQ(split_view_controller()->InSplitViewMode(), true);
WindowState::Get(window1.get())->OnWMEvent(&maximize_event);
EXPECT_EQ(split_view_controller()->InSplitViewMode(), false);
EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kPrimary);
EXPECT_EQ(split_view_controller()->InSplitViewMode(), true);
ToggleOverview();
EXPECT_EQ(split_view_controller()->InSplitViewMode(), true);
WindowState::Get(window1.get())->OnWMEvent(&fullscreen_event);
EXPECT_EQ(split_view_controller()->InSplitViewMode(), false);
EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
}
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()->InSplitViewMode(), false);
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kPrimary);
EXPECT_EQ(split_view_controller()->InSplitViewMode(), true);
EXPECT_EQ(split_view_controller()->primary_window(), window1.get());
EXPECT_EQ(split_view_controller()->state(),
SplitViewController::State::kPrimarySnapped);
wm::ActivateWindow(window2.get());
EXPECT_EQ(split_view_controller()->secondary_window(), window2.get());
EXPECT_EQ(split_view_controller()->state(),
SplitViewController::State::kBothSnapped);
wm::ActivateWindow(window3.get());
EXPECT_EQ(split_view_controller()->secondary_window(), window3.get());
EXPECT_EQ(split_view_controller()->state(),
SplitViewController::State::kBothSnapped);
}
TEST_F(SplitViewControllerTest, SnapWindowWithUnresizableSnapProperty) {
UpdateDisplay("800x600");
const gfx::Rect bounds(0, 0, 400, 400);
std::unique_ptr<aura::Window> window(CreateWindow(bounds));
window->SetProperty(aura::client::kResizeBehaviorKey,
aura::client::kResizeBehaviorNone);
window->SetProperty(kUnresizableSnappedSizeKey, new gfx::Size(300, 0));
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false);
ToggleOverview();
ASSERT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
split_view_controller()->SnapWindow(
window.get(), SplitViewController::SnapPosition::kPrimary);
EXPECT_EQ(window->GetBoundsInScreen().width(), 300);
}
TEST_F(SplitViewControllerTest, DISABLED_ExitOverviewTest) {
ui::ScopedAnimationDurationScaleMode anmatin_scale(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
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()->InSplitViewMode(), false);
ToggleOverview();
WaitForOverviewEnterAnimation();
CheckOverviewEnterExitHistogram("EnterInTablet", {1, 0}, {0, 0});
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kPrimary);
EXPECT_EQ(split_view_controller()->InSplitViewMode(), true);
EXPECT_EQ(split_view_controller()->state(),
SplitViewController::State::kPrimarySnapped);
EXPECT_EQ(split_view_controller()->primary_window(), window1.get());
wm::ActivateWindow(window1.get());
ToggleOverview();
WaitForOverviewExitAnimation();
EXPECT_EQ(split_view_controller()->state(),
SplitViewController::State::kBothSnapped);
EXPECT_EQ(split_view_controller()->secondary_window(), window3.get());
EXPECT_TRUE(wm::IsActiveWindow(window1.get()));
CheckOverviewEnterExitHistogram("ExitInSplitView", {1, 0}, {0, 1});
}
TEST_F(SplitViewControllerTest,
WallpaperUnblurredAfterLoneOverviewWindowSnapAnimationCompleted) {
const gfx::Rect bounds(400, 400);
std::unique_ptr<aura::Window> window1(CreateWindow(bounds));
std::unique_ptr<aura::Window> window2(CreateWindow(bounds));
ToggleOverview();
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kPrimary);
WallpaperWidgetController* wallpaper_widget_controller =
Shell::GetPrimaryRootWindowController()->wallpaper_widget_controller();
EXPECT_GT(wallpaper_widget_controller->GetWallpaperBlur(), 0);
EXPECT_FALSE(wallpaper_widget_controller->IsAnimating());
ui::ScopedAnimationDurationScaleMode animation_scale(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
ToggleOverview();
EXPECT_GT(wallpaper_widget_controller->GetWallpaperBlur(), 0);
EXPECT_FALSE(wallpaper_widget_controller->IsAnimating());
WaitForOverviewExitAnimation();
EXPECT_EQ(wallpaper_widget_controller->GetWallpaperBlur(), 0);
EXPECT_FALSE(wallpaper_widget_controller->IsAnimating());
}
TEST_F(SplitViewControllerTest, DISABLED_EnterOverviewModeTest) {
ui::ScopedAnimationDurationScaleMode anmatin_scale(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
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::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kSecondary);
EXPECT_EQ(split_view_controller()->state(),
SplitViewController::State::kBothSnapped);
EXPECT_EQ(split_view_controller()->GetDefaultSnappedWindow(), window1.get());
ToggleOverview();
WaitForOverviewEnterAnimation();
CheckOverviewEnterExitHistogram("EnterInSplitView", {0, 1}, {0, 0});
EXPECT_EQ(split_view_controller()->state(),
SplitViewController::State::kPrimarySnapped);
EXPECT_FALSE(
base::Contains(GetWindowsInOverviewGrids(),
split_view_controller()->GetDefaultSnappedWindow()));
ToggleOverview();
WaitForOverviewExitAnimation();
CheckOverviewEnterExitHistogram("ExitInSplitView", {0, 1}, {0, 1});
}
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::SnapPosition::kPrimary);
EXPECT_TRUE(split_view_divider());
EXPECT_EQ(ui::ZOrderLevel::kNormal,
split_view_divider()->divider_widget()->GetZOrderLevel());
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kSecondary);
EXPECT_TRUE(split_view_divider());
EXPECT_EQ(ui::ZOrderLevel::kNormal,
split_view_divider()->divider_widget()->GetZOrderLevel());
EXPECT_TRUE(IsStackedBelow(
window1.get(),
split_view_divider()->divider_widget()->GetNativeWindow()));
EXPECT_TRUE(IsStackedBelow(
window2.get(),
split_view_divider()->divider_widget()->GetNativeWindow()));
std::unique_ptr<aura::Window> window3(CreateNonSnappableWindow(bounds));
wm::ActivateWindow(window3.get());
EXPECT_FALSE(split_view_divider());
}
TEST_F(SplitViewControllerTest, DividerStateWhenDraggedOverviewItemDestroyed) {
std::unique_ptr<aura::Window> window1 = CreateTestWindow();
std::unique_ptr<aura::Window> window2 = CreateTestWindow();
std::unique_ptr<aura::Window> window3 = CreateTestWindow();
ToggleOverview();
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kPrimary);
EXPECT_EQ(ui::ZOrderLevel::kNormal,
split_view_divider()->divider_widget()->GetZOrderLevel());
OverviewSession* overview_session =
Shell::Get()->overview_controller()->overview_session();
OverviewItem* overview_item =
overview_session->GetOverviewItemForWindow(window2.get());
gfx::PointF drag_point = overview_item->target_bounds().CenterPoint();
overview_session->InitiateDrag(overview_item, drag_point,
false);
drag_point.Offset(5.f, 0.f);
overview_session->Drag(overview_item, drag_point);
EXPECT_EQ(ui::ZOrderLevel::kNormal,
split_view_divider()->divider_widget()->GetZOrderLevel());
window2.reset();
EXPECT_EQ(ui::ZOrderLevel::kNormal,
split_view_divider()->divider_widget()->GetZOrderLevel());
split_view_controller()->SnapWindow(
window3.get(), SplitViewController::SnapPosition::kSecondary);
EXPECT_EQ(ui::ZOrderLevel::kNormal,
split_view_divider()->divider_widget()->GetZOrderLevel());
EXPECT_TRUE(IsStackedBelow(window1.get(), window3.get()));
EXPECT_TRUE(IsStackedBelow(
window3.get(),
split_view_divider()->divider_widget()->GetNativeWindow()));
}
TEST_F(SplitViewControllerTest, DividerStateWhenOverviewItemDragCancelled) {
std::unique_ptr<aura::Window> window1 = CreateTestWindow();
std::unique_ptr<aura::Window> window2 = CreateTestWindow();
std::unique_ptr<aura::Window> window3 = CreateTestWindow();
ToggleOverview();
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kPrimary);
EXPECT_EQ(ui::ZOrderLevel::kNormal,
split_view_divider()->divider_widget()->GetZOrderLevel());
OverviewSession* overview_session =
Shell::Get()->overview_controller()->overview_session();
OverviewItem* overview_item =
overview_session->GetOverviewItemForWindow(window2.get());
gfx::PointF drag_point = overview_item->target_bounds().CenterPoint();
overview_session->InitiateDrag(overview_item, drag_point,
false);
drag_point.Offset(5.f, 0.f);
overview_session->Drag(overview_item, drag_point);
EXPECT_EQ(ui::ZOrderLevel::kNormal,
split_view_divider()->divider_widget()->GetZOrderLevel());
overview_session->ResetDraggedWindowGesture();
EXPECT_EQ(ui::ZOrderLevel::kNormal,
split_view_divider()->divider_widget()->GetZOrderLevel());
split_view_controller()->SnapWindow(
window3.get(), SplitViewController::SnapPosition::kSecondary);
EXPECT_EQ(ui::ZOrderLevel::kNormal,
split_view_divider()->divider_widget()->GetZOrderLevel());
wm::ActivateWindow(window3.get());
EXPECT_EQ(ui::ZOrderLevel::kNormal,
split_view_divider()->divider_widget()->GetZOrderLevel());
EXPECT_TRUE(IsStackedBelow(
window1.get(),
split_view_divider()->divider_widget()->GetNativeWindow()));
EXPECT_TRUE(IsStackedBelow(
window3.get(),
split_view_divider()->divider_widget()->GetNativeWindow()));
}
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::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kSecondary);
ASSERT_TRUE(split_view_divider());
int window1_width = window1->GetBoundsInScreen().width();
int window2_width = window2->GetBoundsInScreen().width();
gfx::Rect divider_bounds =
split_view_divider()->GetDividerBoundsInScreen(false );
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);
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);
divider_bounds =
split_view_divider()->GetDividerBoundsInScreen(false );
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);
divider_bounds =
split_view_divider()->GetDividerBoundsInScreen(false );
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);
EXPECT_NEAR(window1_width, old_window2_width, 1);
EXPECT_NEAR(window2_width, old_window1_width, 1);
}
TEST_F(SplitViewControllerTest,
DisplayDisconnectionWithSnappedWindowInTabletMode) {
ui::ScopedAnimationDurationScaleMode animation_scale(
ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
UpdateDisplay("800x600,800x600");
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
EXPECT_TRUE(EnterOverview());
Shell::Get()->display_manager()->SetMirrorMode(display::MirrorMode::kOff,
absl::nullopt);
std::unique_ptr<aura::Window> w1(
CreateTestWindowInShellWithBounds(gfx::Rect(0, 0, 100, 100)));
std::unique_ptr<aura::Window> w2(
CreateTestWindowInShellWithBounds(gfx::Rect(900, 0, 100, 100)));
ASSERT_NE(w1->GetRootWindow(), w2->GetRootWindow());
auto* split_view_controller_on_display2 =
SplitViewController::Get(w2->GetRootWindow());
split_view_controller_on_display2->SnapWindow(
w2.get(), SplitViewController::SnapPosition::kPrimary);
ASSERT_TRUE(split_view_controller_on_display2->split_view_divider());
UpdateDisplay("800x600");
base::RunLoop().RunUntilIdle();
}
TEST_F(SplitViewControllerTest,
DisplayDisconnectionWhileDraggingSplitDividerInTabletMode) {
ui::ScopedAnimationDurationScaleMode animation_scale(
ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
UpdateDisplay("800x600,800x600");
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
EXPECT_TRUE(EnterOverview());
Shell::Get()->display_manager()->SetMirrorMode(display::MirrorMode::kOff,
absl::nullopt);
std::unique_ptr<aura::Window> w(
CreateTestWindowInShellWithBounds(gfx::Rect(900, 0, 100, 100)));
auto* split_view_controller = SplitViewController::Get(w->GetRootWindow());
split_view_controller->SnapWindow(
w.get(), SplitViewController::SnapPosition::kPrimary);
auto* split_view_divider = split_view_controller->split_view_divider();
ASSERT_TRUE(split_view_divider);
auto* event_generator = GetEventGenerator();
const gfx::Point divider_center_pointer =
split_view_divider->GetDividerBoundsInScreen(false)
.CenterPoint();
event_generator->PressTouch(divider_center_pointer);
const gfx::Vector2d delta(100, 0);
event_generator->MoveTouch(divider_center_pointer + delta);
UpdateDisplay("800x600");
base::RunLoop().RunUntilIdle();
}
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::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kSecondary);
const gfx::Rect bounds_window1 = window1->GetBoundsInScreen();
const gfx::Rect bounds_window2 = window2->GetBoundsInScreen();
const gfx::Rect bounds_divider =
split_view_divider()->GetDividerBoundsInScreen(false );
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());
EXPECT_EQ(bounds_divider.x(), bounds_window1.x() + bounds_window1.width());
EXPECT_EQ(bounds_window2.x(), bounds_divider.x() + bounds_divider.width());
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 );
EXPECT_NE(bounds_window1, new_bounds_window1);
EXPECT_NE(bounds_window2, new_bounds_window2);
EXPECT_NE(bounds_divider, new_bounds_divider);
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_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::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kSecondary);
const gfx::Rect bounds_window1 = window1->GetBoundsInScreen();
const gfx::Rect bounds_window2 = window2->GetBoundsInScreen();
const gfx::Rect bounds_divider =
split_view_divider()->GetDividerBoundsInScreen(false );
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());
EXPECT_EQ(bounds_divider.x(), bounds_window1.x() + bounds_window1.width());
EXPECT_EQ(bounds_window2.x(), bounds_divider.x() + bounds_divider.width());
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 );
EXPECT_NE(bounds_window1, new_bounds_window1);
EXPECT_NE(bounds_window2, new_bounds_window2);
EXPECT_NE(bounds_divider, new_bounds_divider);
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_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::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kSecondary);
const gfx::Rect bounds_window1 = window1->GetBoundsInScreen();
const gfx::Rect bounds_window2 = window2->GetBoundsInScreen();
const gfx::Rect bounds_divider =
split_view_divider()->GetDividerBoundsInScreen(false );
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());
EXPECT_EQ(bounds_divider.x(), bounds_window1.x() + bounds_window1.width());
EXPECT_EQ(bounds_window2.x(), bounds_divider.x() + bounds_divider.width());
const gfx::Point divider_center =
split_view_divider()
->GetDividerBoundsInScreen(false )
.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 );
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 );
EXPECT_NE(bounds_window1, new_bounds_window1);
EXPECT_NE(bounds_window2, new_bounds_window2);
EXPECT_NE(bounds_divider, new_bounds_divider);
EXPECT_NE(bounds_window1, animation_start_bounds_window1);
EXPECT_NE(bounds_window2, animation_start_bounds_window2);
EXPECT_NE(bounds_divider, animation_start_bounds_divider);
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_F(SplitViewControllerTest,
InternalDisplayConfigurationChangeDuringDividerSnapToLeft) {
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::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kSecondary);
const gfx::Rect bounds_window1 = window1->GetBoundsInScreen();
const gfx::Rect bounds_window2 = window2->GetBoundsInScreen();
const gfx::Rect bounds_divider =
split_view_divider()->GetDividerBoundsInScreen(false );
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());
EXPECT_EQ(bounds_divider.x(), bounds_window1.x() + bounds_window1.width());
EXPECT_EQ(bounds_window2.x(), bounds_divider.x() + bounds_divider.width());
const gfx::Point divider_center =
split_view_divider()
->GetDividerBoundsInScreen(false )
.CenterPoint();
GetEventGenerator()->set_current_screen_location(divider_center);
GetEventGenerator()->DragMouseBy(20 - bounds_window1.width(), 0);
ASSERT_TRUE(split_view_controller()->InSplitViewMode());
ASSERT_TRUE(IsDividerAnimating());
UpdateDisplay("507x500");
EXPECT_FALSE(split_view_controller()->InSplitViewMode());
}
TEST_F(SplitViewControllerTest,
InternalDisplayConfigurationChangeDuringDividerSnapToRight) {
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::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kSecondary);
const gfx::Rect bounds_window1 = window1->GetBoundsInScreen();
const gfx::Rect bounds_window2 = window2->GetBoundsInScreen();
const gfx::Rect bounds_divider =
split_view_divider()->GetDividerBoundsInScreen(false );
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());
EXPECT_EQ(bounds_divider.x(), bounds_window1.x() + bounds_window1.width());
EXPECT_EQ(bounds_window2.x(), bounds_divider.x() + bounds_divider.width());
const gfx::Point divider_center =
split_view_divider()
->GetDividerBoundsInScreen(false )
.CenterPoint();
GetEventGenerator()->set_current_screen_location(divider_center);
GetEventGenerator()->DragMouseBy(bounds_window2.width() - 20, 0);
ASSERT_TRUE(split_view_controller()->InSplitViewMode());
ASSERT_TRUE(IsDividerAnimating());
UpdateDisplay("507x500");
EXPECT_FALSE(split_view_controller()->InSplitViewMode());
}
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::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kSecondary);
ASSERT_EQ(split_view_controller()->primary_window(), window1.get());
ASSERT_EQ(split_view_controller()->secondary_window(), window2.get());
gfx::Rect left_bounds = window1->GetBoundsInScreen();
gfx::Rect right_bounds = window2->GetBoundsInScreen();
split_view_controller()->SwapWindows(
SplitViewController::SwapWindowsSource::kDoubleTap);
EXPECT_EQ(split_view_controller()->primary_window(), window2.get());
EXPECT_EQ(split_view_controller()->secondary_window(), window1.get());
EXPECT_EQ(left_bounds, window2->GetBoundsInScreen());
EXPECT_EQ(right_bounds, window1->GetBoundsInScreen());
EndSplitView();
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kSecondary);
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kPrimary);
ASSERT_EQ(split_view_controller()->secondary_window(), window1.get());
ASSERT_EQ(split_view_controller()->primary_window(), window2.get());
left_bounds = window2->GetBoundsInScreen();
right_bounds = window1->GetBoundsInScreen();
split_view_controller()->SwapWindows(
SplitViewController::SwapWindowsSource::kDoubleTap);
EXPECT_EQ(split_view_controller()->primary_window(), window1.get());
EXPECT_EQ(split_view_controller()->secondary_window(), window2.get());
EXPECT_EQ(left_bounds, window1->GetBoundsInScreen());
EXPECT_EQ(right_bounds, window2->GetBoundsInScreen());
const gfx::Point divider_center =
split_view_divider()
->GetDividerBoundsInScreen(false )
.CenterPoint();
GetEventGenerator()->set_current_screen_location(divider_center);
GetEventGenerator()->DoubleClickLeftButton();
EXPECT_EQ(split_view_controller()->primary_window(), window2.get());
EXPECT_EQ(split_view_controller()->secondary_window(), window1.get());
EXPECT_EQ(left_bounds, window2->GetBoundsInScreen());
EXPECT_EQ(right_bounds, window1->GetBoundsInScreen());
}
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::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kSecondary);
ASSERT_EQ(split_view_controller()->primary_window(), window1.get());
ASSERT_EQ(split_view_controller()->secondary_window(), window2.get());
gfx::Rect left_bounds = window1->GetBoundsInScreen();
gfx::Rect right_bounds = window2->GetBoundsInScreen();
const gfx::Point divider_center =
split_view_divider()
->GetDividerBoundsInScreen(false )
.CenterPoint();
GetEventGenerator()->GestureTapAt(divider_center);
GetEventGenerator()->GestureTapAt(divider_center);
EXPECT_EQ(split_view_controller()->primary_window(), window2.get());
EXPECT_EQ(split_view_controller()->secondary_window(), window1.get());
EXPECT_EQ(left_bounds, window2->GetBoundsInScreen());
EXPECT_EQ(right_bounds, window1->GetBoundsInScreen());
}
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::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kSecondary);
ASSERT_EQ(split_view_controller()->primary_window(), window1.get());
ASSERT_EQ(split_view_controller()->secondary_window(), window2.get());
const gfx::Point divider_center =
split_view_divider()
->GetDividerBoundsInScreen(false )
.CenterPoint();
GetEventGenerator()->set_current_screen_location(divider_center);
GetEventGenerator()->DragMouseBy(20, 0);
GetEventGenerator()->DoubleClickLeftButton();
EXPECT_EQ(split_view_controller()->primary_window(), window1.get());
EXPECT_EQ(split_view_controller()->secondary_window(), window2.get());
}
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::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kSecondary);
ASSERT_EQ(split_view_controller()->primary_window(), window1.get());
ASSERT_EQ(split_view_controller()->secondary_window(), window2.get());
const gfx::Point divider_center =
split_view_divider()
->GetDividerBoundsInScreen(false )
.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()->primary_window(), window1.get());
EXPECT_EQ(split_view_controller()->secondary_window(), window2.get());
}
TEST_F(SplitViewControllerTest, OverviewNotStealFocusOnSwapWindows) {
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();
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kPrimary);
wm::ActivateWindow(window2.get());
split_view_controller()->SwapWindows(
SplitViewController::SwapWindowsSource::kDoubleTap);
EXPECT_TRUE(wm::IsActiveWindow(window2.get()));
}
class SplitViewControllerFloatTest : public SplitViewControllerTest {
public:
SplitViewControllerFloatTest() = default;
SplitViewControllerFloatTest(const SplitViewControllerFloatTest&) = delete;
SplitViewControllerFloatTest& operator=(const SplitViewControllerFloatTest&) =
delete;
~SplitViewControllerFloatTest() override = default;
private:
base::test::ScopedFeatureList scoped_feature_list_{
chromeos::wm::features::kWindowLayoutMenu};
};
TEST_F(SplitViewControllerFloatTest, DontAutosnapFloatedWindow) {
std::unique_ptr<aura::Window> window1(CreateAppWindow());
std::unique_ptr<aura::Window> window2(CreateAppWindow());
std::unique_ptr<aura::Window> floated_window(CreateAppWindow());
Shell::Get()->float_controller()->ToggleFloat(floated_window.get());
ASSERT_TRUE(WindowState::Get(floated_window.get())->IsFloated());
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kPrimary);
auto* overview_controller = Shell::Get()->overview_controller();
ASSERT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
auto* overview_session = overview_controller->overview_session();
ASSERT_TRUE(overview_session->IsWindowInOverview(window2.get()));
ASSERT_TRUE(overview_session->IsWindowInOverview(floated_window.get()));
wm::ActivateWindow(window2.get());
EXPECT_TRUE(split_view_controller()->IsWindowInSplitView(window2.get()));
wm::ActivateWindow(floated_window.get());
EXPECT_FALSE(
split_view_controller()->IsWindowInSplitView(floated_window.get()));
EXPECT_TRUE(WindowState::Get(floated_window.get())->IsFloated());
EndSplitView();
EXPECT_TRUE(WindowState::Get(floated_window.get())->IsFloated());
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kPrimary);
EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
overview_session = overview_controller->overview_session();
EXPECT_TRUE(overview_session->IsWindowInOverview(floated_window.get()));
wm::ActivateWindow(floated_window.get());
EXPECT_TRUE(
split_view_controller()->IsWindowInSplitView(floated_window.get()));
EXPECT_FALSE(WindowState::Get(floated_window.get())->IsFloated());
}
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::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kSecondary);
ASSERT_EQ(split_view_controller()->primary_window(), window1.get());
ASSERT_EQ(split_view_controller()->secondary_window(), window2.get());
const gfx::Point divider_center =
split_view_divider()
->GetDividerBoundsInScreen(false )
.CenterPoint();
GetEventGenerator()->set_current_screen_location(divider_center);
GetEventGenerator()->DragMouseBy(20, 0);
GetEventGenerator()->PressLeftButton();
EXPECT_FALSE(split_view_controller()->is_resizing_with_divider());
GetEventGenerator()->ReleaseLeftButton();
}
TEST_F(SplitViewControllerTest, LongPressEntersSplitView) {
LongPressOnOverivewButtonTray();
EXPECT_FALSE(split_view_controller()->InSplitViewMode());
const gfx::Rect bounds(0, 0, 400, 400);
std::unique_ptr<aura::Window> window1(CreateWindow(bounds));
wm::ActivateWindow(window1.get());
LongPressOnOverivewButtonTray();
EXPECT_TRUE(split_view_controller()->InSplitViewMode());
}
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());
ToggleOverview();
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kPrimary);
ASSERT_TRUE(split_view_controller()->InSplitViewMode());
LongPressOnOverivewButtonTray();
EXPECT_FALSE(split_view_controller()->InSplitViewMode());
EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
EXPECT_EQ(window1.get(), window_util::GetActiveWindow());
ToggleOverview();
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kSecondary);
ASSERT_TRUE(split_view_controller()->InSplitViewMode());
LongPressOnOverivewButtonTray();
EXPECT_FALSE(split_view_controller()->InSplitViewMode());
EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
EXPECT_EQ(window1.get(), window_util::GetActiveWindow());
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kSecondary);
wm::ActivateWindow(window1.get());
ASSERT_TRUE(split_view_controller()->InSplitViewMode());
LongPressOnOverivewButtonTray();
EXPECT_FALSE(split_view_controller()->InSplitViewMode());
EXPECT_EQ(window1.get(), window_util::GetActiveWindow());
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kSecondary);
wm::ActivateWindow(window2.get());
ASSERT_TRUE(split_view_controller()->InSplitViewMode());
LongPressOnOverivewButtonTray();
EXPECT_FALSE(split_view_controller()->InSplitViewMode());
EXPECT_EQ(window2.get(), window_util::GetActiveWindow());
}
TEST_F(SplitViewControllerTest, LongPressEntersSplitViewWithTransientChild) {
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());
LongPressOnOverivewButtonTray();
EXPECT_TRUE(split_view_controller()->InSplitViewMode());
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::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
right_window.get(), SplitViewController::SnapPosition::kSecondary);
ASSERT_TRUE(split_view_controller()->InSplitViewMode());
aura::Window* transient_child =
aura::test::CreateTestWindowWithId(0, right_window.get());
::wm::AddTransientChild(right_window.get(), transient_child);
wm::ActivateWindow(transient_child);
LongPressOnOverivewButtonTray();
EXPECT_FALSE(split_view_controller()->InSplitViewMode());
EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
EXPECT_EQ(right_window.get(), window_util::GetActiveWindow());
}
#if defined(NDEBUG)
#define MAYBE_LongPressInOverviewMode LongPressInOverviewMode
#else
#define MAYBE_LongPressInOverviewMode DISABLED_LongPressInOverviewMode
#endif
TEST_F(SplitViewControllerTest, MAYBE_LongPressInOverviewMode) {
ui::ScopedAnimationDurationScaleMode anmatin_scale(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
ToggleOverview();
WaitForOverviewEnterAnimation();
ASSERT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
ASSERT_FALSE(split_view_controller()->InSplitViewMode());
CheckOverviewEnterExitHistogram("EnterInTablet", {0, 0}, {0, 0});
LongPressOnOverivewButtonTray();
EXPECT_FALSE(split_view_controller()->InSplitViewMode());
EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
std::unique_ptr<aura::Window> window(CreateWindow(gfx::Rect(0, 0, 400, 400)));
wm::ActivateWindow(window.get());
CheckOverviewEnterExitHistogram("ExitByActivation", {0, 0}, {0, 0});
ToggleOverview();
WaitForOverviewEnterAnimation();
CheckOverviewEnterExitHistogram("EnterInTablet2", {1, 0}, {0, 0});
ASSERT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
ASSERT_FALSE(split_view_controller()->InSplitViewMode());
LongPressOnOverivewButtonTray();
EXPECT_TRUE(split_view_controller()->InSplitViewMode());
EXPECT_EQ(window.get(), split_view_controller()->primary_window());
CheckOverviewEnterExitHistogram("NoTransition", {1, 0}, {0, 0});
}
TEST_F(SplitViewControllerTest, LongPressWithUnsnappableWindow) {
const gfx::Rect bounds(0, 0, 400, 400);
std::unique_ptr<aura::Window> unsnappable_window(
CreateNonSnappableWindow(bounds));
ASSERT_FALSE(split_view_controller()->InSplitViewMode());
std::unique_ptr<aura::Window> regular_window(CreateWindow(bounds));
wm::ActivateWindow(regular_window.get());
wm::ActivateWindow(unsnappable_window.get());
ASSERT_EQ(unsnappable_window.get(), window_util::GetActiveWindow());
LongPressOnOverivewButtonTray();
EXPECT_FALSE(split_view_controller()->InSplitViewMode());
ToggleOverview();
ASSERT_TRUE(Shell::Get()
->mru_window_tracker()
->BuildWindowForCycleList(kActiveDesk)
.size() > 0);
ASSERT_EQ(unsnappable_window.get(),
Shell::Get()->mru_window_tracker()->BuildWindowForCycleList(
kActiveDesk)[0]);
LongPressOnOverivewButtonTray();
EXPECT_FALSE(split_view_controller()->InSplitViewMode());
}
TEST_F(SplitViewControllerTest, LongPressWithMinimizedWindow) {
std::unique_ptr<aura::Window> window(CreateWindow(gfx::Rect(400, 400)));
WindowState::Get(window.get())->Minimize();
LongPressOnOverivewButtonTray();
EXPECT_TRUE(split_view_controller()->InSplitViewMode());
}
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());
test_api.SetDisplayRotation(display::Display::ROTATE_0,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(test_api.GetCurrentOrientation(),
chromeos::OrientationType::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::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kSecondary);
gfx::Rect bounds_window1 = window1->GetBoundsInScreen();
gfx::Rect bounds_window2 = window2->GetBoundsInScreen();
gfx::Rect bounds_divider =
split_view_divider()->GetDividerBoundsInScreen(false );
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_api.SetDisplayRotation(display::Display::ROTATE_270,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(test_api.GetCurrentOrientation(),
chromeos::OrientationType::kPortraitPrimary);
bounds_window1 = window1->GetBoundsInScreen();
bounds_window2 = window2->GetBoundsInScreen();
bounds_divider =
split_view_divider()->GetDividerBoundsInScreen(false );
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());
test_api.SetDisplayRotation(display::Display::ROTATE_180,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(test_api.GetCurrentOrientation(),
chromeos::OrientationType::kLandscapeSecondary);
bounds_window1 = window1->GetBoundsInScreen();
bounds_window2 = window2->GetBoundsInScreen();
bounds_divider =
split_view_divider()->GetDividerBoundsInScreen(false );
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());
test_api.SetDisplayRotation(display::Display::ROTATE_90,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(test_api.GetCurrentOrientation(),
chromeos::OrientationType::kPortraitSecondary);
bounds_window1 = window1->GetBoundsInScreen();
bounds_window2 = window2->GetBoundsInScreen();
bounds_divider =
split_view_divider()->GetDividerBoundsInScreen(false );
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());
test_api.SetDisplayRotation(display::Display::ROTATE_0,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(test_api.GetCurrentOrientation(),
chromeos::OrientationType::kLandscapePrimary);
bounds_window1 = window1->GetBoundsInScreen();
bounds_window2 = window2->GetBoundsInScreen();
bounds_divider =
split_view_divider()->GetDividerBoundsInScreen(false );
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_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::SnapPosition::kPrimary);
EXPECT_TRUE(split_view_controller()->InSplitViewMode());
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false);
EXPECT_FALSE(split_view_controller()->InSplitViewMode());
}
TEST_F(SplitViewControllerTest, SnapWindowWithMinimumSizeTest) {
const gfx::Rect bounds(0, 0, 400, 400);
std::unique_ptr<aura::Window> window1(CreateWindow(bounds));
EXPECT_TRUE(split_view_controller()->CanSnapWindow(window1.get()));
UpdateDisplay("800x600");
aura::test::TestWindowDelegate* delegate =
static_cast<aura::test::TestWindowDelegate*>(window1->delegate());
delegate->set_minimum_size(gfx::Size(396, 0));
EXPECT_TRUE(split_view_controller()->CanSnapWindow(window1.get()));
delegate->set_minimum_size(gfx::Size(397, 0));
EXPECT_FALSE(split_view_controller()->CanSnapWindow(window1.get()));
UpdateDisplay("799x600");
delegate->set_minimum_size(gfx::Size(395, 0));
EXPECT_TRUE(split_view_controller()->CanSnapWindow(window1.get()));
delegate->set_minimum_size(gfx::Size(396, 0));
EXPECT_FALSE(split_view_controller()->CanSnapWindow(window1.get()));
}
TEST_F(SplitViewControllerTest, CanSnapWindowWithUnresizableSnapProperty) {
UpdateDisplay("800x600");
const gfx::Rect bounds(0, 0, 400, 400);
std::unique_ptr<aura::Window> window(CreateWindow(bounds));
window->SetProperty(aura::client::kResizeBehaviorKey,
aura::client::kResizeBehaviorNone);
EXPECT_FALSE(split_view_controller()->CanSnapWindow(window.get()));
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false);
window->SetProperty(kUnresizableSnappedSizeKey, new gfx::Size(300, 0));
EXPECT_TRUE(split_view_controller()->CanSnapWindow(window.get()));
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
EXPECT_FALSE(split_view_controller()->CanSnapWindow(window.get()));
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false);
UpdateDisplay("200x100");
EXPECT_FALSE(split_view_controller()->CanSnapWindow(window.get()));
}
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());
test_api.SetDisplayRotation(display::Display::ROTATE_0,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(test_api.GetCurrentOrientation(),
chromeos::OrientationType::kLandscapePrimary);
gfx::Rect display_bounds =
screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
window1.get());
ToggleOverview();
EXPECT_TRUE(split_view_controller()->CanSnapWindow(window1.get()));
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kPrimary);
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()->StartResizeWithDivider(divider_bounds.CenterPoint());
gfx::Point resize_point(display_bounds.width() * 0.33f, 0);
split_view_controller()->ResizeWithDivider(resize_point);
histograms().ExpectTotalCount(
"Ash.SplitViewResize.PresentationTime.TabletMode.SingleWindow", 1);
histograms().ExpectTotalCount(
"Ash.SplitViewResize.PresentationTime.MaxLatency.TabletMode.SingleWindow",
0);
gfx::Rect snapped_window_bounds =
split_view_controller()->GetSnappedWindowBoundsInScreen(
SplitViewController::SnapPosition::kPrimary, window1.get());
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()->EndResizeWithDivider(resize_point);
histograms().ExpectTotalCount(
"Ash.SplitViewResize.PresentationTime.TabletMode.SingleWindow", 1);
histograms().ExpectTotalCount(
"Ash.SplitViewResize.PresentationTime.MaxLatency.TabletMode.SingleWindow",
1);
SkipDividerSnapAnimation();
EndSplitView();
test_api.SetDisplayRotation(display::Display::ROTATE_270,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(test_api.GetCurrentOrientation(),
chromeos::OrientationType::kPortraitPrimary);
display_bounds =
screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
window1.get());
delegate1->set_minimum_size(
gfx::Size(display_bounds.width(), display_bounds.height() * 0.4f));
EXPECT_TRUE(split_view_controller()->CanSnapWindow(window1.get()));
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kPrimary);
EXPECT_TRUE(window1->layer()->GetTargetTransform().IsIdentity());
divider_bounds = split_view_divider()->GetDividerBoundsInScreen(false);
split_view_controller()->StartResizeWithDivider(divider_bounds.CenterPoint());
resize_point.SetPoint(0, display_bounds.height() * 0.33f);
split_view_controller()->ResizeWithDivider(resize_point);
snapped_window_bounds =
split_view_controller()->GetSnappedWindowBoundsInScreen(
SplitViewController::SnapPosition::kPrimary, window1.get());
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()->EndResizeWithDivider(resize_point);
SkipDividerSnapAnimation();
EndSplitView();
test_api.SetDisplayRotation(display::Display::ROTATE_180,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(test_api.GetCurrentOrientation(),
chromeos::OrientationType::kLandscapeSecondary);
display_bounds =
screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
window1.get());
delegate1->set_minimum_size(
gfx::Size(display_bounds.width() * 0.4f, display_bounds.height()));
EXPECT_TRUE(split_view_controller()->CanSnapWindow(window1.get()));
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kSecondary);
EXPECT_TRUE(window1->layer()->GetTargetTransform().IsIdentity());
divider_bounds = split_view_divider()->GetDividerBoundsInScreen(false);
split_view_controller()->StartResizeWithDivider(divider_bounds.CenterPoint());
resize_point.SetPoint(display_bounds.width() * 0.33f, 0);
split_view_controller()->ResizeWithDivider(resize_point);
snapped_window_bounds =
split_view_controller()->GetSnappedWindowBoundsInScreen(
SplitViewController::SnapPosition::kSecondary, window1.get());
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()->EndResizeWithDivider(resize_point);
SkipDividerSnapAnimation();
EndSplitView();
test_api.SetDisplayRotation(display::Display::ROTATE_90,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(test_api.GetCurrentOrientation(),
chromeos::OrientationType::kPortraitSecondary);
display_bounds =
screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
window1.get());
delegate1->set_minimum_size(
gfx::Size(display_bounds.width(), display_bounds.height() * 0.4f));
EXPECT_TRUE(split_view_controller()->CanSnapWindow(window1.get()));
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kSecondary);
EXPECT_TRUE(window1->layer()->GetTargetTransform().IsIdentity());
divider_bounds = split_view_divider()->GetDividerBoundsInScreen(false);
split_view_controller()->StartResizeWithDivider(divider_bounds.CenterPoint());
resize_point.SetPoint(0, display_bounds.height() * 0.33f);
split_view_controller()->ResizeWithDivider(resize_point);
snapped_window_bounds =
split_view_controller()->GetSnappedWindowBoundsInScreen(
SplitViewController::SnapPosition::kSecondary, window1.get());
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()->EndResizeWithDivider(resize_point);
SkipDividerSnapAnimation();
EndSplitView();
}
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(chromeos::OrientationType::kLandscapePrimary,
GetCurrentScreenOrientation());
gfx::Rect workarea_bounds =
screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
window1.get());
ToggleOverview();
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kPrimary);
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());
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();
delegate1->set_minimum_size(
gfx::Size(workarea_bounds.width() * 0.4f, workarea_bounds.height()));
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kSecondary);
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());
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();
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::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kSecondary);
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());
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();
}
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::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
window1.get());
ToggleOverview();
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kPrimary);
ASSERT_TRUE(split_view_divider());
EXPECT_GT(divider_position(), 0.33f * workarea_bounds.width());
EXPECT_LE(divider_position(), 0.5f * workarea_bounds.width());
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::SnapPosition::kSecondary);
EXPECT_GT(divider_position(), 0.33f * workarea_bounds.width());
EXPECT_LE(divider_position(), 0.5f * workarea_bounds.width());
}
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::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kSecondary);
EXPECT_TRUE(split_view_controller()->InSplitViewMode());
EXPECT_EQ(split_view_controller()->state(),
SplitViewController::State::kBothSnapped);
GetSessionControllerClient()->LockScreen();
UpdateDisplay("400x800");
EXPECT_TRUE(split_view_controller()->InSplitViewMode());
EXPECT_EQ(split_view_controller()->state(),
SplitViewController::State::kBothSnapped);
GetSessionControllerClient()->UnlockScreen();
EXPECT_TRUE(split_view_controller()->InSplitViewMode());
EXPECT_EQ(split_view_controller()->state(),
SplitViewController::State::kBothSnapped);
}
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::SnapPosition::kPrimary);
ToggleOverview();
EXPECT_EQ(split_view_controller()->state(),
SplitViewController::State::kPrimarySnapped);
EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
std::unique_ptr<aura::Window> window3(CreateWindow(bounds));
EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
}
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 = WindowState::Get(window1.get());
auto* w2_state = WindowState::Get(window2.get());
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));
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kSecondary);
gfx::Rect divider_bounds =
split_view_divider()->GetDividerBoundsInScreen(false );
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);
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());
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false);
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());
}
TEST_F(SplitViewControllerTest,
MinimizeSingleWindowDuringResizeCompletesDrags) {
const gfx::Rect bounds(0, 0, 400, 400);
std::unique_ptr<aura::Window> window1(CreateWindow(bounds));
auto* w1_state = WindowState::Get(window1.get());
auto* window_state_delegate1 = new TestWindowStateDelegate();
w1_state->SetDelegate(base::WrapUnique(window_state_delegate1));
ToggleOverview();
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kPrimary);
gfx::Rect divider_bounds =
split_view_divider()->GetDividerBoundsInScreen(false );
const int screen_width =
screen_util::GetDisplayWorkAreaBoundsInParentForActiveDeskContainer(
window1.get())
.width();
GetEventGenerator()->set_current_screen_location(
divider_bounds.CenterPoint());
GetEventGenerator()->PressLeftButton();
GetEventGenerator()->MoveMouseTo(screen_width * 0.67f, 0);
EXPECT_TRUE(window_state_delegate1->drag_in_progress());
EXPECT_NE(nullptr, w1_state->drag_details());
WMEvent minimize_event(WM_EVENT_MINIMIZE);
WindowState::Get(window1.get())->OnWMEvent(&minimize_event);
EXPECT_FALSE(window_state_delegate1->drag_in_progress());
EXPECT_EQ(nullptr, w1_state->drag_details());
}
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 = WindowState::Get(window1.get());
auto* w2_state = WindowState::Get(window2.get());
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));
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kSecondary);
gfx::Rect divider_bounds =
split_view_divider()->GetDividerBoundsInScreen(false );
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);
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());
WMEvent minimize_event(WM_EVENT_MINIMIZE);
WindowState::Get(window1.get())->OnWMEvent(&minimize_event);
EXPECT_FALSE(window_state_delegate1->drag_in_progress());
EXPECT_FALSE(window_state_delegate2->drag_in_progress());
EXPECT_EQ(nullptr, w1_state->drag_details());
EXPECT_EQ(nullptr, w2_state->drag_details());
}
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::SnapPosition::kPrimary);
EXPECT_TRUE(split_view_controller()->InSplitViewMode());
window1->SetProperty(aura::client::kResizeBehaviorKey,
aura::client::kResizeBehaviorNone);
EXPECT_FALSE(split_view_controller()->InSplitViewMode());
}
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()));
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kPrimary);
EXPECT_FALSE(shadow_controller->IsShadowVisibleForWindow(window1.get()));
auto* overview_controller = Shell::Get()->overview_controller();
EXPECT_TRUE(overview_controller->InOverviewSession());
auto* overview_session = overview_controller->overview_session();
EXPECT_TRUE(overview_session->IsWindowInOverview(window2.get()));
EXPECT_TRUE(overview_session->IsWindowInOverview(window3.get()));
EXPECT_FALSE(shadow_controller->IsShadowVisibleForWindow(window2.get()));
EXPECT_FALSE(shadow_controller->IsShadowVisibleForWindow(window3.get()));
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kSecondary);
EXPECT_FALSE(shadow_controller->IsShadowVisibleForWindow(window1.get()));
EXPECT_FALSE(shadow_controller->IsShadowVisibleForWindow(window2.get()));
EXPECT_TRUE(shadow_controller->IsShadowVisibleForWindow(window3.get()));
split_view_controller()->SnapWindow(
window3.get(), SplitViewController::SnapPosition::kSecondary);
EXPECT_FALSE(shadow_controller->IsShadowVisibleForWindow(window1.get()));
EXPECT_TRUE(shadow_controller->IsShadowVisibleForWindow(window2.get()));
EXPECT_FALSE(shadow_controller->IsShadowVisibleForWindow(window3.get()));
}
TEST_F(SplitViewControllerTest, OverviewExitAnimationTest) {
ui::ScopedAnimationDurationScaleMode anmatin_scale(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
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<OverviewStatesObserver> overview_observer =
std::make_unique<OverviewStatesObserver>(window1->GetRootWindow());
ToggleOverview();
WaitForOverviewEnterAnimation();
EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
ToggleOverview();
WaitForOverviewExitAnimation();
EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
EXPECT_TRUE(overview_observer->overview_animate_when_exiting());
CheckOverviewEnterExitHistogram("NormalEnterExit", {1, 0}, {1, 0});
ToggleOverview();
WaitForOverviewEnterAnimation();
wm::ActivateWindow(window1.get());
WaitForOverviewExitAnimation();
EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
EXPECT_TRUE(overview_observer->overview_animate_when_exiting());
CheckOverviewEnterExitHistogram("EnterExitByActivation", {2, 0}, {2, 0});
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kPrimary);
WaitForOverviewEnterAnimation();
EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
overview_observer =
std::make_unique<OverviewStatesObserver>(window1->GetRootWindow());
EXPECT_TRUE(overview_observer->overview_animate_when_exiting());
CheckOverviewEnterExitHistogram("EnterInSplitView", {2, 1}, {2, 0});
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kSecondary);
WaitForOverviewExitAnimation();
EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
EXPECT_FALSE(overview_observer->overview_animate_when_exiting());
CheckOverviewEnterExitHistogram("ExitBySnap", {2, 1}, {2, 1});
ToggleOverview();
WaitForOverviewEnterAnimation();
EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
EXPECT_TRUE(overview_observer->overview_animate_when_exiting());
CheckOverviewEnterExitHistogram("EnterInSplitView2", {2, 2}, {2, 1});
ToggleOverview();
WaitForOverviewExitAnimation();
EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
EXPECT_FALSE(overview_observer->overview_animate_when_exiting());
CheckOverviewEnterExitHistogram("ExitInSplitView", {2, 2}, {2, 2});
}
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::SnapPosition;
split_view_controller()->SnapWindow(window1.get(), svc::kPrimary);
split_view_controller()->SnapWindow(window2.get(), svc::kSecondary);
split_view_controller()->EndSplitView();
EXPECT_TRUE(WindowState::Get(window1.get())->IsMaximized());
EXPECT_TRUE(WindowState::Get(window2.get())->IsMaximized());
split_view_controller()->SnapWindow(window1.get(), svc::kPrimary);
split_view_controller()->SnapWindow(window2.get(), svc::kSecondary);
split_view_controller()->EndSplitView(
SplitViewController::EndReason::kHomeLauncherPressed);
EXPECT_FALSE(WindowState::Get(window1.get())->IsMaximized());
EXPECT_FALSE(WindowState::Get(window2.get())->IsMaximized());
}
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::SnapPosition::kPrimary);
EXPECT_TRUE(split_view_controller()->InSplitViewMode());
EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
wm::ActivateWindow(window3.get());
EXPECT_FALSE(split_view_controller()->InSplitViewMode());
EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
}
TEST_F(SplitViewControllerTest, AdjustTransientChildBounds) {
std::unique_ptr<views::Widget> widget(CreateTestWidget());
aura::Window* window = widget->GetNativeWindow();
window->SetProperty(aura::client::kResizeBehaviorKey,
aura::client::kResizeBehaviorCanResize |
aura::client::kResizeBehaviorCanMaximize);
split_view_controller()->SnapWindow(
window, SplitViewController::SnapPosition::kPrimary);
const gfx::Rect window_bounds = window->GetBoundsInScreen();
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));
EXPECT_TRUE(window_bounds.Contains(bubble_window->GetBoundsInScreen()));
bubble_window->SetBoundsInScreen(
split_view_controller()->GetSnappedWindowBoundsInScreen(
SplitViewController::SnapPosition::kSecondary, window),
display::Screen::GetScreen()->GetDisplayNearestWindow(window));
EXPECT_TRUE(window_bounds.Contains(bubble_window->GetBoundsInScreen()));
EndSplitView();
}
TEST_F(SplitViewControllerTest, DividerClosestRatioOnWorkArea) {
UpdateDisplay("1200x800");
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(chromeos::OrientationType::kLandscapePrimary,
test_api.GetCurrentOrientation());
const gfx::Rect bounds(0, 0, 200, 200);
std::unique_ptr<aura::Window> window(CreateWindow(bounds));
ToggleOverview();
split_view_controller()->SnapWindow(
window.get(), SplitViewController::SnapPosition::kPrimary);
test_api.SetDisplayRotation(display::Display::ROTATE_90,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(chromeos::OrientationType::kPortraitSecondary,
test_api.GetCurrentOrientation());
EXPECT_EQ(divider_closest_ratio(), chromeos::kDefaultSnapRatio);
test_api.SetDisplayRotation(display::Display::ROTATE_0,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(chromeos::OrientationType::kLandscapePrimary,
test_api.GetCurrentOrientation());
EXPECT_EQ(divider_closest_ratio(), chromeos::kDefaultSnapRatio);
gfx::Rect divider_bounds =
split_view_divider()->GetDividerBoundsInScreen(false);
gfx::Rect workarea_bounds =
screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
window.get());
generator->set_current_screen_location(divider_bounds.CenterPoint());
generator->DragMouseTo(
gfx::Point(workarea_bounds.width() * chromeos::kOneThirdSnapRatio,
workarea_bounds.y()));
SkipDividerSnapAnimation();
EXPECT_EQ(divider_closest_ratio(), chromeos::kOneThirdSnapRatio);
test_api.SetDisplayRotation(display::Display::ROTATE_90,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(chromeos::OrientationType::kPortraitSecondary,
test_api.GetCurrentOrientation());
EXPECT_EQ(divider_closest_ratio(), chromeos::kTwoThirdSnapRatio);
test_api.SetDisplayRotation(display::Display::ROTATE_270,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(chromeos::OrientationType::kPortraitPrimary,
test_api.GetCurrentOrientation());
EXPECT_EQ(divider_closest_ratio(), chromeos::kOneThirdSnapRatio);
}
TEST_F(SplitViewControllerTest,
DividerClosestRatioUpdatedForClamshellTabletTransition) {
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());
test_api.SetDisplayRotation(display::Display::ROTATE_180,
display::Display::RotationSource::ACTIVE);
TabletModeController* tablet_mode_controller =
Shell::Get()->tablet_mode_controller();
tablet_mode_controller->SetEnabledForTest(false);
Shell::Get()->display_manager()->SetDisplayRotation(
display_id, display::Display::ROTATE_180,
display::Display::RotationSource::ACTIVE);
tablet_mode_controller->SetEnabledForTest(true);
const gfx::Rect bounds(0, 0, 200, 200);
std::unique_ptr<aura::Window> window(CreateWindow(bounds));
ToggleOverview();
split_view_controller()->SnapWindow(
window.get(), SplitViewController::SnapPosition::kPrimary);
ui::test::EventGenerator* generator = GetEventGenerator();
const gfx::Rect divider_bounds =
split_view_divider()->GetDividerBoundsInScreen(false);
generator->set_current_screen_location(divider_bounds.CenterPoint());
const gfx::Rect workarea_bounds =
screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
window.get());
generator->DragMouseTo(
gfx::Point(workarea_bounds.width() * chromeos::kOneThirdSnapRatio,
workarea_bounds.y()));
SkipDividerSnapAnimation();
EXPECT_EQ(divider_closest_ratio(), chromeos::kTwoThirdSnapRatio);
test_api.SetDisplayRotation(display::Display::ROTATE_0,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(divider_closest_ratio(), chromeos::kOneThirdSnapRatio);
}
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::SnapPosition::kPrimary);
EXPECT_TRUE(split_view_controller()->InSplitViewMode());
window_util::PinWindow(window1.get(), true);
EXPECT_FALSE(split_view_controller()->InSplitViewMode());
}
TEST_F(SplitViewControllerTest, PinnedWindowDisallowsSplitView) {
const gfx::Rect bounds(0, 0, 400, 400);
std::unique_ptr<aura::Window> window1(CreateWindow(bounds));
EXPECT_TRUE(ShouldAllowSplitView());
window_util::PinWindow(window1.get(), true);
EXPECT_FALSE(ShouldAllowSplitView());
}
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());
test_api.SetDisplayRotation(display::Display::ROTATE_0,
display::Display::RotationSource::ACTIVE);
gfx::Rect display_bounds =
screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
window.get());
ToggleOverview();
split_view_controller()->SnapWindow(
window.get(), SplitViewController::SnapPosition::kPrimary);
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()->StartResizeWithDivider(divider_bounds.CenterPoint());
gfx::Point resize_point(display_bounds.width() * 0.33f, 0);
split_view_controller()->ResizeWithDivider(resize_point);
histograms().ExpectTotalCount(
"Ash.SplitViewResize.PresentationTime.TabletMode.SingleWindow", 1);
histograms().ExpectTotalCount(
"Ash.SplitViewResize.PresentationTime.MaxLatency.TabletMode.SingleWindow",
0);
ASSERT_FALSE(window->layer()->GetTargetTransform().IsIdentity());
EndSplitView();
histograms().ExpectTotalCount(
"Ash.SplitViewResize.PresentationTime.TabletMode.SingleWindow", 1);
histograms().ExpectTotalCount(
"Ash.SplitViewResize.PresentationTime.MaxLatency.TabletMode.SingleWindow",
1);
EXPECT_TRUE(window->layer()->GetTargetTransform().IsIdentity());
}
TEST_F(SplitViewControllerTest, ResizeTwoWindows) {
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));
std::unique_ptr<aura::Window> window2(CreateWindow(bounds));
gfx::Rect display_bounds =
screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
window1.get());
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kSecondary);
gfx::Rect divider_bounds =
split_view_divider()->GetDividerBoundsInScreen(false);
split_view_controller()->StartResizeWithDivider(divider_bounds.CenterPoint());
gfx::Point resize_point(display_bounds.width() * chromeos::kOneThirdSnapRatio,
0);
split_view_controller()->ResizeWithDivider(resize_point);
histograms().ExpectTotalCount(
"Ash.SplitViewResize.PresentationTime.TabletMode.MultiWindow", 1);
split_view_controller()->ResizeWithDivider(
gfx::Point(resize_point.x(), resize_point.y() + 1));
histograms().ExpectTotalCount(
"Ash.SplitViewResize.PresentationTime.TabletMode.MultiWindow", 2);
histograms().ExpectTotalCount(
"Ash.SplitViewResize.PresentationTime.MaxLatency.TabletMode.MultiWindow",
0);
split_view_controller()->EndResizeWithDivider(resize_point);
histograms().ExpectTotalCount(
"Ash.SplitViewResize.PresentationTime.TabletMode.MultiWindow", 2);
histograms().ExpectTotalCount(
"Ash.SplitViewResize.PresentationTime.MaxLatency.TabletMode.MultiWindow",
1);
ToggleOverview();
split_view_controller()->StartResizeWithDivider(divider_bounds.CenterPoint());
split_view_controller()->ResizeWithDivider(resize_point);
histograms().ExpectTotalCount(
"Ash.SplitViewResize.PresentationTime.TabletMode.WithOverview", 1);
split_view_controller()->ResizeWithDivider(
gfx::Point(resize_point.x(), resize_point.y() + 1));
histograms().ExpectTotalCount(
"Ash.SplitViewResize.PresentationTime.TabletMode.WithOverview", 2);
histograms().ExpectTotalCount(
"Ash.SplitViewResize.PresentationTime.MaxLatency.TabletMode.WithOverview",
0);
split_view_controller()->EndResizeWithDivider(resize_point);
histograms().ExpectTotalCount(
"Ash.SplitViewResize.PresentationTime.TabletMode.WithOverview", 2);
histograms().ExpectTotalCount(
"Ash.SplitViewResize.PresentationTime.MaxLatency.TabletMode.WithOverview",
1);
}
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());
test_api.SetDisplayRotation(display::Display::ROTATE_0,
display::Display::RotationSource::ACTIVE);
gfx::Rect display_bounds =
screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
window.get());
ToggleOverview();
split_view_controller()->SnapWindow(
window.get(), SplitViewController::SnapPosition::kPrimary);
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()->StartResizeWithDivider(divider_bounds.CenterPoint());
gfx::Point resize_point((int)(display_bounds.width() * 0.33f) + 20, 0);
split_view_controller()->ResizeWithDivider(resize_point);
split_view_controller()->EndResizeWithDivider(resize_point);
ASSERT_TRUE(IsDividerAnimating());
ASSERT_FALSE(window->layer()->GetTargetTransform().IsIdentity());
EndSplitView();
EXPECT_FALSE(IsDividerAnimating());
EXPECT_TRUE(window->layer()->GetTargetTransform().IsIdentity());
}
class TestOverviewItemsOnOverviewModeEndObserver : public OverviewObserver {
public:
TestOverviewItemsOnOverviewModeEndObserver() {
Shell::Get()->overview_controller()->AddObserver(this);
}
TestOverviewItemsOnOverviewModeEndObserver(
const TestOverviewItemsOnOverviewModeEndObserver&) = delete;
TestOverviewItemsOnOverviewModeEndObserver& operator=(
const TestOverviewItemsOnOverviewModeEndObserver&) = delete;
~TestOverviewItemsOnOverviewModeEndObserver() override {
Shell::Get()->overview_controller()->RemoveObserver(this);
}
void OnOverviewModeEnding(OverviewSession* overview_session) override {
items_on_last_overview_end_ = overview_session->num_items();
}
int items_on_last_overview_end() const { return items_on_last_overview_end_; }
private:
int items_on_last_overview_end_ = 0;
};
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());
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kPrimary);
ASSERT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
EXPECT_EQ(
1u, Shell::Get()->overview_controller()->overview_session()->num_items());
TestOverviewItemsOnOverviewModeEndObserver observer;
ToggleOverview();
EXPECT_EQ(0, observer.items_on_last_overview_end());
}
TEST_F(SplitViewControllerTest, EndSplitViewWhileDragging) {
std::unique_ptr<aura::Window> window = CreateTestWindow();
ToggleOverview();
split_view_controller()->SnapWindow(
window.get(), SplitViewController::SnapPosition::kPrimary);
gfx::Rect divider_bounds =
split_view_divider()->GetDividerBoundsInScreen(false);
split_view_controller()->StartResizeWithDivider(divider_bounds.CenterPoint());
ASSERT_TRUE(split_view_controller()->InSplitViewMode());
ASSERT_TRUE(split_view_controller()->is_resizing_with_divider());
gfx::Point resize_point(divider_bounds.CenterPoint());
resize_point.Offset(100, 0);
split_view_controller()->ResizeWithDivider(resize_point);
histograms().ExpectTotalCount(
"Ash.SplitViewResize.PresentationTime.TabletMode.SingleWindow", 1);
histograms().ExpectTotalCount(
"Ash.SplitViewResize.PresentationTime.MaxLatency.TabletMode.SingleWindow",
0);
split_view_controller()->EndSplitView();
EXPECT_FALSE(split_view_controller()->is_resizing_with_divider());
histograms().ExpectTotalCount(
"Ash.SplitViewResize.PresentationTime.TabletMode.SingleWindow", 1);
histograms().ExpectTotalCount(
"Ash.SplitViewResize.PresentationTime.MaxLatency.TabletMode.SingleWindow",
1);
}
TEST_F(SplitViewControllerTest, AutoSnapFromMinimizedState) {
const gfx::Rect bounds(0, 0, 400, 400);
std::unique_ptr<aura::Window> window1(CreateWindow(bounds));
std::unique_ptr<aura::Window> window2(CreateNonSnappableWindow(bounds));
std::unique_ptr<aura::Window> window3(CreateWindow(bounds));
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false);
WindowState::Get(window1.get())->Minimize();
window1->Show();
EXPECT_FALSE(split_view_controller()->InSplitViewMode());
EXPECT_FALSE(split_view_controller()->IsWindowInSplitView(window1.get()));
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
WindowState::Get(window1.get())->Minimize();
window1->Show();
EXPECT_FALSE(split_view_controller()->InSplitViewMode());
EXPECT_FALSE(split_view_controller()->IsWindowInSplitView(window1.get()));
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
WindowState::Get(window2.get())->Minimize();
window2->Show();
EXPECT_FALSE(split_view_controller()->InSplitViewMode());
EXPECT_FALSE(split_view_controller()->IsWindowInSplitView(window2.get()));
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
split_view_controller()->SnapWindow(
window3.get(), SplitViewController::SnapPosition::kPrimary);
EXPECT_TRUE(split_view_controller()->InTabletSplitViewMode());
EXPECT_TRUE(split_view_controller()->IsWindowInSplitView(window3.get()));
EXPECT_EQ(split_view_controller()->GetPositionOfSnappedWindow(window3.get()),
SplitViewController::SnapPosition::kPrimary);
WindowState::Get(window1.get())->Minimize();
window1->SetProperty(kHideDuringWindowDragging, true);
window1->Show();
EXPECT_FALSE(split_view_controller()->IsWindowInSplitView(window1.get()));
window1->ClearProperty(kHideDuringWindowDragging);
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
split_view_controller()->SnapWindow(
window3.get(), SplitViewController::SnapPosition::kPrimary);
EXPECT_TRUE(split_view_controller()->InTabletSplitViewMode());
EXPECT_TRUE(split_view_controller()->IsWindowInSplitView(window3.get()));
EXPECT_EQ(split_view_controller()->GetPositionOfSnappedWindow(window3.get()),
SplitViewController::SnapPosition::kPrimary);
WindowState::Get(window1.get())->Minimize();
window1->Show();
EXPECT_TRUE(split_view_controller()->InTabletSplitViewMode());
EXPECT_TRUE(split_view_controller()->IsWindowInSplitView(window1.get()));
EXPECT_EQ(split_view_controller()->GetPositionOfSnappedWindow(window1.get()),
SplitViewController::SnapPosition::kSecondary);
EndSplitView();
}
TEST_F(SplitViewControllerTest, DoNotObserveTransientIfNotInSplitview) {
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<views::Widget> widget(CreateTestWidget());
aura::Window* parent = widget->GetNativeWindow();
parent->SetProperty(aura::client::kResizeBehaviorKey,
aura::client::kResizeBehaviorCanResize |
aura::client::kResizeBehaviorCanMaximize);
views::Widget* bubble_widget = views::BubbleDialogDelegateView::CreateBubble(
new TestBubbleDialogDelegateView(widget->GetContentsView()));
aura::Window* bubble_transient = bubble_widget->GetNativeWindow();
EXPECT_TRUE(::wm::HasTransientAncestor(bubble_transient, parent));
ToggleOverview();
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
parent, SplitViewController::SnapPosition::kSecondary);
EXPECT_TRUE(bubble_transient->HasObserver(split_view_divider()));
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kSecondary);
EXPECT_FALSE(bubble_transient->HasObserver(split_view_divider()));
}
TEST_F(SplitViewControllerTest, WindowDestroyedDuringResize) {
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::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kSecondary);
gfx::Rect divider_bounds =
split_view_divider()->GetDividerBoundsInScreen(false);
split_view_controller()->StartResizeWithDivider(divider_bounds.CenterPoint());
split_view_controller()->ResizeWithDivider(gfx::Point(100, 100));
window1.reset();
EXPECT_FALSE(split_view_controller()->is_resizing_with_divider());
}
TEST_F(SplitViewControllerTest, WMSnapEvent) {
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_FALSE(split_view_controller()->InSplitViewMode());
WMEvent wm_left_snap_event(WM_EVENT_SNAP_PRIMARY);
WindowState::Get(window1.get())->OnWMEvent(&wm_left_snap_event);
EXPECT_TRUE(split_view_controller()->InSplitViewMode());
EXPECT_EQ(split_view_controller()->primary_window(), window1.get());
EXPECT_FALSE(split_view_controller()->IsWindowInSplitView(window2.get()));
OverviewController* overview_controller = Shell::Get()->overview_controller();
EXPECT_TRUE(overview_controller->InOverviewSession());
OverviewSession* overview_session = overview_controller->overview_session();
EXPECT_TRUE(overview_session->IsWindowInOverview(window2.get()));
WMEvent wm_right_snap_event(WM_EVENT_SNAP_SECONDARY);
WindowState::Get(window1.get())->OnWMEvent(&wm_right_snap_event);
EXPECT_TRUE(split_view_controller()->InSplitViewMode());
EXPECT_EQ(split_view_controller()->secondary_window(), window1.get());
EXPECT_FALSE(split_view_controller()->IsWindowInSplitView(window2.get()));
EXPECT_TRUE(overview_controller->InOverviewSession());
EXPECT_TRUE(overview_session->IsWindowInOverview(window2.get()));
WindowState::Get(window2.get())->OnWMEvent(&wm_right_snap_event);
EXPECT_TRUE(split_view_controller()->InSplitViewMode());
EXPECT_EQ(split_view_controller()->secondary_window(), window2.get());
EXPECT_FALSE(split_view_controller()->IsWindowInSplitView(window1.get()));
EXPECT_TRUE(overview_controller->InOverviewSession());
EXPECT_TRUE(overview_session->IsWindowInOverview(window1.get()));
WindowState::Get(window1.get())->OnWMEvent(&wm_left_snap_event);
EXPECT_TRUE(split_view_controller()->InSplitViewMode());
EXPECT_EQ(split_view_controller()->primary_window(), window1.get());
EXPECT_EQ(split_view_controller()->secondary_window(), window2.get());
EXPECT_FALSE(overview_controller->InOverviewSession());
WindowState::Get(window1.get())->OnWMEvent(&wm_right_snap_event);
EXPECT_TRUE(split_view_controller()->InSplitViewMode());
EXPECT_EQ(split_view_controller()->secondary_window(), window1.get());
EXPECT_FALSE(split_view_controller()->IsWindowInSplitView(window2.get()));
EXPECT_TRUE(overview_controller->InOverviewSession());
overview_session = overview_controller->overview_session();
EXPECT_TRUE(overview_session->IsWindowInOverview(window2.get()));
ToggleOverview();
EndSplitView();
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false);
WindowState::Get(window1.get())->OnWMEvent(&wm_left_snap_event);
EXPECT_FALSE(split_view_controller()->InSplitViewMode());
EXPECT_FALSE(overview_controller->InOverviewSession());
ToggleOverview();
WindowState::Get(window1.get())->OnWMEvent(&wm_left_snap_event);
EXPECT_TRUE(split_view_controller()->InSplitViewMode());
EXPECT_TRUE(overview_controller->InOverviewSession());
overview_session = overview_controller->overview_session();
EXPECT_TRUE(overview_session->IsWindowInOverview(window2.get()));
WindowState::Get(window1.get())->OnWMEvent(&wm_right_snap_event);
EXPECT_TRUE(split_view_controller()->InSplitViewMode());
EXPECT_EQ(split_view_controller()->secondary_window(), window1.get());
EXPECT_FALSE(split_view_controller()->IsWindowInSplitView(window2.get()));
EXPECT_TRUE(overview_controller->InOverviewSession());
EXPECT_TRUE(overview_session->IsWindowInOverview(window2.get()));
}
TEST_F(SplitViewControllerTest, SplitViewDividerObserveSnappedWindow) {
auto* tablet_mode_controller = Shell::Get()->tablet_mode_controller();
tablet_mode_controller->SetEnabledForTest(false);
EXPECT_FALSE(tablet_mode_controller->InTabletMode());
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));
split_view_controller()->SnapWindow(
left_window.get(), SplitViewController::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
right_window.get(), SplitViewController::SnapPosition::kSecondary);
tablet_mode_controller->SetEnabledForTest(true);
EXPECT_TRUE(tablet_mode_controller->InTabletMode());
EXPECT_TRUE(split_view_controller()->InTabletSplitViewMode());
EXPECT_TRUE(split_view_divider());
aura::Window::Windows observed_windows =
split_view_divider()->observed_windows_for_testing();
EXPECT_TRUE(base::Contains(observed_windows, left_window.get()));
EXPECT_TRUE(base::Contains(observed_windows, right_window.get()));
}
TEST_F(SplitViewControllerTest, SnapBetweenDifferentRatios) {
std::unique_ptr<aura::Window> window1 = CreateTestWindow();
std::unique_ptr<aura::Window> window2 = CreateTestWindow();
WMEvent snap_primary_default(WM_EVENT_SNAP_PRIMARY);
WindowState::Get(window1.get())->OnWMEvent(&snap_primary_default);
WMEvent snap_secondary_default(WM_EVENT_SNAP_SECONDARY);
WindowState::Get(window2.get())->OnWMEvent(&snap_secondary_default);
const gfx::Rect work_area_bounds =
display::Screen::GetScreen()->GetPrimaryDisplay().work_area();
int divider_origin_x = split_view_divider()
->GetDividerBoundsInScreen(
false)
.x();
const int divider_delta = kSplitviewDividerShortSideLength / 2;
EXPECT_EQ(
divider_origin_x,
work_area_bounds.width() * chromeos::kDefaultSnapRatio - divider_delta);
EXPECT_EQ(work_area_bounds.width() * chromeos::kDefaultSnapRatio,
window1->bounds().width() + divider_delta);
EXPECT_EQ(work_area_bounds.width() * chromeos::kDefaultSnapRatio,
window2->bounds().width() + divider_delta);
WMEvent snap_primary_two_third(WM_EVENT_SNAP_PRIMARY,
chromeos::kTwoThirdSnapRatio);
WindowState::Get(window1.get())->OnWMEvent(&snap_primary_two_third);
divider_origin_x = split_view_divider()
->GetDividerBoundsInScreen(
false)
.x();
EXPECT_EQ(divider_origin_x, std::round(work_area_bounds.width() *
chromeos::kTwoThirdSnapRatio) -
divider_delta);
EXPECT_EQ(std::round(work_area_bounds.width() * chromeos::kTwoThirdSnapRatio),
window1->bounds().width() + divider_delta);
EXPECT_EQ(std::round(work_area_bounds.width() * chromeos::kOneThirdSnapRatio),
window2->bounds().width() + divider_delta);
}
TEST_F(SplitViewControllerTest, SwapPartialWindows) {
std::unique_ptr<aura::Window> window1 = CreateTestWindow();
std::unique_ptr<aura::Window> window2 = CreateTestWindow();
WMEvent snap_primary_two_third(WM_EVENT_SNAP_PRIMARY,
chromeos::kTwoThirdSnapRatio);
WindowState::Get(window1.get())->OnWMEvent(&snap_primary_two_third);
WMEvent snap_secondary_one_third(WM_EVENT_SNAP_SECONDARY,
chromeos::kOneThirdSnapRatio);
WindowState::Get(window2.get())->OnWMEvent(&snap_secondary_one_third);
const gfx::Rect work_area_bounds =
display::Screen::GetScreen()->GetPrimaryDisplay().work_area();
int divider_origin_x = split_view_divider()
->GetDividerBoundsInScreen(
false)
.x();
const int divider_delta = kSplitviewDividerShortSideLength / 2;
EXPECT_EQ(divider_origin_x, std::round(work_area_bounds.width() *
chromeos::kTwoThirdSnapRatio) -
divider_delta);
EXPECT_EQ(std::round(work_area_bounds.width() * chromeos::kTwoThirdSnapRatio),
window1->bounds().width() + divider_delta);
EXPECT_EQ(std::round(work_area_bounds.width() * chromeos::kOneThirdSnapRatio),
window2->bounds().width() + divider_delta);
split_view_controller()->SwapWindows(
SplitViewController::SwapWindowsSource::kDoubleTap);
EXPECT_EQ(WindowState::Get(window1.get())->GetStateType(),
chromeos::WindowStateType::kSecondarySnapped);
EXPECT_EQ(WindowState::Get(window2.get())->GetStateType(),
chromeos::WindowStateType::kPrimarySnapped);
divider_origin_x = split_view_divider()
->GetDividerBoundsInScreen(
false)
.x();
EXPECT_NEAR(
divider_origin_x,
std::round(work_area_bounds.width() * chromeos::kOneThirdSnapRatio) -
divider_delta,
1);
EXPECT_NEAR(
std::round(work_area_bounds.width() * chromeos::kTwoThirdSnapRatio),
window1->bounds().width() + divider_delta, 1);
EXPECT_NEAR(
std::round(work_area_bounds.width() * chromeos::kOneThirdSnapRatio),
window2->bounds().width() + divider_delta, 1);
}
TEST_F(SplitViewControllerTest, SnapTwoThirdPartialWindow) {
UpdateDisplay("800x600");
aura::test::TestWindowDelegate window_delegate;
std::unique_ptr<aura::Window> window(CreateTestWindowInShellWithDelegate(
&window_delegate, -1, gfx::Rect(500, 500)));
window_delegate.set_minimum_size(gfx::Size(500, 500));
window->SetProperty(aura::client::kAppType,
static_cast<int>(AppType::BROWSER));
WMEvent snap_primary(WM_EVENT_SNAP_PRIMARY, chromeos::kTwoThirdSnapRatio);
WindowState::Get(window.get())->OnWMEvent(&snap_primary);
EXPECT_TRUE(WindowState::Get(window.get())->IsSnapped());
}
TEST_F(SplitViewControllerTest, SelectWindowCannotOneThirdSnap) {
UpdateDisplay("900x600");
aura::test::TestWindowDelegate window_delegate1;
std::unique_ptr<aura::Window> window1(CreateTestWindowInShellWithDelegate(
&window_delegate1, -1, gfx::Rect(500, 500)));
window_delegate1.set_minimum_size(gfx::Size(500, 500));
window1->SetProperty(aura::client::kAppType,
static_cast<int>(AppType::BROWSER));
aura::test::TestWindowDelegate window_delegate2;
std::unique_ptr<aura::Window> window2(CreateTestWindowInShellWithDelegate(
&window_delegate2, -1, gfx::Rect(500, 500)));
window_delegate2.set_minimum_size(gfx::Size(400, 400));
window2->SetProperty(aura::client::kAppType,
static_cast<int>(AppType::BROWSER));
wm::ActivateWindow(window1.get());
WMEvent snap_primary(WM_EVENT_SNAP_PRIMARY, chromeos::kTwoThirdSnapRatio);
WindowState::Get(window1.get())->OnWMEvent(&snap_primary);
ASSERT_EQ(chromeos::kTwoThirdSnapRatio,
WindowState::Get(window1.get())->snap_ratio());
ASSERT_TRUE(WindowState::Get(window1.get())->IsSnapped());
ASSERT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
wm::ActivateWindow(window2.get());
EXPECT_TRUE(WindowState::Get(window1.get())->IsMaximized());
EXPECT_TRUE(WindowState::Get(window2.get())->IsMaximized());
EXPECT_EQ(SplitViewController::State::kNoSnap,
split_view_controller()->state());
}
TEST_F(SplitViewControllerTest, SnapWindowWithMinSizeOpensOverview) {
const gfx::Rect bounds(0, 0, 400, 400);
std::unique_ptr<aura::Window> window1(CreateWindow(bounds));
std::unique_ptr<aura::Window> window2(CreateWindow(bounds));
gfx::Rect work_area_bounds =
screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
window1.get());
aura::test::TestWindowDelegate* delegate2 =
static_cast<aura::test::TestWindowDelegate*>(window2->delegate());
delegate2->set_minimum_size(
gfx::Size(work_area_bounds.width() * 0.4f, work_area_bounds.height()));
split_view_controller()->SnapWindow(
window1.get(), SplitViewController::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
window2.get(), SplitViewController::SnapPosition::kSecondary);
WMEvent snap_primary_two_third(WM_EVENT_SNAP_PRIMARY,
chromeos::kTwoThirdSnapRatio);
WindowState::Get(window1.get())->OnWMEvent(&snap_primary_two_third);
EXPECT_EQ(split_view_controller()->state(),
SplitViewController::State::kPrimarySnapped);
EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
wm::ActivateWindow(window2.get());
EXPECT_EQ(split_view_controller()->state(),
SplitViewController::State::kBothSnapped);
const int divider_delta = kSplitviewDividerShortSideLength / 2;
EXPECT_EQ(work_area_bounds.width() * 0.5f,
window1->bounds().width() + divider_delta);
EXPECT_EQ(work_area_bounds.width() * 0.5f,
window2->bounds().width() + divider_delta);
}
TEST_F(SplitViewControllerTest, AutoSnapPartialWindows) {
const gfx::Rect bounds(0, 0, 400, 400);
std::unique_ptr<aura::Window> window1(CreateWindow(bounds));
std::unique_ptr<aura::Window> window2(CreateWindow(bounds));
WMEvent snap_primary_two_third(WM_EVENT_SNAP_PRIMARY,
chromeos::kTwoThirdSnapRatio);
WindowState::Get(window1.get())->OnWMEvent(&snap_primary_two_third);
wm::ActivateWindow(window2.get());
gfx::Rect work_area_bounds =
screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
window1.get());
EXPECT_EQ(split_view_controller()->state(),
SplitViewController::State::kBothSnapped);
const int divider_delta = kSplitviewDividerShortSideLength / 2;
EXPECT_EQ(std::round(work_area_bounds.width() * chromeos::kTwoThirdSnapRatio),
window1->bounds().width() + divider_delta);
EXPECT_EQ(std::round(work_area_bounds.width() * chromeos::kOneThirdSnapRatio),
window2->bounds().width() + divider_delta);
EndSplitView();
aura::test::TestWindowDelegate* delegate2 =
static_cast<aura::test::TestWindowDelegate*>(window2->delegate());
delegate2->set_minimum_size(
gfx::Size(work_area_bounds.width() * 0.4f, work_area_bounds.height()));
WindowState::Get(window1.get())->OnWMEvent(&snap_primary_two_third);
wm::ActivateWindow(window2.get());
EXPECT_EQ(split_view_controller()->state(),
SplitViewController::State::kBothSnapped);
EXPECT_EQ(work_area_bounds.width() * chromeos::kDefaultSnapRatio,
window1->bounds().width() + divider_delta);
EXPECT_EQ(work_area_bounds.width() * chromeos::kDefaultSnapRatio,
window2->bounds().width() + divider_delta);
}
TEST_F(SplitViewControllerTest, StackingOrderWithDivider) {
std::unique_ptr<aura::Window> w1(CreateTestWindow());
std::unique_ptr<aura::Window> w2(CreateTestWindow());
SplitViewController* controller = split_view_controller();
controller->SnapWindow(w1.get(), SplitViewController::SnapPosition::kPrimary);
EXPECT_EQ(split_view_controller()->primary_window(), w1.get());
split_view_controller()->SnapWindow(
w2.get(), SplitViewController::SnapPosition::kSecondary);
EXPECT_EQ(controller->state(), SplitViewController::State::kBothSnapped);
SplitViewDivider* divider = split_view_divider();
ASSERT_TRUE(divider);
aura::Window* divider_widget_native_window =
divider->divider_widget()->GetNativeWindow();
EXPECT_TRUE(IsStackedBelow(w1.get(), divider_widget_native_window));
EXPECT_TRUE(IsStackedBelow(w2.get(), divider_widget_native_window));
controller->OnWindowDragStarted(w1.get());
EXPECT_TRUE(IsStackedBelow(divider_widget_native_window, w1.get()));
controller->OnWindowDragCanceled();
EXPECT_TRUE(IsStackedBelow(w1.get(), divider_widget_native_window));
EXPECT_TRUE(IsStackedBelow(w2.get(), divider_widget_native_window));
}
TEST_F(SplitViewControllerTest, SnapWindowsWithDifferentParentContainers) {
std::unique_ptr<aura::Window> always_on_top_window(CreateTestWindow());
always_on_top_window->SetProperty(aura::client::kZOrderingKey,
ui::ZOrderLevel::kFloatingWindow);
std::unique_ptr<aura::Window> normal_window(CreateTestWindow());
SplitViewController* controller = split_view_controller();
controller->SnapWindow(always_on_top_window.get(),
SplitViewController::SnapPosition::kPrimary);
controller->SnapWindow(normal_window.get(),
SplitViewController::SnapPosition::kSecondary);
EXPECT_EQ(controller->state(), SplitViewController::State::kBothSnapped);
SplitViewDivider* divider = split_view_divider();
ASSERT_TRUE(divider);
aura::Window* divider_widget_native_window =
divider->divider_widget()->GetNativeWindow();
EXPECT_EQ(divider_widget_native_window->parent(),
always_on_top_window->parent());
EXPECT_EQ(ui::ZOrderLevel::kFloatingWindow,
always_on_top_window->GetProperty(aura::client::kZOrderingKey));
EXPECT_EQ(ui::ZOrderLevel::kNormal,
normal_window->GetProperty(aura::client::kZOrderingKey));
wm::ActivateWindow(always_on_top_window.get());
EXPECT_EQ(controller->state(), SplitViewController::State::kBothSnapped);
EXPECT_EQ(ui::ZOrderLevel::kFloatingWindow,
always_on_top_window->GetProperty(aura::client::kZOrderingKey));
EXPECT_TRUE(
IsStackedBelow(always_on_top_window.get(), divider_widget_native_window));
wm::ActivateWindow(normal_window.get());
EXPECT_EQ(controller->state(), SplitViewController::State::kBothSnapped);
EXPECT_EQ(ui::ZOrderLevel::kFloatingWindow,
always_on_top_window->GetProperty(aura::client::kZOrderingKey));
EXPECT_TRUE(
IsStackedBelow(always_on_top_window.get(), divider_widget_native_window));
controller->OnWindowDragStarted(normal_window.get());
EXPECT_EQ(divider_widget_native_window->parent(), normal_window->parent());
EXPECT_TRUE(
IsStackedBelow(divider_widget_native_window, normal_window.get()));
controller->OnWindowDragCanceled();
EXPECT_EQ(divider_widget_native_window->parent(),
always_on_top_window->parent());
EXPECT_TRUE(
IsStackedBelow(always_on_top_window.get(), divider_widget_native_window));
}
TEST_F(SplitViewControllerTest, WMSnapEventDeviceOrientationMetricsInTablet) {
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(),
chromeos::OrientationType::kLandscapePrimary);
constexpr char kDeviceOrientationTablet[] =
"Ash.SplitView.DeviceOrientation.TabletMode";
constexpr char kDeviceOrientationEntryPoint[] =
"Ash.SplitView.EntryPoint.DeviceOrientation";
constexpr char kDeviceOrientationInSplitView[] =
"Ash.SplitView.OrientationInSplitView";
base::HistogramTester histogram_tester;
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_FALSE(split_view_controller()->InSplitViewMode());
WMEvent wm_left_snap_event(WM_EVENT_SNAP_PRIMARY);
WindowState::Get(window1.get())->OnWMEvent(&wm_left_snap_event);
EXPECT_TRUE(split_view_controller()->InSplitViewMode());
OverviewController* overview_controller = Shell::Get()->overview_controller();
EXPECT_TRUE(overview_controller->InOverviewSession());
histogram_tester.ExpectBucketCount(
kDeviceOrientationTablet,
SplitViewMetricsController::DeviceOrientation::kLandscape, 1);
histogram_tester.ExpectBucketCount(
kDeviceOrientationEntryPoint,
SplitViewMetricsController::DeviceOrientation::kLandscape, 1);
test_api.SetDisplayRotation(display::Display::ROTATE_270,
display::Display::RotationSource::ACTIVE);
ASSERT_EQ(test_api.GetCurrentOrientation(),
chromeos::OrientationType::kPortraitPrimary);
histogram_tester.ExpectBucketCount(
kDeviceOrientationTablet,
SplitViewMetricsController::DeviceOrientation::kPortrait, 1);
histogram_tester.ExpectBucketCount(
kDeviceOrientationInSplitView,
SplitViewMetricsController::DeviceOrientation::kPortrait, 1);
}
TEST_F(SplitViewControllerTest,
WMSnapEventDeviceOrientationMetricsInClamshell) {
UpdateDisplay("800x600/l");
base::HistogramTester histogram_tester;
constexpr char kDeviceOrientationClamshell[] =
"Ash.SplitView.DeviceOrientation.ClamshellMode";
constexpr char kDeviceOrientationEntryPoint[] =
"Ash.SplitView.EntryPoint.DeviceOrientation";
constexpr char kDeviceOrientationInSplitView[] =
"Ash.SplitView.OrientationInSplitView";
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(window1.get());
EXPECT_FALSE(split_view_controller()->InSplitViewMode());
const WMEvent wm_left_snap_event(WM_EVENT_SNAP_PRIMARY);
const WMEvent wm_right_snap_event(WM_EVENT_SNAP_SECONDARY);
const WMEvent fullscreen_event(WM_EVENT_TOGGLE_FULLSCREEN);
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false);
WindowState::Get(window1.get())->OnWMEvent(&wm_left_snap_event);
histogram_tester.ExpectBucketCount(
kDeviceOrientationClamshell,
SplitViewMetricsController::DeviceOrientation::kPortrait, 0);
wm::ActivateWindow(window2.get());
WindowState::Get(window2.get())->OnWMEvent(&wm_right_snap_event);
histogram_tester.ExpectBucketCount(
kDeviceOrientationClamshell,
SplitViewMetricsController::DeviceOrientation::kPortrait, 1);
histogram_tester.ExpectBucketCount(
kDeviceOrientationEntryPoint,
SplitViewMetricsController::DeviceOrientation::kPortrait, 1);
histogram_tester.ExpectBucketCount(
kDeviceOrientationClamshell,
SplitViewMetricsController::DeviceOrientation::kLandscape, 0);
histogram_tester.ExpectBucketCount(
kDeviceOrientationInSplitView,
SplitViewMetricsController::DeviceOrientation::kLandscape, 0);
UpdateDisplay("800x600");
histogram_tester.ExpectBucketCount(
kDeviceOrientationClamshell,
SplitViewMetricsController::DeviceOrientation::kLandscape, 1);
histogram_tester.ExpectBucketCount(
kDeviceOrientationInSplitView,
SplitViewMetricsController::DeviceOrientation::kLandscape, 1);
histogram_tester.ExpectBucketCount(
kDeviceOrientationEntryPoint,
SplitViewMetricsController::DeviceOrientation::kLandscape, 0);
WindowState::Get(window1.get())->OnWMEvent(&fullscreen_event);
WindowState::Get(window1.get())->OnWMEvent(&wm_left_snap_event);
histogram_tester.ExpectBucketCount(
kDeviceOrientationClamshell,
SplitViewMetricsController::DeviceOrientation::kLandscape, 2);
histogram_tester.ExpectBucketCount(
kDeviceOrientationInSplitView,
SplitViewMetricsController::DeviceOrientation::kLandscape, 1);
histogram_tester.ExpectBucketCount(
kDeviceOrientationEntryPoint,
SplitViewMetricsController::DeviceOrientation::kLandscape, 1);
}
class SplitViewKeyboardTest : public SplitViewControllerTest {
public:
SplitViewKeyboardTest() {
scoped_feature_list_.InitAndEnableFeature(features::kAdjustSplitViewForVK);
}
SplitViewKeyboardTest(const SplitViewKeyboardTest&) = delete;
SplitViewKeyboardTest& operator=(const SplitViewKeyboardTest&) = delete;
~SplitViewKeyboardTest() override = default;
void SetUp() override {
SplitViewControllerTest::SetUp();
SetVirtualKeyboardEnabled(true);
}
keyboard::KeyboardUIController* keyboard_controller() {
return keyboard::KeyboardUIController::Get();
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_F(SplitViewKeyboardTest, PushUpBottomWindow) {
UpdateDisplay("1200x800");
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(chromeos::OrientationType::kLandscapePrimary,
test_api.GetCurrentOrientation());
gfx::Rect bounds(0, 0, 400, 400);
std::unique_ptr<aura::Window> bottom_window(CreateWindow(bounds));
auto bottom_client =
std::make_unique<TestTextInputClient>(bottom_window.get());
split_view_controller()->SnapWindow(
bottom_window.get(), SplitViewController::SnapPosition::kSecondary);
test_api.SetDisplayRotation(display::Display::ROTATE_270,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(chromeos::OrientationType::kPortraitPrimary,
test_api.GetCurrentOrientation());
EXPECT_FALSE(split_view_controller()->IsPhysicalLeftOrTop(
SplitViewController::SnapPosition::kSecondary, bottom_window.get()));
const gfx::Rect keyboard_bounds =
keyboard_controller()->GetKeyboardWindow()->GetBoundsInScreen();
const gfx::Rect orig_bottom_bounds = bottom_window->GetBoundsInScreen();
const gfx::Rect orig_divider_bounds = split_view_controller()
->split_view_divider()
->divider_widget()
->GetWindowBoundsInScreen();
bottom_client->set_caret_bounds(gfx::Rect(
keyboard_bounds.top_center() +
gfx::Vector2d(0, -kMinCaretKeyboardDist - kCaretHeightForTest - 10),
gfx::Size(0, kCaretHeightForTest)));
bottom_client->Focus();
EXPECT_TRUE(keyboard_controller()->IsKeyboardVisible());
EXPECT_EQ(orig_bottom_bounds, bottom_window->GetBoundsInScreen());
EXPECT_EQ(orig_divider_bounds, split_view_controller()
->split_view_divider()
->divider_widget()
->GetWindowBoundsInScreen());
EXPECT_TRUE(split_view_controller()->split_view_divider()->IsAdjustable());
bottom_client->UnFocus();
EXPECT_FALSE(keyboard_controller()->IsKeyboardVisible());
const gfx::Rect shift_bottom_bounds(
keyboard_bounds.origin() + gfx::Vector2d(0, -orig_bottom_bounds.height()),
orig_bottom_bounds.size());
const gfx::Rect shift_divider_bounds(
shift_bottom_bounds.origin() +
gfx::Vector2d(0, -orig_divider_bounds.height()),
orig_divider_bounds.size());
bottom_client->set_caret_bounds(
gfx::Rect(keyboard_bounds.top_center() + gfx::Vector2d(0, 10),
gfx::Size(0, kCaretHeightForTest)));
bottom_client->Focus();
EXPECT_TRUE(keyboard_controller()->IsKeyboardVisible());
EXPECT_EQ(shift_bottom_bounds, bottom_window->GetBoundsInScreen());
EXPECT_EQ(shift_divider_bounds, split_view_controller()
->split_view_divider()
->divider_widget()
->GetWindowBoundsInScreen());
EXPECT_FALSE(split_view_controller()->split_view_divider()->IsAdjustable());
bottom_client->UnFocus();
EXPECT_FALSE(keyboard_controller()->IsKeyboardVisible());
EXPECT_EQ(orig_bottom_bounds, bottom_window->GetBoundsInScreen());
EXPECT_EQ(orig_divider_bounds, split_view_controller()
->split_view_divider()
->divider_widget()
->GetWindowBoundsInScreen());
EXPECT_TRUE(split_view_controller()->split_view_divider()->IsAdjustable());
}
TEST_F(SplitViewKeyboardTest, PushUpBottomWindowLimitHeight) {
UpdateDisplay("1200x800");
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(chromeos::OrientationType::kLandscapePrimary,
test_api.GetCurrentOrientation());
gfx::Rect bounds(0, 0, 200, 200);
std::unique_ptr<aura::Window> bottom_window(CreateWindow(bounds));
auto bottom_client =
std::make_unique<TestTextInputClient>(bottom_window.get());
split_view_controller()->SnapWindow(
bottom_window.get(), SplitViewController::SnapPosition::kSecondary);
test_api.SetDisplayRotation(display::Display::ROTATE_270,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(chromeos::OrientationType::kPortraitPrimary,
test_api.GetCurrentOrientation());
EXPECT_FALSE(split_view_controller()->IsPhysicalLeftOrTop(
SplitViewController::SnapPosition::kSecondary, bottom_window.get()));
const gfx::Rect keyboard_bounds =
keyboard_controller()->GetKeyboardWindow()->GetBoundsInScreen();
const gfx::Rect divider_bounds =
split_view_divider()->GetDividerBoundsInScreen(false );
const gfx::Rect screen_bounds =
screen_util::GetDisplayWorkAreaBoundsInParent(bottom_window.get());
const int screen_height = screen_bounds.height();
const int limit_y = screen_height * kMinDividerPositionRatio;
split_view_controller()->StartResizeWithDivider(divider_bounds.CenterPoint());
split_view_controller()->ResizeWithDivider(
gfx::Point(0, screen_height * 0.15f));
const gfx::Rect orig_bottom_bounds = bottom_window->GetBoundsInScreen();
EXPECT_LT(keyboard_bounds.y() - orig_bottom_bounds.height(), limit_y);
const gfx::Rect orig_divider_bounds = split_view_controller()
->split_view_divider()
->divider_widget()
->GetWindowBoundsInScreen();
const gfx::Rect shift_bottom_bounds(0, limit_y, keyboard_bounds.width(),
keyboard_bounds.y() - limit_y);
const gfx::Rect shift_divider_bounds(
shift_bottom_bounds.origin() +
gfx::Vector2d(0, -orig_divider_bounds.height()),
orig_divider_bounds.size());
bottom_client->set_caret_bounds(gfx::Rect(keyboard_bounds.top_center(),
gfx::Size(0, kCaretHeightForTest)));
bottom_client->Focus();
EXPECT_TRUE(keyboard_controller()->IsKeyboardVisible());
EXPECT_EQ(shift_bottom_bounds, bottom_window->GetBoundsInScreen());
EXPECT_EQ(shift_divider_bounds, split_view_controller()
->split_view_divider()
->divider_widget()
->GetWindowBoundsInScreen());
EXPECT_FALSE(split_view_controller()->split_view_divider()->IsAdjustable());
bottom_client->UnFocus();
EXPECT_FALSE(keyboard_controller()->IsKeyboardVisible());
EXPECT_EQ(orig_bottom_bounds, bottom_window->GetBoundsInScreen());
EXPECT_EQ(orig_divider_bounds, split_view_controller()
->split_view_divider()
->divider_widget()
->GetWindowBoundsInScreen());
EXPECT_TRUE(split_view_controller()->split_view_divider()->IsAdjustable());
}
TEST_F(SplitViewKeyboardTest, RestoreByActivatingTopWindow) {
UpdateDisplay("1200x800");
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(chromeos::OrientationType::kLandscapePrimary,
test_api.GetCurrentOrientation());
gfx::Rect bounds(0, 0, 400, 400);
std::unique_ptr<aura::Window> top_window(CreateWindow(bounds));
std::unique_ptr<aura::Window> bottom_window(CreateWindow(bounds));
auto top_client = std::make_unique<TestTextInputClient>(top_window.get());
auto bottom_client =
std::make_unique<TestTextInputClient>(bottom_window.get());
split_view_controller()->SnapWindow(
top_window.get(), SplitViewController::SnapPosition::kPrimary);
split_view_controller()->SnapWindow(
bottom_window.get(), SplitViewController::SnapPosition::kSecondary);
test_api.SetDisplayRotation(display::Display::ROTATE_270,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(chromeos::OrientationType::kPortraitPrimary,
test_api.GetCurrentOrientation());
EXPECT_TRUE(split_view_controller()->IsPhysicalLeftOrTop(
SplitViewController::SnapPosition::kPrimary, top_window.get()));
const gfx::Rect keyboard_bounds =
keyboard_controller()->GetKeyboardWindow()->GetBoundsInScreen();
const gfx::Rect orig_bottom_bounds = bottom_window->GetBoundsInScreen();
const gfx::Rect shift_bottom_bounds(
keyboard_bounds.origin() + gfx::Vector2d(0, -orig_bottom_bounds.height()),
orig_bottom_bounds.size());
const gfx::Rect orig_divider_bounds = split_view_controller()
->split_view_divider()
->divider_widget()
->GetWindowBoundsInScreen();
const gfx::Rect shift_divider_bounds(
shift_bottom_bounds.origin() +
gfx::Vector2d(0, -orig_divider_bounds.height()),
orig_divider_bounds.size());
bottom_client->set_caret_bounds(
gfx::Rect(keyboard_bounds.top_center() + gfx::Vector2d(0, 10),
gfx::Size(0, kCaretHeightForTest)));
bottom_client->Focus();
EXPECT_TRUE(keyboard_controller()->IsKeyboardVisible());
EXPECT_EQ(shift_bottom_bounds, bottom_window->GetBoundsInScreen());
EXPECT_EQ(shift_divider_bounds, split_view_controller()
->split_view_divider()
->divider_widget()
->GetWindowBoundsInScreen());
EXPECT_FALSE(split_view_controller()->split_view_divider()->IsAdjustable());
top_client->Focus();
EXPECT_TRUE(keyboard_controller()->IsKeyboardVisible());
EXPECT_EQ(orig_bottom_bounds, bottom_window->GetBoundsInScreen());
EXPECT_EQ(orig_divider_bounds, split_view_controller()
->split_view_divider()
->divider_widget()
->GetWindowBoundsInScreen());
EXPECT_TRUE(split_view_controller()->split_view_divider()->IsAdjustable());
}
TEST_F(SplitViewKeyboardTest, NoInputField) {
UpdateDisplay("1200x800");
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(chromeos::OrientationType::kLandscapePrimary,
test_api.GetCurrentOrientation());
gfx::Rect bounds(0, 0, 400, 400);
std::unique_ptr<aura::Window> bottom_window(CreateWindow(bounds));
split_view_controller()->SnapWindow(
bottom_window.get(), SplitViewController::SnapPosition::kSecondary);
test_api.SetDisplayRotation(display::Display::ROTATE_270,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(chromeos::OrientationType::kPortraitPrimary,
test_api.GetCurrentOrientation());
EXPECT_FALSE(split_view_controller()->IsPhysicalLeftOrTop(
SplitViewController::SnapPosition::kSecondary, bottom_window.get()));
const gfx::Rect orig_bottom_bounds = bottom_window->GetBoundsInScreen();
const gfx::Rect orig_divider_bounds = split_view_controller()
->split_view_divider()
->divider_widget()
->GetWindowBoundsInScreen();
keyboard_controller()->ShowKeyboard(false);
EXPECT_TRUE(keyboard_controller()->IsKeyboardVisible());
EXPECT_EQ(orig_bottom_bounds, bottom_window->GetBoundsInScreen());
EXPECT_EQ(orig_divider_bounds, split_view_controller()
->split_view_divider()
->divider_widget()
->GetWindowBoundsInScreen());
EXPECT_TRUE(split_view_controller()->split_view_divider()->IsAdjustable());
}
TEST_F(SplitViewKeyboardTest, ShowHideOnScreenKeyboardWithOverviewEnabled) {
UpdateDisplay("1200x800");
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());
std::unique_ptr<aura::Window> right_window(
CreateWindow(gfx::Rect(0, 0, 400, 400)));
for (auto rotation :
{display::Display::ROTATE_0, display::Display::ROTATE_270}) {
EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
test_api.SetDisplayRotation(rotation,
display::Display::RotationSource::ACTIVE);
const gfx::Rect origin_work_area =
screen_util::GetDisplayWorkAreaBoundsInParent(right_window.get());
keyboard_controller()->ShowKeyboard(true);
const gfx::Rect shrink_work_area =
screen_util::GetDisplayWorkAreaBoundsInParent(right_window.get());
const gfx::Rect keyboard_bounds =
keyboard_controller()->GetKeyboardWindow()->GetBoundsInScreen();
EXPECT_EQ(origin_work_area.height() - shrink_work_area.height(),
gfx::IntersectRects(keyboard_bounds, origin_work_area).height());
split_view_controller()->SnapWindow(
right_window.get(), SplitViewController::SnapPosition::kSecondary);
EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession());
EXPECT_EQ(right_window->bounds().bottom(), shrink_work_area.bottom());
keyboard_controller()->HideKeyboardByUser();
EXPECT_EQ(right_window->bounds().bottom(), origin_work_area.bottom());
EndSplitView();
ExitOverview();
}
}
}