#include "ash/wm/snap_group/snap_group.h"
#include <algorithm>
#include <memory>
#include <optional>
#include <sstream>
#include <vector>
#include "ash/accessibility/accessibility_controller.h"
#include "ash/accessibility/magnifier/docked_magnifier_controller.h"
#include "ash/accessibility/test_accessibility_controller_client.h"
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/display/window_tree_host_manager.h"
#include "ash/keyboard/ui/keyboard_ui_controller.h"
#include "ash/public/cpp/accelerators.h"
#include "ash/public/cpp/shelf_types.h"
#include "ash/public/cpp/test/shell_test_api.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/shelf/shelf.h"
#include "ash/shelf/shelf_layout_manager.h"
#include "ash/shell.h"
#include "ash/shell_delegate.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/close_button.h"
#include "ash/system/toast/toast_manager_impl.h"
#include "ash/test/ash_test_base.h"
#include "ash/test/ash_test_util.h"
#include "ash/test_shell_delegate.h"
#include "ash/wm/desks/desks_controller.h"
#include "ash/wm/desks/desks_test_api.h"
#include "ash/wm/desks/desks_test_util.h"
#include "ash/wm/desks/desks_util.h"
#include "ash/wm/desks/overview_desk_bar_view.h"
#include "ash/wm/desks/templates/saved_desk_save_desk_button.h"
#include "ash/wm/desks/templates/saved_desk_test_util.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_drop_target.h"
#include "ash/wm/overview/overview_focus_cycler.h"
#include "ash/wm/overview/overview_grid.h"
#include "ash/wm/overview/overview_grid_test_api.h"
#include "ash/wm/overview/overview_group_item.h"
#include "ash/wm/overview/overview_item.h"
#include "ash/wm/overview/overview_item_base.h"
#include "ash/wm/overview/overview_item_view.h"
#include "ash/wm/overview/overview_metrics.h"
#include "ash/wm/overview/overview_session.h"
#include "ash/wm/overview/overview_test_base.h"
#include "ash/wm/overview/overview_test_util.h"
#include "ash/wm/overview/overview_types.h"
#include "ash/wm/overview/overview_utils.h"
#include "ash/wm/overview/overview_window_drag_controller.h"
#include "ash/wm/overview/scoped_overview_transform_window.h"
#include "ash/wm/snap_group/snap_group_constants.h"
#include "ash/wm/snap_group/snap_group_controller.h"
#include "ash/wm/snap_group/snap_group_metrics.h"
#include "ash/wm/snap_group/snap_group_observer.h"
#include "ash/wm/snap_group/snap_group_test_util.h"
#include "ash/wm/splitview/layout_divider_controller.h"
#include "ash/wm/splitview/split_view_constants.h"
#include "ash/wm/splitview/split_view_controller.h"
#include "ash/wm/splitview/split_view_divider.h"
#include "ash/wm/splitview/split_view_divider_view.h"
#include "ash/wm/splitview/split_view_overview_session.h"
#include "ash/wm/splitview/split_view_setup_view.h"
#include "ash/wm/splitview/split_view_test_util.h"
#include "ash/wm/splitview/split_view_utils.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h"
#include "ash/wm/test/test_frame_view_ash.h"
#include "ash/wm/toplevel_window_event_handler.h"
#include "ash/wm/window_cycle/window_cycle_controller.h"
#include "ash/wm/window_cycle/window_cycle_item_view.h"
#include "ash/wm/window_cycle/window_cycle_list.h"
#include "ash/wm/window_cycle/window_cycle_view.h"
#include "ash/wm/window_mini_view.h"
#include "ash/wm/window_resizer.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_util.h"
#include "ash/wm/wm_constants.h"
#include "ash/wm/wm_event.h"
#include "ash/wm/wm_metrics.h"
#include "ash/wm/workspace/multi_window_resize_controller.h"
#include "ash/wm/workspace/phantom_window_controller.h"
#include "ash/wm/workspace/workspace_event_handler.h"
#include "ash/wm/workspace/workspace_window_resizer.h"
#include "ash/wm/workspace/workspace_window_resizer_test_api.h"
#include "ash/wm/workspace_controller_test_api.h"
#include "base/check_op.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/metrics/user_action_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "chromeos/constants/chromeos_features.h"
#include "chromeos/ui/base/display_util.h"
#include "chromeos/ui/base/window_properties.h"
#include "chromeos/ui/base/window_state_type.h"
#include "chromeos/ui/frame/caption_buttons/snap_controller.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/test/test_window_delegate.h"
#include "ui/aura/window.h"
#include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
#include "ui/base/hit_test.h"
#include "ui/base/mojom/ui_base_types.mojom-shared.h"
#include "ui/display/display_switches.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/test/display_manager_test_api.h"
#include "ui/events/event_constants.h"
#include "ui/events/keycodes/keyboard_codes_posix.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/point_conversions.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/geometry/vector2d.h"
#include "ui/gfx/geometry/vector2d_f.h"
#include "ui/gfx/scoped_animation_duration_scale_mode.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/controls/menu/menu_item_view.h"
#include "ui/views/test/test_widget_observer.h"
#include "ui/views/test/views_test_utils.h"
#include "ui/views/widget/widget.h"
#include "ui/views/window/dialog_delegate.h"
#include "ui/wm/core/coordinate_conversion.h"
#include "ui/wm/core/window_modality_controller.h"
#include "ui/wm/core/window_util.h"
#include "ui/wm/public/window_move_client.h"
namespace ash {
namespace {
#define TRACE_CALL(expression) \
do { \
SCOPED_TRACE(#expression); \
expression; \
} while (0)
using chromeos::WindowStateType;
using testing::ElementsAre;
using ui::mojom::CursorType;
using enum WindowSnapGrouping;
using WindowCyclingDirection = WindowCycleController::WindowCyclingDirection;
void SwitchToTabletMode() {
TabletModeControllerTestApi test_api;
test_api.DetachAllMice();
test_api.EnterTabletMode();
}
void ExitTabletMode() {
TabletModeControllerTestApi().LeaveTabletMode();
}
void MaximizeToClearTheSession(aura::Window* window) {
WindowState* window_state = WindowState::Get(window);
window_state->Maximize();
SplitViewOverviewSession* split_view_overview_session =
RootWindowController::ForWindow(window)->split_view_overview_session();
EXPECT_FALSE(split_view_overview_session);
}
void DragGroupItemToPoint(OverviewItemBase* item,
const gfx::Point& screen_location,
ui::test::EventGenerator* event_generator,
bool by_touch_gestures,
bool drop) {
DCHECK(item);
gfx::Point location =
gfx::ToRoundedPoint(item->target_bounds().CenterPoint());
location.Offset(5, 5);
event_generator->set_current_screen_location(location);
if (by_touch_gestures) {
event_generator->PressTouch();
event_generator->MoveTouchBy(50, 0);
event_generator->MoveTouch(screen_location);
if (drop) {
event_generator->ReleaseTouch();
}
} else {
event_generator->PressLeftButton();
Shell::Get()->cursor_manager()->SetDisplay(
display::Screen::Get()->GetDisplayNearestPoint(screen_location));
event_generator->MoveMouseTo(screen_location);
if (drop) {
event_generator->ReleaseLeftButton();
}
}
}
void ActivateWindowInOverviewGroupItem(
aura::Window* window,
ui::test::EventGenerator* event_generator,
bool by_touch_gestures) {
ASSERT_TRUE(IsInOverviewSession());
OverviewGroupItem* overview_group_item =
static_cast<OverviewGroupItem*>(GetOverviewItemForWindow(window));
ASSERT_TRUE(overview_group_item);
const auto& overview_items =
overview_group_item->overview_items_for_testing();
const std::unique_ptr<OverviewItem>& selected_item =
overview_items[0]->GetWindow() == window ? overview_items[0]
: overview_items[1];
ASSERT_EQ(window, selected_item->GetWindow());
event_generator->set_current_screen_location(
gfx::ToRoundedPoint(selected_item->target_bounds().CenterPoint()));
if (by_touch_gestures) {
event_generator->PressTouch();
event_generator->ReleaseTouch();
} else {
event_generator->ClickLeftButton();
}
}
gfx::Point GetDragPoint(aura::Window* window) {
auto* frame = FrameViewAsh::Get(window);
views::test::RunScheduledLayout(frame);
return frame->GetHeaderView()->GetBoundsInScreen().CenterPoint();
}
void VerifySnapGroupOnDisplay(SnapGroup* snap_group, const int64_t display_id) {
aura::Window* root = snap_group->window1()->GetRootWindow();
EXPECT_EQ(root, snap_group->window2()->GetRootWindow());
EXPECT_EQ(root, snap_group->snap_group_divider()->GetRootWindow());
EXPECT_EQ(display_id,
display::Screen::Get()->GetDisplayNearestWindow(root).id());
}
void ResizeDividerTo(ui::test::EventGenerator* event_generator,
gfx::Point resize_point) {
const gfx::Point divider_center(
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint());
event_generator->MoveMouseTo(divider_center);
event_generator->PressLeftButton();
event_generator->MoveMouseTo(resize_point, 2);
event_generator->ReleaseLeftButton();
}
void LongTapAt(ui::test::EventGenerator* event_generator,
const gfx::Point& point) {
ui::GestureEvent long_press(
point.x(), point.y(), 0, base::TimeTicks::Now(),
ui::GestureEventDetails(ui::EventType::kGestureLongPress));
event_generator->Dispatch(&long_press);
}
void DragWindowTo(ui::test::EventGenerator* event_generator,
aura::Window* window,
gfx::Point point,
bool release) {
wm::ActivateWindow(window);
event_generator->MoveMouseTo(GetDragPoint(window));
event_generator->PressLeftButton();
event_generator->MoveMouseTo(point);
ASSERT_TRUE(WindowState::Get(window)->is_dragged());
if (release) {
event_generator->ReleaseLeftButton();
}
}
std::pair<gfx::Rect, gfx::Rect> GetExpectedSnappedBounds(
const display::Display& display) {
const gfx::Rect work_area(display.work_area());
gfx::Rect primary_bounds, secondary_bounds;
if (IsLayoutHorizontal(display)) {
work_area.SplitVertically(primary_bounds, secondary_bounds);
} else {
work_area.SplitHorizontally(primary_bounds, secondary_bounds);
}
return std::make_pair(primary_bounds, secondary_bounds);
}
void ExpectWindowsSnappedSideBySide(WindowSnapGrouping grouping,
aura::Window* primary,
aura::Window* secondary) {
EXPECT_EQ(WindowState::Get(primary)->GetStateType(),
WindowStateType::kPrimarySnapped);
EXPECT_EQ(WindowState::Get(secondary)->GetStateType(),
WindowStateType::kSecondarySnapped);
TRACE_CALL(VerifyNotSplitViewOrOverviewSession(primary));
TRACE_CALL(VerifyNotSplitViewOrOverviewSession(secondary));
const bool grouped =
SnapGroupController::Get()->AreWindowsInSnapGroup(primary, secondary);
if (grouping == kGrouped) {
EXPECT_TRUE(grouped);
TRACE_CALL(UnionBoundsEqualToWorkAreaBounds(primary, secondary,
GetTopmostSnapGroupDivider()));
} else {
EXPECT_FALSE(grouped);
auto [primary_half, secondary_half] =
GetExpectedSnappedBounds(display::Screen::Get()->GetPrimaryDisplay());
EXPECT_EQ(primary_half, primary->GetBoundsInScreen());
EXPECT_EQ(secondary_half, secondary->GetBoundsInScreen());
}
}
void SnapWindowsSideBySide(WindowSnapGrouping grouping,
aura::Window* primary,
aura::Window* secondary) {
auto snap_source = (grouping == kGrouped)
? WindowSnapActionSource::kKeyboardShortcutToSnap
: WindowSnapActionSource::kAutoSnapInSplitView;
SnapOneTestWindow(primary, WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio, snap_source);
SnapOneTestWindow(secondary, WindowStateType::kSecondarySnapped,
chromeos::kDefaultSnapRatio, snap_source);
TRACE_CALL(ExpectWindowsSnappedSideBySide(grouping, primary, secondary));
}
class SnapGroupTestBase : public OverviewTestBase {
public:
template <typename... TaskEnvironmentTraits>
explicit SnapGroupTestBase(TaskEnvironmentTraits&&... traits)
: OverviewTestBase(std::forward<TaskEnvironmentTraits>(traits)...) {}
SnapGroupTestBase(const SnapGroupTestBase&) = delete;
SnapGroupTestBase& operator=(const SnapGroupTestBase&) = delete;
~SnapGroupTestBase() override = default;
std::unique_ptr<aura::Window> CreateAppWindowWithMinSize(gfx::Size min_size) {
std::unique_ptr<aura::Window> window =
CreateAppWindow(gfx::Rect(800, 600), chromeos::AppType::SYSTEM_APP,
kShellWindowId_Invalid, new TestWidgetDelegateAsh);
auto* custom_frame =
static_cast<TestFrameViewAsh*>(FrameViewAsh::Get(window.get()));
custom_frame->SetMinimumSize(min_size);
return window;
}
};
}
class FasterSplitScreenTest : public SnapGroupTestBase {
public:
FasterSplitScreenTest() = default;
FasterSplitScreenTest(const FasterSplitScreenTest&) = delete;
FasterSplitScreenTest& operator=(const FasterSplitScreenTest&) = delete;
~FasterSplitScreenTest() override = default;
void SetUp() override {
OverviewTestBase::SetUp();
WindowCycleList::SetDisableInitialDelayForTesting(true);
}
protected:
base::HistogramTester histogram_tester_;
};
TEST_F(FasterSplitScreenTest, OldPartialOverview) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
ToggleOverview();
ASSERT_TRUE(IsInOverviewSession());
auto* event_generator = GetEventGenerator();
DragGroupItemToPoint(GetOverviewItemForWindow(w1.get()), gfx::Point(0, 0),
event_generator, false,
true);
EXPECT_EQ(WindowStateType::kPrimarySnapped,
WindowState::Get(w1.get())->GetStateType());
VerifySplitViewOverviewSession(w1.get());
EXPECT_TRUE(GetSplitViewController()->InSplitViewMode());
ClickOverviewItem(event_generator, w2.get());
VerifyNotSplitViewOrOverviewSession(w1.get());
MaximizeToClearTheSession(w1.get());
ToggleOverview();
ASSERT_TRUE(IsInOverviewSession());
DragGroupItemToPoint(GetOverviewItemForWindow(w1.get()), gfx::Point(0, 0),
event_generator, false,
true);
VerifySplitViewOverviewSession(w1.get());
EXPECT_TRUE(GetSplitViewController()->InSplitViewMode());
DragGroupItemToPoint(GetOverviewItemForWindow(w2.get()),
GetWorkAreaBounds().top_right(), event_generator,
false,
true);
EXPECT_EQ(WindowStateType::kSecondarySnapped,
WindowState::Get(w2.get())->GetStateType());
VerifyNotSplitViewOrOverviewSession(w1.get());
MaximizeToClearTheSession(w1.get());
MaximizeToClearTheSession(w2.get());
SwitchToTabletMode();
EXPECT_FALSE(IsInOverviewSession());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
EXPECT_TRUE(IsInOverviewSession());
EXPECT_TRUE(GetSplitViewController()->InSplitViewMode());
}
TEST_F(FasterSplitScreenTest, DisableSnapWindowSuggestionsPref) {
PrefService* pref =
Shell::Get()->session_controller()->GetActivePrefService();
pref->SetBoolean(prefs::kSnapWindowSuggestions, false);
ASSERT_FALSE(pref->GetBoolean(prefs::kSnapWindowSuggestions));
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
VerifyNotSplitViewOrOverviewSession(w1.get());
pref->SetBoolean(prefs::kSnapWindowSuggestions, true);
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
VerifySplitViewOverviewSession(w1.get());
}
TEST_F(FasterSplitScreenTest, Basic) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
VerifySplitViewOverviewSession(w1.get());
auto* overview_controller = Shell::Get()->overview_controller();
EXPECT_TRUE(
overview_controller->overview_session()->IsWindowInOverview(w2.get()));
ClickOverviewItem(GetEventGenerator(), w2.get());
WaitForOverviewExitAnimation();
EXPECT_EQ(WindowStateType::kSecondarySnapped,
WindowState::Get(w2.get())->GetStateType());
EXPECT_FALSE(overview_controller->InOverviewSession());
EXPECT_FALSE(
RootWindowController::ForWindow(w1.get())->split_view_overview_session());
std::unique_ptr<aura::Window> w3(CreateAppWindow());
SnapOneTestWindow(w3.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
EXPECT_FALSE(overview_controller->InOverviewSession());
std::unique_ptr<aura::Window> w4(CreateAppWindow());
SnapOneTestWindow(w4.get(), WindowStateType::kSecondarySnapped,
chromeos::kDefaultSnapRatio);
EXPECT_FALSE(overview_controller->InOverviewSession());
EXPECT_EQ(WindowStateType::kSecondarySnapped,
WindowState::Get(w4.get())->GetStateType());
EXPECT_EQ(WindowStateType::kPrimarySnapped,
WindowState::Get(w1.get())->GetStateType());
EXPECT_EQ(WindowStateType::kSecondarySnapped,
WindowState::Get(w2.get())->GetStateType());
EXPECT_EQ(WindowStateType::kPrimarySnapped,
WindowState::Get(w3.get())->GetStateType());
ToggleOverview();
auto* overview_grid = GetOverviewGridForRoot(w1->GetRootWindow());
EXPECT_FALSE(overview_grid->no_windows_widget());
EXPECT_FALSE(overview_grid->split_view_setup_widget());
}
TEST_F(FasterSplitScreenTest, CloseSnappedWindowEndsSplitViewOverviewSession) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w1.get(),
WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
VerifySplitViewOverviewSession(w1.get());
w1.reset();
EXPECT_FALSE(OverviewController::Get()->InOverviewSession());
}
TEST_F(FasterSplitScreenTest, SnapActionSourceLimitations) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
struct {
WindowSnapActionSource snap_action_source;
bool should_show_partial_overview;
} kTestCases[]{
{WindowSnapActionSource::kSnapByWindowLayoutMenu,
true},
{WindowSnapActionSource::kDragWindowToEdgeToSnap,
true},
{WindowSnapActionSource::kLongPressCaptionButtonToSnap,
true},
{WindowSnapActionSource::kKeyboardShortcutToSnap,
false},
{WindowSnapActionSource::kSnapByWindowStateRestore,
false},
{WindowSnapActionSource::kSnapByFullRestoreOrDeskTemplateOrSavedDesk,
false},
};
for (const auto test_case : kTestCases) {
SnapOneTestWindow(w1.get(), WindowStateType::kSecondarySnapped,
chromeos::kDefaultSnapRatio,
test_case.snap_action_source);
EXPECT_EQ(test_case.should_show_partial_overview, IsInOverviewSession());
MaximizeToClearTheSession(w1.get());
}
}
TEST_F(FasterSplitScreenTest, CycleSnap) {
auto* snap_group_controller = SnapGroupController::Get();
std::unique_ptr<aura::Window> w1(CreateAppWindow());
auto* window_state = WindowState::Get(w1.get());
for (auto event_type :
{WM_EVENT_CYCLE_SNAP_PRIMARY, WM_EVENT_CYCLE_SNAP_SECONDARY}) {
auto state_type = event_type == WM_EVENT_CYCLE_SNAP_PRIMARY
? chromeos::WindowStateType::kPrimarySnapped
: chromeos::WindowStateType::kSecondarySnapped;
const WindowSnapWMEvent cycle_snap(event_type);
window_state->OnWMEvent(&cycle_snap);
auto* overview_controller = Shell::Get()->overview_controller();
EXPECT_FALSE(overview_controller->InOverviewSession());
EXPECT_EQ(window_state->GetStateType(), state_type);
EXPECT_FALSE(snap_group_controller->GetSnapGroupForGivenWindow(w1.get()));
window_state->OnWMEvent(&cycle_snap);
EXPECT_FALSE(overview_controller->InOverviewSession());
EXPECT_TRUE(window_state->IsNormalStateType());
EXPECT_FALSE(snap_group_controller->GetSnapGroupForGivenWindow(w1.get()));
}
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* window_state2 = WindowState::Get(w2.get());
for (auto event_type :
{WM_EVENT_CYCLE_SNAP_PRIMARY, WM_EVENT_CYCLE_SNAP_SECONDARY}) {
{
const WindowSnapWMEvent snap(event_type == WM_EVENT_CYCLE_SNAP_PRIMARY
? WM_EVENT_SNAP_SECONDARY
: WM_EVENT_SNAP_PRIMARY);
window_state2->OnWMEvent(&snap);
EXPECT_EQ(window_state2->GetStateType(),
event_type == WM_EVENT_CYCLE_SNAP_PRIMARY
? chromeos::WindowStateType::kSecondarySnapped
: chromeos::WindowStateType::kPrimarySnapped);
}
auto state_type = event_type == WM_EVENT_CYCLE_SNAP_PRIMARY
? chromeos::WindowStateType::kPrimarySnapped
: chromeos::WindowStateType::kSecondarySnapped;
const WindowSnapWMEvent cycle_snap(event_type);
window_state->OnWMEvent(&cycle_snap);
auto* overview_controller = Shell::Get()->overview_controller();
EXPECT_FALSE(overview_controller->InOverviewSession());
EXPECT_EQ(window_state->GetStateType(), state_type);
EXPECT_TRUE(snap_group_controller->GetSnapGroupForGivenWindow(w1.get()));
window_state->OnWMEvent(&cycle_snap);
EXPECT_FALSE(overview_controller->InOverviewSession());
EXPECT_EQ(window_state->GetStateType(), state_type);
EXPECT_FALSE(snap_group_controller->GetSnapGroupForGivenWindow(w1.get()));
window_state->OnWMEvent(&cycle_snap);
EXPECT_FALSE(overview_controller->InOverviewSession());
EXPECT_TRUE(window_state->IsNormalStateType());
EXPECT_FALSE(snap_group_controller->GetSnapGroupForGivenWindow(w1.get()));
}
}
TEST_F(FasterSplitScreenTest, EndSplitViewOverviewSession) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kSecondarySnapped,
chromeos::kDefaultSnapRatio);
VerifySplitViewOverviewSession(w1.get());
auto* event_generator = GetEventGenerator();
event_generator->set_current_screen_location(GetDragPoint(w1.get()));
event_generator->DragMouseBy(10, 10);
EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
VerifySplitViewOverviewSession(w1.get());
WMEvent minimize_event(WM_EVENT_MINIMIZE);
WindowState::Get(w1.get())->OnWMEvent(&minimize_event);
EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
VerifySplitViewOverviewSession(w1.get());
w1.reset();
EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
}
TEST_F(FasterSplitScreenTest, ResizeSplitViewOverviewAndWindow) {
UpdateDisplay("900x600");
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
VerifySplitViewOverviewSession(w1.get());
const gfx::Rect initial_bounds(w1->GetBoundsInScreen());
const gfx::Point start_point(w1->GetBoundsInScreen().right_center());
auto* generator = GetEventGenerator();
generator->set_current_screen_location(start_point);
const auto drag_point1 = gfx::Point(
GetWorkAreaBounds().width() * chromeos::kOneThirdSnapRatio - 10,
start_point.y());
generator->DragMouseTo(drag_point1);
gfx::Rect expected_window_bounds(initial_bounds);
expected_window_bounds.set_width(drag_point1.x());
EXPECT_EQ(expected_window_bounds, w1->GetBoundsInScreen());
VerifySplitViewOverviewSession(w1.get());
const auto drag_point2 = gfx::Point(
GetWorkAreaBounds().width() * chromeos::kTwoThirdSnapRatio + 10,
start_point.y());
generator->DragMouseTo(drag_point2);
expected_window_bounds.set_width(drag_point2.x());
EXPECT_EQ(expected_window_bounds, w1->GetBoundsInScreen());
VerifySplitViewOverviewSession(w1.get());
}
TEST_F(FasterSplitScreenTest, ResizeThenDragToSnap) {
std::unique_ptr<aura::Window> w2(CreateAppWindow());
std::unique_ptr<aura::Window> w1(CreateAppWindow());
auto* event_generator = GetEventGenerator();
event_generator->set_current_screen_location(GetDragPoint(w1.get()));
event_generator->DragMouseTo(0, 100);
WindowState* window_state = WindowState::Get(w1.get());
EXPECT_EQ(WindowStateType::kPrimarySnapped, window_state->GetStateType());
const gfx::Rect work_area(GetWorkAreaBounds());
const gfx::Rect snapped_bounds(0, 0, work_area.width() / 2,
work_area.height());
EXPECT_EQ(snapped_bounds, w1->GetBoundsInScreen());
event_generator->set_current_screen_location(snapped_bounds.right_center());
event_generator->DragMouseBy(100, 10);
EXPECT_EQ(WindowStateType::kPrimarySnapped, window_state->GetStateType());
EXPECT_NE(snapped_bounds, w1->GetBoundsInScreen());
event_generator->set_current_screen_location(GetDragPoint(w1.get()));
event_generator->DragMouseBy(10, 10);
EXPECT_FALSE(IsInOverviewSession());
EXPECT_EQ(WindowStateType::kNormal, window_state->GetStateType());
EXPECT_NE(snapped_bounds, w1->GetBoundsInScreen());
event_generator->set_current_screen_location(GetDragPoint(w1.get()));
event_generator->DragMouseTo(0, 100);
EXPECT_EQ(snapped_bounds, w1->GetBoundsInScreen());
event_generator->set_current_screen_location(snapped_bounds.right_center());
event_generator->DragMouseBy(-100, 10);
EXPECT_EQ(WindowStateType::kPrimarySnapped, window_state->GetStateType());
EXPECT_NE(snapped_bounds, w1->GetBoundsInScreen());
event_generator->set_current_screen_location(GetDragPoint(w2.get()));
event_generator->DragMouseTo(0, 100);
EXPECT_EQ(WindowStateType::kPrimarySnapped,
WindowState::Get(w2.get())->GetStateType());
EXPECT_EQ(snapped_bounds, w2->GetBoundsInScreen());
}
TEST_F(FasterSplitScreenTest, ResizeAndAutoSnap) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
const gfx::Rect initial_bounds(w1->GetBoundsInScreen());
ASSERT_TRUE(OverviewController::Get()->InOverviewSession());
auto* generator = GetEventGenerator();
generator->set_current_screen_location(
w1->GetBoundsInScreen().right_center());
const int drag_x = 100;
generator->DragMouseBy(drag_x, 0);
ASSERT_TRUE(OverviewController::Get()->InOverviewSession());
gfx::Rect expected_window_bounds(initial_bounds);
expected_window_bounds.set_width(initial_bounds.width() + drag_x);
EXPECT_EQ(expected_window_bounds, w1->GetBoundsInScreen());
gfx::Rect expected_autosnap_bounds(GetWorkAreaBounds());
expected_autosnap_bounds.Subtract(w1->GetBoundsInScreen());
EXPECT_EQ(expected_autosnap_bounds,
GetOverviewGridBounds(w1->GetRootWindow()));
std::unique_ptr<aura::Window> w3(CreateAppWindow());
EXPECT_EQ(WindowStateType::kSecondarySnapped,
WindowState::Get(w3.get())->GetStateType());
expected_autosnap_bounds.Subtract(
gfx::Rect(expected_window_bounds.top_right(),
gfx::Size(kSplitviewDividerShortSideLength / 2,
GetWorkAreaBounds().height())));
EXPECT_EQ(expected_autosnap_bounds, w3->GetBoundsInScreen());
}
TEST_F(FasterSplitScreenTest, SnappedWindowFocusTest) {
UpdateDisplay("800x600");
std::unique_ptr<aura::Window> w2(CreateAppWindow(gfx::Rect(200, 100)));
std::unique_ptr<aura::Window> w1(CreateAppWindow(gfx::Rect(100, 100)));
ASSERT_TRUE(wm::IsActiveWindow(w1.get()));
auto* event_generator = GetEventGenerator();
for (const bool skip_pairing : {true, false}) {
SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kSecondarySnapped,
chromeos::kDefaultSnapRatio);
VerifySplitViewOverviewSession(w1.get());
auto* w2_overview_item = GetOverviewItemForWindow(w2.get());
EXPECT_TRUE(w2_overview_item);
const auto w2_overview_item_bounds = w2_overview_item->target_bounds();
const gfx::Point click_point =
skip_pairing
? gfx::ToRoundedPoint(w2_overview_item_bounds.bottom_right()) +
gfx::Vector2d(20, 20)
: gfx::ToRoundedPoint(w2_overview_item_bounds.CenterPoint());
event_generator->MoveMouseTo(click_point);
event_generator->ClickLeftButton();
EXPECT_EQ(wm::IsActiveWindow(w1.get()), skip_pairing);
EXPECT_FALSE(IsInOverviewSession());
MaximizeToClearTheSession(w1.get());
}
}
TEST_F(FasterSplitScreenTest, DragToPartialOverview) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
ToggleOverview();
OverviewSession* overview_session =
OverviewController::Get()->overview_session();
ASSERT_TRUE(overview_session);
EXPECT_TRUE(overview_session->IsWindowInOverview(w1.get()));
EXPECT_TRUE(overview_session->IsWindowInOverview(w2.get()));
auto* event_generator = GetEventGenerator();
DragGroupItemToPoint(GetOverviewItemForWindow(w1.get()), gfx::Point(0, 0),
event_generator,
false, true);
EXPECT_EQ(WindowStateType::kPrimarySnapped,
WindowState::Get(w1.get())->GetStateType());
VerifySplitViewOverviewSession(w1.get());
EXPECT_TRUE(overview_session->IsWindowInOverview(w2.get()));
ClickOverviewItem(event_generator, w2.get());
EXPECT_EQ(WindowStateType::kSecondarySnapped,
WindowState::Get(w2.get())->GetStateType());
EXPECT_EQ(WindowStateType::kPrimarySnapped,
WindowState::Get(w1.get())->GetStateType());
EXPECT_FALSE(OverviewController::Get()->InOverviewSession());
}
TEST_F(FasterSplitScreenTest, SkipPairingInOverviewWhenActivatingTheEmptyArea) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
VerifySplitViewOverviewSession(w1.get());
ASSERT_EQ(1u, GetOverviewSession()->grid_list().size());
auto* w2_overview_item = GetOverviewItemForWindow(w2.get());
EXPECT_TRUE(w2_overview_item);
const gfx::Point outside_point =
gfx::ToRoundedPoint(
w2_overview_item->GetTransformedBounds().bottom_right()) +
gfx::Vector2d(20, 20);
auto* event_generator = GetEventGenerator();
event_generator->MoveMouseTo(outside_point);
event_generator->ClickLeftButton();
EXPECT_FALSE(IsInOverviewSession());
EXPECT_EQ(WindowState::Get(w1.get())->GetStateType(),
WindowStateType::kPrimarySnapped);
MaximizeToClearTheSession(w1.get());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
VerifySplitViewOverviewSession(w1.get());
event_generator->MoveTouch(outside_point);
event_generator->PressTouch();
event_generator->ReleaseTouch();
EXPECT_FALSE(IsInOverviewSession());
EXPECT_EQ(WindowState::Get(w1.get())->GetStateType(),
WindowStateType::kPrimarySnapped);
}
TEST_F(FasterSplitScreenTest, SkipPairingWhenActivatingTheSnappedWindow) {
UpdateDisplay("800x600");
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
aura::test::TestWindowDelegate delegate;
auto* event_generator = GetEventGenerator();
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
VerifySplitViewOverviewSession(w1.get());
event_generator->MoveMouseTo(w1->GetBoundsInScreen().CenterPoint());
EXPECT_TRUE(IsInOverviewSession());
MaximizeToClearTheSession(w1.get());
struct {
int window_component;
bool is_click_event;
} kTestCases[]{
{ HTCLIENT, true},
{ HTCAPTION, true},
{ HTCLIENT, false},
{ HTCAPTION, false},
};
for (const auto& test_case : kTestCases) {
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
VerifySplitViewOverviewSession(w1.get());
delegate.set_window_component(test_case.window_component);
if (test_case.is_click_event) {
event_generator->ClickLeftButton();
} else {
event_generator->PressTouch();
event_generator->ReleaseTouch();
}
EXPECT_FALSE(IsInOverviewSession());
MaximizeToClearTheSession(w1.get());
}
}
TEST_F(FasterSplitScreenTest, SkipPairingOnKeyEvent) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
VerifySplitViewOverviewSession(w1.get());
ASSERT_EQ(1u, GetOverviewSession()->grid_list().size());
PressAndReleaseKey(ui::VKEY_ESCAPE, ui::EF_NONE);
OverviewController* overview_controller = OverviewController::Get();
EXPECT_FALSE(overview_controller->InOverviewSession());
EXPECT_EQ(WindowState::Get(w1.get())->GetStateType(),
WindowStateType::kPrimarySnapped);
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
VerifySplitViewOverviewSession(w1.get());
PressAndReleaseKey(ui::VKEY_TAB, ui::EF_ALT_DOWN);
EXPECT_FALSE(overview_controller->InOverviewSession());
EXPECT_EQ(WindowState::Get(w1.get())->GetStateType(),
WindowStateType::kPrimarySnapped);
EXPECT_TRUE(Shell::Get()->window_cycle_controller()->IsCycling());
}
TEST_F(FasterSplitScreenTest, SkipPairingToast) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
VerifySplitViewOverviewSession(w1.get());
auto* overview_grid = GetOverviewGridForRoot(w1->GetRootWindow());
ASSERT_TRUE(overview_grid);
auto* split_view_setup_view = overview_grid->GetSplitViewSetupView();
ASSERT_TRUE(split_view_setup_view);
LeftClickOn(split_view_setup_view->GetViewByID(
SplitViewSetupView::kDismissButtonIDForTest));
EXPECT_FALSE(OverviewController::Get()->InOverviewSession());
}
TEST_F(FasterSplitScreenTest, DontStartPartialOverviewAfterSkippingPairing) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
VerifySplitViewOverviewSession(w1.get());
PressAndReleaseKey(ui::VKEY_ESCAPE, ui::EF_NONE);
OverviewController* overview_controller = OverviewController::Get();
EXPECT_FALSE(overview_controller->InOverviewSession());
SnapOneTestWindow(w2.get(), WindowStateType::kSecondarySnapped,
chromeos::kDefaultSnapRatio);
EXPECT_FALSE(overview_controller->InOverviewSession());
EXPECT_EQ(WindowState::Get(w1.get())->GetStateType(),
WindowStateType::kPrimarySnapped);
EXPECT_EQ(WindowState::Get(w2.get())->GetStateType(),
WindowStateType::kSecondarySnapped);
}
TEST_F(FasterSplitScreenTest, DontStartPartialOverviewAfterClosingWindow) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
VerifySplitViewOverviewSession(w1.get());
ClickOverviewItem(GetEventGenerator(), w2.get());
w2.reset();
std::unique_ptr<aura::Window> w3(CreateAppWindow());
SnapOneTestWindow(w3.get(), WindowStateType::kSecondarySnapped,
chromeos::kDefaultSnapRatio);
EXPECT_FALSE(OverviewController::Get()->InOverviewSession());
}
TEST_F(FasterSplitScreenTest, StartPartialOverviewForMinimizedWindow) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
VerifySplitViewOverviewSession(w1.get());
WindowState::Get(w1.get())->Minimize();
SnapOneTestWindow(w2.get(), WindowStateType::kSecondarySnapped,
chromeos::kDefaultSnapRatio);
VerifySplitViewOverviewSession(w2.get());
}
TEST_F(FasterSplitScreenTest,
DoNotShowCannotSnapToastWhenActivatingTheSnappedWindow) {
UpdateDisplay("800x600");
std::unique_ptr<aura::Window> w1(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kKeyboardShortcutToSnap);
ASSERT_TRUE(WindowState::Get(w1.get())->IsSnapped());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w2.get(), WindowStateType::kSecondarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kDragWindowToEdgeToSnap);
EXPECT_FALSE(IsInOverviewSession());
wm::ActivateWindow(w1.get());
EXPECT_FALSE(ToastManager::Get()->IsToastShown(kAppCannotSnapToastId));
}
TEST_F(FasterSplitScreenTest, DontStartPartialOverviewForFloatedWindow) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
VerifySplitViewOverviewSession(w1.get());
wm::ActivateWindow(w2.get());
SnapOneTestWindow(w2.get(), WindowStateType::kSecondarySnapped,
chromeos::kDefaultSnapRatio);
EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
std::unique_ptr<aura::Window> floated_window = CreateAppWindow();
PressAndReleaseKey(ui::VKEY_F, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN);
EXPECT_TRUE(WindowState::Get(floated_window.get())->IsFloated());
EXPECT_TRUE(
w2->GetBoundsInScreen().Contains(floated_window->GetBoundsInScreen()));
std::unique_ptr<aura::Window> w3(CreateAppWindow());
SnapOneTestWindow(w3.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession());
}
TEST_F(FasterSplitScreenTest, DontStartPartiOverviewIfThereIsOnlyOneWindow) {
UpdateDisplay("900x600, 901+0-900x600");
ASSERT_EQ(Shell::GetAllRootWindows().size(), 2u);
DesksController* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
ASSERT_EQ(2u, desks_controller->desks().size());
Desk* desk0 = desks_controller->GetDeskAtIndex(0);
Desk* desk1 = desks_controller->GetDeskAtIndex(1);
std::unique_ptr<aura::Window> w1(
CreateAppWindow(gfx::Rect(10, 20, 200, 100)));
std::unique_ptr<aura::Window> w2(
CreateAppWindow(gfx::Rect(100, 20, 200, 100)));
ASSERT_EQ(desks_util::GetDeskForContext(w1.get()), desk0);
ASSERT_EQ(desks_util::GetDeskForContext(w2.get()), desk0);
desks_controller->MoveWindowFromActiveDeskTo(
w2.get(), desk1, w2->GetRootWindow(),
DesksMoveWindowFromActiveDeskSource::kShortcut);
ASSERT_EQ(desks_util::GetDeskForContext(w2.get()), desk1);
std::unique_ptr<aura::Window> w3(
CreateAppWindow(gfx::Rect(1000, 20, 200, 100)));
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
EXPECT_FALSE(IsInOverviewSession());
}
TEST_F(FasterSplitScreenTest,
OppositeSnappedWindowOcclusionWithIntersectionsTest) {
UpdateDisplay("800x600");
std::unique_ptr<aura::Window> w2 = CreateAppWindow();
SnapOneTestWindow(w2.get(), WindowStateType::kSecondarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kKeyboardShortcutToSnap);
ASSERT_TRUE(w2->IsVisible());
std::unique_ptr<aura::Window> w3 =
CreateAppWindow(gfx::Rect(350, 200, 150, 200));
ASSERT_TRUE(w3->IsVisible());
EXPECT_TRUE(w3->GetBoundsInScreen().Intersects(w2->GetBoundsInScreen()));
std::unique_ptr<aura::Window> w1 = CreateAppWindow();
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
ASSERT_TRUE(w1->IsVisible());
VerifySplitViewOverviewSession(w1.get());
wm::ActivateWindow(w2.get());
SnapOneTestWindow(w2.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kKeyboardShortcutToSnap);
EXPECT_FALSE(IsInOverviewSession());
SnapOneTestWindow(w1.get(), WindowStateType::kSecondarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
EXPECT_FALSE(IsInOverviewSession());
}
TEST_F(FasterSplitScreenTest,
OppositeSnappedWindowOcclusionWithoutIntersectionsTest) {
UpdateDisplay("800x600");
std::unique_ptr<aura::Window> w2 = CreateAppWindow();
SnapOneTestWindow(w2.get(), WindowStateType::kSecondarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kKeyboardShortcutToSnap);
ASSERT_TRUE(w2->IsVisible());
std::unique_ptr<aura::Window> w3 =
CreateAppWindow(gfx::Rect(550, 45, 50, 50));
ASSERT_TRUE(w3->IsVisible());
EXPECT_TRUE(w2->GetBoundsInScreen().Contains(w3->GetBoundsInScreen()));
std::unique_ptr<aura::Window> w1 = CreateAppWindow();
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
ASSERT_TRUE(w1->IsVisible());
VerifySplitViewOverviewSession(w1.get());
wm::ActivateWindow(w2.get());
SnapOneTestWindow(w2.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kKeyboardShortcutToSnap);
EXPECT_FALSE(IsInOverviewSession());
SnapOneTestWindow(w1.get(), WindowStateType::kSecondarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
EXPECT_FALSE(IsInOverviewSession());
}
TEST_F(FasterSplitScreenTest, NoCrashOnDisplayRemoval) {
UpdateDisplay("800x600,1000x600");
display::test::DisplayManagerTestApi display_manager_test(display_manager());
std::unique_ptr<aura::Window> window1(
CreateTestWindowInShell({.bounds = {900, 0, 100, 100}}));
std::unique_ptr<aura::Window> window2(
CreateTestWindowInShell({.bounds = {1000, 0, 100, 100}}));
SnapOneTestWindow(window1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
ASSERT_EQ(
display_manager_test.GetSecondaryDisplay().id(),
display::Screen::Get()->GetDisplayNearestWindow(window1.get()).id());
const gfx::Rect work_area(
display_manager_test.GetSecondaryDisplay().work_area());
EXPECT_EQ(gfx::Rect(800, 0, work_area.width() / 2, work_area.height()),
window1->GetBoundsInScreen());
VerifySplitViewOverviewSession(window1.get());
UpdateDisplay("800x600");
base::RunLoop().RunUntilIdle();
}
TEST_F(FasterSplitScreenTest, SnapWindowWithMinimumSize) {
UpdateDisplay("800x600");
std::unique_ptr<aura::Window> w1(CreateAppWindow());
aura::test::TestWindowDelegate delegate;
std::unique_ptr<aura::Window> w2(
CreateTestWindowInShell({.delegate = &delegate}));
int min_width = 396;
delegate.set_minimum_size(gfx::Size(min_width, 0));
WindowState* window_state = WindowState::Get(w1.get());
const WindowSnapWMEvent snap_type(
WM_EVENT_SNAP_PRIMARY, chromeos::kTwoThirdSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
window_state->OnWMEvent(&snap_type);
ASSERT_TRUE(OverviewController::Get()->InOverviewSession());
auto* event_generator = GetEventGenerator();
event_generator->MoveMouseTo(
gfx::ToRoundedPoint(GetOverviewItemForWindow(w2.get())
->GetTransformedBounds()
.CenterPoint()));
event_generator->ClickLeftButton();
EXPECT_EQ(min_width, w2->GetBoundsInScreen().width());
MaximizeToClearTheSession(w2.get());
min_width = 450;
delegate.set_minimum_size(gfx::Size(min_width, 0));
const WindowSnapWMEvent snap_default(
WM_EVENT_SNAP_PRIMARY, chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
window_state->OnWMEvent(&snap_default);
ASSERT_TRUE(OverviewController::Get()->InOverviewSession());
event_generator->MoveMouseTo(
gfx::ToRoundedPoint(GetOverviewItemForWindow(w2.get())
->GetTransformedBounds()
.CenterPoint()));
event_generator->ClickLeftButton();
EXPECT_EQ(min_width, w2->GetBoundsInScreen().width());
}
TEST_F(FasterSplitScreenTest, OppositeSnappedWindowOnOtherDisplay) {
UpdateDisplay("800x600,801+0-800x600");
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
std::unique_ptr<aura::Window> w3(
CreateAppWindow(gfx::Rect(900, 0, 100, 100)));
std::unique_ptr<aura::Window> w4(
CreateAppWindow(gfx::Rect(1000, 0, 100, 100)));
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
display::Screen* screen = display::Screen::Get();
auto display_list = screen->GetAllDisplays();
ASSERT_EQ(display_list[0], screen->GetDisplayNearestWindow(w1.get()));
EXPECT_TRUE(IsInOverviewSession());
EXPECT_TRUE(
RootWindowController::ForWindow(w1.get())->split_view_overview_session());
ClickOverviewItem(GetEventGenerator(), w2.get());
EXPECT_EQ(display_list[0], screen->GetDisplayNearestWindow(w2.get()));
SnapOneTestWindow(w3.get(), WindowStateType::kSecondarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
ASSERT_EQ(display_list[1], screen->GetDisplayNearestWindow(w3.get()));
EXPECT_TRUE(IsInOverviewSession());
EXPECT_TRUE(
RootWindowController::ForWindow(w3.get())->split_view_overview_session());
}
TEST_F(FasterSplitScreenTest, WindowBoundsRefreshedOnDisplayChanges) {
UpdateDisplay("900x600");
std::unique_ptr<aura::Window> window1(CreateAppWindow());
std::unique_ptr<aura::Window> window2(CreateAppWindow());
SnapOneTestWindow(window1.get(), WindowStateType::kPrimarySnapped,
chromeos::kTwoThirdSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
VerifySplitViewOverviewSession(window1.get());
ASSERT_EQ(WindowState::Get(window1.get())->snap_ratio(),
chromeos::kTwoThirdSnapRatio);
const auto work_area_bounds_1 = GetWorkAreaBounds();
ASSERT_EQ(
window1->GetBoundsInScreen(),
gfx::Rect(0, 0, work_area_bounds_1.width() * chromeos::kTwoThirdSnapRatio,
work_area_bounds_1.height()));
UpdateDisplay("1200x600");
VerifySplitViewOverviewSession(window1.get());
EXPECT_EQ(WindowState::Get(window1.get())->snap_ratio(),
chromeos::kTwoThirdSnapRatio);
const auto work_area_bounds_2 = GetWorkAreaBounds();
EXPECT_EQ(
window1->GetBoundsInScreen(),
gfx::Rect(0, 0, work_area_bounds_2.width() * chromeos::kTwoThirdSnapRatio,
work_area_bounds_2.height()));
}
TEST_F(FasterSplitScreenTest, KeyboardAndWorkAreaBoundsChanges) {
std::unique_ptr<aura::Window> window1(CreateAppWindow());
std::unique_ptr<aura::Window> window2(CreateAppWindow());
SnapOneTestWindow(window1.get(), chromeos::WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
VerifySplitViewOverviewSession(window1.get());
SetVirtualKeyboardEnabled(true);
auto* keyboard_controller = keyboard::KeyboardUIController::Get();
keyboard_controller->ShowKeyboard(true);
VerifySplitViewOverviewSession(window1.get());
auto* root_window = window1->GetRootWindow();
EXPECT_EQ(chromeos::WindowStateType::kPrimarySnapped,
WindowState::Get(window1.get())->GetStateType());
auto* overview_grid = GetOverviewGridForRoot(root_window);
EXPECT_TRUE(
GetOverviewGridBounds(root_window)
.Contains(
overview_grid->GetSplitViewSetupView()->GetBoundsInScreen()));
keyboard_controller->HideKeyboardByUser();
VerifySplitViewOverviewSession(window1.get());
EXPECT_EQ(chromeos::WindowStateType::kPrimarySnapped,
WindowState::Get(window1.get())->GetStateType());
EXPECT_TRUE(
GetOverviewGridBounds(root_window)
.Contains(
overview_grid->GetSplitViewSetupView()->GetBoundsInScreen()));
auto* docked_magnifier_controller =
Shell::Get()->docked_magnifier_controller();
docked_magnifier_controller->SetEnabled(true);
EXPECT_FALSE(IsInOverviewSession());
}
TEST_F(FasterSplitScreenTest, NoCrashWhenDraggingTheSnappedWindow) {
std::unique_ptr<aura::Window> window1(CreateAppWindow());
std::unique_ptr<aura::Window> window2(CreateAppWindow());
SnapOneTestWindow(window1.get(), WindowStateType::kPrimarySnapped,
chromeos::kTwoThirdSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
VerifySplitViewOverviewSession(window1.get());
std::unique_ptr<WindowResizer> resizer(CreateWindowResizer(
window1.get(), gfx::PointF(), HTCAPTION, wm::WINDOW_MOVE_SOURCE_MOUSE));
resizer->Drag(gfx::PointF(500, 100), 0);
WindowState* window_state = WindowState::Get(window1.get());
EXPECT_TRUE(window_state->is_dragged());
resizer->CompleteDrag();
EXPECT_FALSE(window_state->IsSnapped());
}
TEST_F(FasterSplitScreenTest,
NoCrashWhenDraggingTheAutoSnappedWindowThatWasPreviouslyMinimized) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(
CreateAppWindow(gfx::Rect(100, 100, 100, 100)));
WindowState* w2_window_state = WindowState::Get(w2.get());
w2_window_state->Minimize();
ASSERT_TRUE(w2_window_state->IsMinimized());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
VerifySplitViewOverviewSession(w1.get());
auto* w2_overview_item = GetOverviewItemForWindow(w2.get());
auto* event_generator = GetEventGenerator();
event_generator->set_current_screen_location(
gfx::ToRoundedPoint(w2_overview_item->target_bounds().CenterPoint()));
event_generator->ClickLeftButton();
EXPECT_EQ(w2_window_state->GetStateType(),
WindowStateType::kSecondarySnapped);
std::unique_ptr<WindowResizer> resizer(CreateWindowResizer(
w2.get(), gfx::PointF(), HTCAPTION, wm::WINDOW_MOVE_SOURCE_MOUSE));
resizer->Drag(gfx::PointF(500, 100), 0);
EXPECT_TRUE(w2_window_state->is_dragged());
resizer->CompleteDrag();
EXPECT_FALSE(w2_window_state->IsSnapped());
}
TEST_F(FasterSplitScreenTest, EnterOverviewSnappingWindow) {
std::unique_ptr<aura::Window> window1(
CreateAppWindow(gfx::Rect(20, 20, 200, 100)));
std::unique_ptr<aura::Window> windo2(
CreateAppWindow(gfx::Rect(10, 10, 200, 100)));
OverviewController* overview_controller = OverviewController::Get();
overview_controller->StartOverview(OverviewStartAction::kOverviewButton);
ASSERT_TRUE(IsInOverviewSession());
auto* overview_item = GetOverviewItemForWindow(window1.get());
auto* event_generator = GetEventGenerator();
event_generator->MoveMouseTo(
gfx::ToRoundedPoint(overview_item->target_bounds().CenterPoint()));
event_generator->PressLeftButton();
event_generator->DragMouseTo(0, 0);
event_generator->ReleaseLeftButton();
EXPECT_TRUE(IsInOverviewSession());
}
TEST_F(FasterSplitScreenTest, ClamshellTabletTransitionOneSnappedWindow) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
VerifySplitViewOverviewSession(w1.get());
SwitchToTabletMode();
EXPECT_TRUE(GetSplitViewDivider()->divider_widget());
auto observed_windows = GetSplitViewDivider()->observed_windows();
EXPECT_EQ(1u, observed_windows.size());
EXPECT_EQ(w1.get(), observed_windows.front());
TabletModeControllerTestApi().LeaveTabletMode();
}
TEST_F(FasterSplitScreenTest, ClamshellTabletTransitionTwoSnappedWindows) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
ClickOverviewItem(GetEventGenerator(), w2.get());
EXPECT_FALSE(GetSplitViewDivider()->divider_widget());
SwitchToTabletMode();
EXPECT_TRUE(GetSplitViewDivider()->divider_widget());
auto observed_windows = GetSplitViewDivider()->observed_windows();
EXPECT_EQ(2u, observed_windows.size());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(), GetSplitViewDivider());
TabletModeControllerTestApi().LeaveTabletMode();
}
TEST_F(FasterSplitScreenTest,
NoOverlapAfterSnapRatioVariesToAccommodateForMinimumSize) {
UpdateDisplay("900x600");
std::unique_ptr<aura::Window> window1(CreateAppWindow());
std::unique_ptr<aura::Window> window2(
CreateAppWindowWithMinSize(gfx::Size(400, 200)));
SnapOneTestWindow(window2.get(), WindowStateType::kSecondarySnapped,
chromeos::kOneThirdSnapRatio);
VerifySplitViewOverviewSession(window2.get());
auto* item1 = GetOverviewItemForWindow(window1.get());
auto* event_generator = GetEventGenerator();
event_generator->MoveMouseTo(
gfx::ToRoundedPoint(item1->target_bounds().CenterPoint()));
event_generator->ClickLeftButton();
WaitForOverviewExitAnimation();
EXPECT_FALSE(OverviewController::Get()->InOverviewSession());
WindowState* window2_state = WindowState::Get(window2.get());
ASSERT_TRUE(window2_state->snap_ratio());
EXPECT_EQ(window2_state->GetStateType(), WindowStateType::kSecondarySnapped);
EXPECT_GT(window2_state->snap_ratio().value(), chromeos::kOneThirdSnapRatio);
WindowState* window1_state = WindowState::Get(window1.get());
ASSERT_TRUE(window1_state->snap_ratio());
EXPECT_EQ(window1_state->GetStateType(), WindowStateType::kPrimarySnapped);
EXPECT_LT(window1_state->snap_ratio().value(), chromeos::kTwoThirdSnapRatio);
EXPECT_TRUE(SnapGroupController::Get()->AreWindowsInSnapGroup(window1.get(),
window2.get()));
UnionBoundsEqualToWorkAreaBounds(window1.get(), window2.get(),
GetTopmostSnapGroupDivider());
}
TEST_F(FasterSplitScreenTest, NoCrashWhenDoubleTapAfterTransition) {
gfx::ScopedAnimationDurationScaleMode test_duration_mode(
gfx::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
std::unique_ptr<aura::Window> w1(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
SwitchToTabletMode();
EXPECT_TRUE(GetSplitViewDivider()->divider_widget());
const gfx::Point divider_center =
GetSplitViewDivider()
->GetDividerBoundsInScreen(false)
.CenterPoint();
GetEventGenerator()->GestureTapAt(divider_center);
GetEventGenerator()->GestureTapAt(divider_center);
}
TEST_F(FasterSplitScreenTest, BasicTabKeyNavigation) {
std::unique_ptr<aura::Window> window2(CreateAppWindow());
std::unique_ptr<aura::Window> window1(CreateAppWindow());
const WindowSnapWMEvent snap_event(
WM_EVENT_SNAP_PRIMARY, WindowSnapActionSource::kSnapByWindowLayoutMenu);
WindowState::Get(window1.get())->OnWMEvent(&snap_event);
ASSERT_TRUE(IsInOverviewSession());
OverviewFocusCycler* focus_cycler = GetOverviewSession()->focus_cycler();
SendKeyUntilOverviewItemIsFocused(ui::VKEY_TAB, GetEventGenerator());
const std::vector<std::unique_ptr<OverviewItemBase>>& overview_windows =
GetOverviewItemsForRoot(0);
EXPECT_EQ(overview_windows[0]->item_widget(),
focus_cycler->GetOverviewFocusedView()->GetWidget());
OverviewGrid* grid = GetOverviewSession()->grid_list()[0].get();
PressAndReleaseKey(ui::VKEY_TAB);
ASSERT_TRUE(IsInOverviewSession());
EXPECT_EQ(grid->GetSplitViewSetupView()->GetViewByID(
SplitViewSetupView::kDismissButtonIDForTest),
focus_cycler->GetOverviewFocusedView());
PressAndReleaseKey(ui::VKEY_TAB);
ASSERT_TRUE(IsInOverviewSession());
EXPECT_EQ(grid->GetSplitViewSetupView()->GetViewByID(
SplitViewSetupView::kSettingsButtonIDForTest),
focus_cycler->GetOverviewFocusedView());
auto* event_generator = GetEventGenerator();
event_generator->PressKeyAndModifierKeys(ui::VKEY_TAB, ui::EF_SHIFT_DOWN);
ASSERT_TRUE(IsInOverviewSession());
EXPECT_EQ(grid->GetSplitViewSetupView()->GetViewByID(
SplitViewSetupView::kDismissButtonIDForTest),
focus_cycler->GetOverviewFocusedView());
event_generator->PressKeyAndModifierKeys(ui::VKEY_TAB, ui::EF_SHIFT_DOWN);
ASSERT_TRUE(IsInOverviewSession());
EXPECT_EQ(overview_windows[0]->item_widget(),
focus_cycler->GetOverviewFocusedView()->GetWidget());
}
TEST_F(FasterSplitScreenTest, NoCrashOnToastDestroying) {
auto w1 = CreateAppWindow(gfx::Rect(100, 100));
auto w2 = CreateAppWindow(gfx::Rect(100, 100));
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kDragWindowToEdgeToSnap);
ASSERT_TRUE(IsInOverviewSession());
OverviewGrid* grid = GetOverviewSession()->grid_list()[0].get();
auto* split_view_setup_widget = grid->split_view_setup_widget();
ASSERT_TRUE(split_view_setup_widget);
SendKeyUntilOverviewItemIsFocused(ui::VKEY_TAB, GetEventGenerator());
PressAndReleaseKey(ui::VKEY_TAB);
OverviewFocusCycler* focus_cycler = GetOverviewSession()->focus_cycler();
EXPECT_EQ(grid->GetSplitViewSetupView()->GetViewByID(
SplitViewSetupView::kDismissButtonIDForTest),
focus_cycler->GetOverviewFocusedView());
SwitchToTabletMode();
ExitTabletMode();
PressAndReleaseKey(ui::VKEY_TAB);
}
TEST_F(FasterSplitScreenTest, TabbingChromevox) {
Shell::Get()->accessibility_controller()->spoken_feedback().SetEnabled(true);
std::unique_ptr<aura::Window> window2(CreateAppWindow());
std::unique_ptr<aura::Window> window1(CreateAppWindow());
const WindowSnapWMEvent snap_event(
WM_EVENT_SNAP_PRIMARY, WindowSnapActionSource::kSnapByWindowLayoutMenu);
enum class TestCase { kDismissButton, kSettingsButton };
for (auto test_case : {TestCase::kDismissButton}) {
WindowState::Get(window1.get())->OnWMEvent(&snap_event);
ASSERT_TRUE(OverviewController::Get()->InOverviewSession());
PressAndReleaseKey(ui::VKEY_RIGHT, ui::EF_COMMAND_DOWN);
const std::vector<std::unique_ptr<OverviewItemBase>>& overview_windows =
GetOverviewItemsForRoot(0);
OverviewGrid* grid = GetOverviewSession()->grid_list()[0].get();
OverviewFocusCycler* focus_cycler = GetOverviewSession()->focus_cycler();
EXPECT_EQ(overview_windows[0]->item_widget(),
focus_cycler->GetOverviewFocusedView()->GetWidget());
PressAndReleaseKey(ui::VKEY_RIGHT, ui::EF_COMMAND_DOWN);
EXPECT_EQ(grid->GetSplitViewSetupView()->GetViewByID(
SplitViewSetupView::kDismissButtonIDForTest),
focus_cycler->GetOverviewFocusedView());
PressAndReleaseKey(ui::VKEY_RIGHT, ui::EF_COMMAND_DOWN);
EXPECT_EQ(grid->GetSplitViewSetupView()->GetViewByID(
SplitViewSetupView::kSettingsButtonIDForTest),
focus_cycler->GetOverviewFocusedView());
switch (test_case) {
case TestCase::kDismissButton: {
PressAndReleaseKey(ui::VKEY_LEFT, ui::EF_COMMAND_DOWN);
EXPECT_EQ(grid->GetSplitViewSetupView()->GetViewByID(
SplitViewSetupView::kDismissButtonIDForTest),
focus_cycler->GetOverviewFocusedView());
PressAndReleaseKey(ui::VKEY_SPACE);
EXPECT_FALSE(IsInOverviewSession());
break;
}
case TestCase::kSettingsButton: {
PressAndReleaseKey(ui::VKEY_SPACE);
EXPECT_FALSE(IsInOverviewSession());
break;
}
}
}
}
TEST_F(FasterSplitScreenTest, AccessibilityFocusAnnotator) {
auto window1 = CreateAppWindow(gfx::Rect(100, 100));
auto window0 = CreateAppWindow(gfx::Rect(100, 100));
SnapOneTestWindow(window0.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kDragWindowToEdgeToSnap);
auto* focus_widget = views::Widget::GetWidgetForNativeWindow(
GetOverviewSession()->GetOverviewFocusWindow());
ASSERT_TRUE(focus_widget);
OverviewGrid* grid = GetOverviewSession()->grid_list()[0].get();
ASSERT_FALSE(grid->desks_widget());
auto* split_view_setup_widget = grid->split_view_setup_widget();
ASSERT_TRUE(split_view_setup_widget);
auto* item_widget1 = GetOverviewItemForWindow(window1.get())->item_widget();
CheckA11yOverrides("focus", focus_widget,
split_view_setup_widget,
item_widget1);
CheckA11yOverrides("item1", item_widget1, focus_widget,
split_view_setup_widget);
CheckA11yOverrides("splitview", split_view_setup_widget,
item_widget1,
focus_widget);
}
TEST_F(FasterSplitScreenTest, SnapUnresizableWindow) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
w1->SetProperty(aura::client::kResizeBehaviorKey,
aura::client::kResizeBehaviorNone);
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kDragWindowToEdgeToSnap);
VerifyNotSplitViewOrOverviewSession(w1.get());
}
TEST_F(FasterSplitScreenTest, SnapUnresizableCanSnapWindow) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
w1->SetProperty(aura::client::kResizeBehaviorKey,
aura::client::kResizeBehaviorNone);
w1->SetProperty(kUnresizableSnappedSizeKey, new gfx::Size(300, 0));
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kDragWindowToEdgeToSnap);
VerifySplitViewOverviewSession(w1.get());
}
TEST_F(FasterSplitScreenTest,
SplitViewOverviewSessionExitPointClamshellHistograms) {
const auto kWindowLayoutCompleteOnSessionExit =
BuildWindowLayoutCompleteOnSessionExitHistogram();
const auto kSplitViewOverviewSessionExitPoint =
BuildSplitViewOverviewExitPointHistogramName(
WindowSnapActionSource::kDragWindowToEdgeToSnap);
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
histogram_tester_.ExpectBucketCount(kWindowLayoutCompleteOnSessionExit,
true,
0);
histogram_tester_.ExpectBucketCount(kWindowLayoutCompleteOnSessionExit,
false,
0);
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kDragWindowToEdgeToSnap);
VerifySplitViewOverviewSession(w1.get());
auto* event_generator = GetEventGenerator();
ClickOverviewItem(event_generator, w2.get());
histogram_tester_.ExpectBucketCount(kWindowLayoutCompleteOnSessionExit,
true,
1);
histogram_tester_.ExpectBucketCount(kWindowLayoutCompleteOnSessionExit,
false,
0);
histogram_tester_.ExpectBucketCount(
kSplitViewOverviewSessionExitPoint,
SplitViewOverviewSessionExitPoint::kCompleteByActivating,
1);
MaximizeToClearTheSession(w1.get());
MaximizeToClearTheSession(w2.get());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kDragWindowToEdgeToSnap);
VerifySplitViewOverviewSession(w1.get());
auto* item2 = GetOverviewItemForWindow(w2.get());
gfx::Point outside_point =
gfx::ToRoundedPoint(item2->target_bounds().bottom_right());
outside_point.Offset(5, 5);
event_generator->MoveMouseTo(outside_point);
event_generator->ClickLeftButton();
histogram_tester_.ExpectBucketCount(kWindowLayoutCompleteOnSessionExit,
true,
1);
histogram_tester_.ExpectBucketCount(kWindowLayoutCompleteOnSessionExit,
false,
1);
histogram_tester_.ExpectBucketCount(kSplitViewOverviewSessionExitPoint,
SplitViewOverviewSessionExitPoint::kSkip,
1);
MaximizeToClearTheSession(w1.get());
MaximizeToClearTheSession(w2.get());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kDragWindowToEdgeToSnap);
VerifySplitViewOverviewSession(w1.get());
std::unique_ptr<aura::Window> w3(CreateAppWindow());
histogram_tester_.ExpectBucketCount(kWindowLayoutCompleteOnSessionExit,
true,
2);
histogram_tester_.ExpectBucketCount(kWindowLayoutCompleteOnSessionExit,
false,
1);
histogram_tester_.ExpectBucketCount(
kSplitViewOverviewSessionExitPoint,
SplitViewOverviewSessionExitPoint::kCompleteByActivating,
2);
MaximizeToClearTheSession(w1.get());
MaximizeToClearTheSession(w3.get());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kDragWindowToEdgeToSnap);
VerifySplitViewOverviewSession(w1.get());
event_generator->PressAndReleaseKey(ui::VKEY_ESCAPE);
histogram_tester_.ExpectBucketCount(kWindowLayoutCompleteOnSessionExit,
true,
2);
histogram_tester_.ExpectBucketCount(kWindowLayoutCompleteOnSessionExit,
false,
2);
histogram_tester_.ExpectBucketCount(kSplitViewOverviewSessionExitPoint,
SplitViewOverviewSessionExitPoint::kSkip,
2);
MaximizeToClearTheSession(w1.get());
MaximizeToClearTheSession(w2.get());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kDragWindowToEdgeToSnap);
VerifySplitViewOverviewSession(w1.get());
w1.reset();
histogram_tester_.ExpectBucketCount(kWindowLayoutCompleteOnSessionExit,
true,
2);
histogram_tester_.ExpectBucketCount(kWindowLayoutCompleteOnSessionExit,
false,
2);
histogram_tester_.ExpectBucketCount(
kSplitViewOverviewSessionExitPoint,
SplitViewOverviewSessionExitPoint::kWindowDestroy,
1);
}
TEST_F(FasterSplitScreenTest, KeyMetricsIntegrationTest_DragToSnap) {
UpdateDisplay("800x600");
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
const auto kSplitViewOverviewSessionExitPoint =
BuildSplitViewOverviewExitPointHistogramName(
WindowSnapActionSource::kDragWindowToEdgeToSnap);
histogram_tester_.ExpectBucketCount(
kSplitViewOverviewSessionExitPoint,
SplitViewOverviewSessionExitPoint::kCompleteByActivating,
0);
std::unique_ptr<WindowResizer> resizer(CreateWindowResizer(
w1.get(), gfx::PointF(), HTCAPTION, wm::WINDOW_MOVE_SOURCE_MOUSE));
resizer->Drag(gfx::PointF(0, 400), 0);
resizer->CompleteDrag();
resizer.reset();
VerifySplitViewOverviewSession(w1.get());
EXPECT_EQ(
GetSplitViewOverviewSession(w1.get())->snap_action_source_for_testing(),
WindowSnapActionSource::kDragWindowToEdgeToSnap);
auto* event_generator = GetEventGenerator();
ClickOverviewItem(event_generator, w2.get());
histogram_tester_.ExpectBucketCount(
kSplitViewOverviewSessionExitPoint,
SplitViewOverviewSessionExitPoint::kCompleteByActivating,
1);
MaximizeToClearTheSession(w1.get());
MaximizeToClearTheSession(w2.get());
resizer = CreateWindowResizer(w1.get(), gfx::PointF(), HTCAPTION,
wm::WINDOW_MOVE_SOURCE_MOUSE);
resizer->Drag(gfx::PointF(800, 0), 0);
resizer->CompleteDrag();
resizer.reset();
VerifySplitViewOverviewSession(w1.get());
EXPECT_EQ(
GetSplitViewOverviewSession(w1.get())->snap_action_source_for_testing(),
WindowSnapActionSource::kDragWindowToEdgeToSnap);
auto* item2 = GetOverviewItemForWindow(w2.get());
gfx::Point outside_point =
gfx::ToRoundedPoint(item2->target_bounds().bottom_right());
outside_point.Offset(5, 5);
event_generator->MoveMouseTo(outside_point);
event_generator->ClickLeftButton();
histogram_tester_.ExpectBucketCount(kSplitViewOverviewSessionExitPoint,
SplitViewOverviewSessionExitPoint::kSkip,
1);
MaximizeToClearTheSession(w1.get());
}
TEST_F(FasterSplitScreenTest, KeyMetricsIntegrationTest_WindowSizeButton) {
UpdateDisplay("800x600");
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
struct SnapRequestWithActionSource {
chromeos::SnapController::SnapRequestSource request_source;
WindowSnapActionSource snap_action_source;
} kTestCases[]{
{chromeos::SnapController::SnapRequestSource::kWindowLayoutMenu,
WindowSnapActionSource::kSnapByWindowLayoutMenu},
{chromeos::SnapController::SnapRequestSource::kSnapButton,
WindowSnapActionSource::kLongPressCaptionButtonToSnap},
};
for (const auto test_case : kTestCases) {
const auto kSplitViewOverviewSessionExitPoint =
BuildSplitViewOverviewExitPointHistogramName(
test_case.snap_action_source);
histogram_tester_.ExpectBucketCount(
kSplitViewOverviewSessionExitPoint,
SplitViewOverviewSessionExitPoint::kCompleteByActivating,
0);
auto commit_snap = [&]() {
chromeos::SnapController::Get()->CommitSnap(
w1.get(), chromeos::SnapDirection::kSecondary,
chromeos::kDefaultSnapRatio, test_case.request_source);
VerifySplitViewOverviewSession(w1.get());
EXPECT_EQ(GetSplitViewOverviewSession(w1.get())
->snap_action_source_for_testing(),
test_case.snap_action_source);
};
commit_snap();
auto* event_generator = GetEventGenerator();
ClickOverviewItem(GetEventGenerator(), w2.get());
histogram_tester_.ExpectBucketCount(
kSplitViewOverviewSessionExitPoint,
SplitViewOverviewSessionExitPoint::kCompleteByActivating,
1);
MaximizeToClearTheSession(w1.get());
MaximizeToClearTheSession(w2.get());
commit_snap();
auto* item2 = GetOverviewItemForWindow(w2.get());
gfx::Point outside_point =
gfx::ToRoundedPoint(item2->target_bounds().bottom_right());
outside_point.Offset(5, 5);
event_generator->MoveMouseTo(outside_point);
event_generator->ClickLeftButton();
histogram_tester_.ExpectBucketCount(
kSplitViewOverviewSessionExitPoint,
SplitViewOverviewSessionExitPoint::kSkip,
1);
MaximizeToClearTheSession(w1.get());
}
}
TEST_F(FasterSplitScreenTest, OverviewStartActionHistogramTest) {
constexpr char kOverviewStartActionHistogram[] = "Ash.Overview.StartAction";
histogram_tester_.ExpectBucketCount(
kOverviewStartActionHistogram,
OverviewStartAction::kFasterSplitScreenSetup,
0);
std::unique_ptr<aura::Window> window1(CreateAppWindow());
std::unique_ptr<aura::Window> window2(CreateAppWindow());
SnapOneTestWindow(window1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
VerifySplitViewOverviewSession(window1.get());
histogram_tester_.ExpectBucketCount(
kOverviewStartActionHistogram,
OverviewStartAction::kFasterSplitScreenSetup,
1);
}
TEST_F(FasterSplitScreenTest, A11yAlertOnEnteringFaterSplitScreenSetup) {
TestAccessibilityControllerClient client;
std::unique_ptr<aura::Window> window1(CreateAppWindow());
std::unique_ptr<aura::Window> window2(CreateAppWindow());
EXPECT_NE(AccessibilityAlert::FASTER_SPLIT_SCREEN_SETUP,
client.last_a11y_alert());
SnapOneTestWindow(window1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
EXPECT_EQ(AccessibilityAlert::FASTER_SPLIT_SCREEN_SETUP,
client.last_a11y_alert());
}
TEST_F(FasterSplitScreenTest, NoCrashWhenDraggingSnappedWindowToEdge) {
std::unique_ptr<aura::Window> window1(
CreateAppWindow(gfx::Rect(0, 0, 200, 100)));
std::unique_ptr<aura::Window> window2(
CreateAppWindow(gfx::Rect(100, 100, 200, 100)));
SnapOneTestWindow(window1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
WaitForOverviewEntered();
VerifySplitViewOverviewSession(window1.get());
auto* event_generator = GetEventGenerator();
event_generator->set_current_screen_location(
window1.get()->GetBoundsInScreen().right_center());
gfx::Point drag_end_point = GetWorkAreaBounds().right_center();
drag_end_point.Offset(-10, 0);
event_generator->PressLeftButton();
event_generator->MoveMouseTo(drag_end_point);
auto* overview_item2 = GetOverviewItemForWindow(window2.get());
const auto shadow_content_bounds =
overview_item2->get_shadow_content_bounds_for_testing();
EXPECT_FALSE(shadow_content_bounds.IsEmpty());
VerifySplitViewOverviewSession(window1.get());
EXPECT_TRUE(WindowState::Get(window1.get())->is_dragged());
}
TEST_F(FasterSplitScreenTest, RecordWindowIndexAndCount) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
ClickOverviewItem(GetEventGenerator(), w2.get());
histogram_tester_.ExpectBucketCount(kPartialOverviewSelectedWindowIndex,
0,
1);
histogram_tester_.ExpectBucketCount(kPartialOverviewWindowListSize,
1, 1);
MaximizeToClearTheSession(w2.get());
std::unique_ptr<aura::Window> w3(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
ClickOverviewItem(GetEventGenerator(), w2.get());
histogram_tester_.ExpectBucketCount(kPartialOverviewSelectedWindowIndex,
1,
1);
histogram_tester_.ExpectBucketCount(kPartialOverviewWindowListSize,
2, 1);
MaximizeToClearTheSession(w2.get());
std::unique_ptr<aura::Window> w4(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
ClickOverviewItem(GetEventGenerator(), w3.get());
histogram_tester_.ExpectBucketCount(kPartialOverviewSelectedWindowIndex,
2,
1);
histogram_tester_.ExpectBucketCount(kPartialOverviewWindowListSize,
3, 1);
}
class SnapGroupTest : public SnapGroupTestBase {
public:
template <typename... TaskEnvironmentTraits>
explicit SnapGroupTest(TaskEnvironmentTraits&&... traits)
: SnapGroupTestBase(std::forward<TaskEnvironmentTraits>(traits)...) {
scoped_feature_list_.InitWithFeatures(
{features::kSameAppWindowCycle},
{});
}
SnapGroupTest(const SnapGroupTest&) = delete;
SnapGroupTest& operator=(const SnapGroupTest&) = delete;
~SnapGroupTest() override = default;
void SetUp() override {
AshTestBase::SetUp();
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kUseFirstDisplayAsInternal);
}
void CompleteWindowCycling() {
WindowCycleController* window_cycle_controller =
Shell::Get()->window_cycle_controller();
window_cycle_controller->CompleteCycling();
EXPECT_FALSE(window_cycle_controller->IsCycling());
}
void CycleWindow(WindowCyclingDirection direction, int steps) {
WindowCycleController* window_cycle_controller =
Shell::Get()->window_cycle_controller();
for (int i = 0; i < steps; i++) {
window_cycle_controller->HandleCycleWindow(direction);
EXPECT_TRUE(window_cycle_controller->IsCycling());
}
}
std::unique_ptr<aura::Window> CreateTestWindowWithAppID(
std::string app_id_key) {
std::unique_ptr<aura::Window> window = CreateAppWindow();
window->SetProperty(kAppIDKey, std::move(app_id_key));
return window;
}
std::unique_ptr<aura::Window> CreateTransientChildWindow(
aura::Window* transient_parent,
gfx::Rect child_window_bounds) {
auto child = CreateAppWindow(child_window_bounds);
wm::AddTransientChild(transient_parent, child.get());
return child;
}
std::unique_ptr<aura::Window> CreateAlwaysOnTopWindow() {
std::unique_ptr<aura::Window> always_on_top_window(CreateAppWindow());
always_on_top_window->SetProperty(aura::client::kZOrderingKey,
ui::ZOrderLevel::kFloatingWindow);
EXPECT_EQ(kShellWindowId_AlwaysOnTopContainer,
always_on_top_window->parent()->GetId());
return always_on_top_window;
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_F(SnapGroupTest, DisableSnapWindowSuggestionsPref) {
PrefService* pref =
Shell::Get()->session_controller()->GetActivePrefService();
pref->SetBoolean(prefs::kSnapWindowSuggestions, false);
ASSERT_FALSE(pref->GetBoolean(prefs::kSnapWindowSuggestions));
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
VerifyNotSplitViewOrOverviewSession(w1.get());
pref->SetBoolean(prefs::kSnapWindowSuggestions, true);
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
VerifySplitViewOverviewSession(w1.get());
ClickOverviewItem(GetEventGenerator(), w2.get());
EXPECT_TRUE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w1.get(), w2.get()));
}
TEST_F(SnapGroupTest, AddAndRemoveSnapGroupTest) {
auto* snap_group_controller = SnapGroupController::Get();
const auto& snap_groups = snap_group_controller->snap_groups_for_testing();
const auto& window_to_snap_group_map =
snap_group_controller->window_to_snap_group_map_for_testing();
EXPECT_EQ(snap_groups.size(), 0u);
EXPECT_EQ(window_to_snap_group_map.size(), 0u);
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
std::unique_ptr<aura::Window> w3(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_FALSE(snap_group_controller->AddSnapGroup(
w1.get(), w3.get(), false,
std::nullopt));
EXPECT_EQ(snap_groups.size(), 1u);
EXPECT_EQ(window_to_snap_group_map.size(), 2u);
const auto iter1 = window_to_snap_group_map.find(w1.get());
ASSERT_TRUE(iter1 != window_to_snap_group_map.end());
const auto iter2 = window_to_snap_group_map.find(w2.get());
ASSERT_TRUE(iter2 != window_to_snap_group_map.end());
auto* snap_group = snap_groups.back().get();
EXPECT_EQ(iter1->second, snap_group);
EXPECT_EQ(iter2->second, snap_group);
ASSERT_TRUE(snap_group_controller->RemoveSnapGroup(
snap_group, SnapGroupExitPoint::kDragWindowOut));
ASSERT_TRUE(snap_groups.empty());
ASSERT_TRUE(window_to_snap_group_map.empty());
}
TEST_F(SnapGroupTest, NoGapAfterSnapGroupCreationInLandscape) {
gfx::ScopedAnimationDurationScaleMode animation_scale(
gfx::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
UpdateDisplay("1366x768");
const gfx::Size window_minimum_size = gfx::Size(500, 0);
aura::test::TestWindowDelegate delegate1;
std::unique_ptr<aura::Window> w1(
CreateTestWindowInShell({.delegate = &delegate1, .bounds = {800, 600}}));
delegate1.set_minimum_size(window_minimum_size);
w1->SetProperty(chromeos::kAppTypeKey, chromeos::AppType::CHROME_APP);
aura::test::TestWindowDelegate delegate2;
std::unique_ptr<aura::Window> w2(CreateTestWindowInShell(
{.delegate = &delegate2, .bounds = {500, 0, 800, 600}}));
delegate2.set_minimum_size(window_minimum_size);
w2->SetProperty(chromeos::kAppTypeKey, chromeos::AppType::CHROME_APP);
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kTwoThirdSnapRatio);
WaitForOverviewEntered();
VerifySplitViewOverviewSession(w1.get());
ClickOverviewItem(GetEventGenerator(), w2.get());
EXPECT_EQ(chromeos::WindowStateType::kSecondarySnapped,
WindowState::Get(w2.get())->GetStateType());
WaitForOverviewExitAnimation();
EXPECT_TRUE(GetTopmostSnapGroupDivider());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
}
TEST_F(SnapGroupTest, NoGapAfterSnapGroupCreationInPortrait) {
gfx::ScopedAnimationDurationScaleMode animation_scale(
gfx::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
UpdateDisplay("768x1366");
const gfx::Size window_minimum_size = gfx::Size(0, 500);
aura::test::TestWindowDelegate delegate1;
std::unique_ptr<aura::Window> w1(
CreateTestWindowInShell({.delegate = &delegate1, .bounds = {800, 600}}));
w1->SetProperty(chromeos::kAppTypeKey, chromeos::AppType::CHROME_APP);
delegate1.set_minimum_size(window_minimum_size);
aura::test::TestWindowDelegate delegate2;
std::unique_ptr<aura::Window> w2(CreateTestWindowInShell(
{.delegate = &delegate2, .bounds = {500, 0, 800, 600}}));
delegate2.set_minimum_size(window_minimum_size);
w2->SetProperty(chromeos::kAppTypeKey, chromeos::AppType::CHROME_APP);
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kTwoThirdSnapRatio);
WaitForOverviewEntered();
VerifySplitViewOverviewSession(w1.get());
ClickOverviewItem(GetEventGenerator(), w2.get());
EXPECT_EQ(chromeos::WindowStateType::kSecondarySnapped,
WindowState::Get(w2.get())->GetStateType());
WaitForOverviewExitAnimation();
EXPECT_TRUE(GetTopmostSnapGroupDivider());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
}
TEST_F(SnapGroupTest, DisallowFormSnapGroupWithAlwaysOnTopWindow) {
std::unique_ptr<aura::Window> normal_window(CreateAppWindow());
std::unique_ptr<aura::Window> always_on_top_window(CreateAlwaysOnTopWindow());
SnapOneTestWindow(normal_window.get(),
chromeos::WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
VerifySplitViewOverviewSession(normal_window.get());
OverviewItemBase* always_on_top_overview_item =
GetOverviewItemForWindow(always_on_top_window.get());
auto* event_generator = GetEventGenerator();
event_generator->MoveMouseTo(gfx::ToRoundedPoint(
always_on_top_overview_item->target_bounds().CenterPoint()));
event_generator->ClickLeftButton();
SnapGroupController* snap_group_controller = SnapGroupController::Get();
EXPECT_FALSE(snap_group_controller->AreWindowsInSnapGroup(
normal_window.get(), always_on_top_window.get()));
EXPECT_FALSE(
snap_group_controller->GetSnapGroupForGivenWindow(normal_window.get()));
EXPECT_FALSE(
snap_group_controller->GetSnapGroupForGivenWindow(normal_window.get()));
}
TEST_F(SnapGroupTest, DisallowVisibleOnAllWorkspacesWindowToFormGroup) {
auto* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
ASSERT_EQ(2u, desks_controller->desks().size());
const Desk* desk0 = desks_controller->GetDeskAtIndex(0);
const Desk* desk1 = desks_controller->GetDeskAtIndex(1);
ASSERT_TRUE(desk0->is_active());
ASSERT_FALSE(desk1->is_active());
std::unique_ptr<aura::Window> w0(CreateAppWindow());
auto* window_widget0 = views::Widget::GetWidgetForNativeView(w0.get());
window_widget0->SetVisibleOnAllWorkspaces(true);
std::unique_ptr<aura::Window> w1(CreateAppWindow());
SnapOneTestWindow(w0.get(),
chromeos::WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
VerifySplitViewOverviewSession(w0.get());
OverviewItemBase* overview_item1 = GetOverviewItemForWindow(w1.get());
const gfx::Point overview_item1_center =
gfx::ToRoundedPoint(overview_item1->target_bounds().CenterPoint());
auto* event_generator = GetEventGenerator();
event_generator->MoveMouseTo(overview_item1_center);
event_generator->ClickLeftButton();
SnapGroupController* snap_group_controller = SnapGroupController::Get();
EXPECT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w0.get(), w1.get()));
EXPECT_FALSE(snap_group_controller->GetSnapGroupForGivenWindow(w0.get()));
EXPECT_FALSE(snap_group_controller->GetSnapGroupForGivenWindow(w1.get()));
}
TEST_F(SnapGroupTest, ShelfRoundedCornersInFasterSplitScreenEntryPoint) {
ShelfLayoutManager* shelf_layout_manager =
AshTestBase::GetPrimaryShelf()->shelf_layout_manager();
ASSERT_EQ(ShelfBackgroundType::kDefaultBg,
shelf_layout_manager->shelf_background_type());
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
SnapGroupController* snap_group_controller = SnapGroupController::Get();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_EQ(ShelfBackgroundType::kMaximized,
shelf_layout_manager->shelf_background_type());
std::unique_ptr<aura::Window> w3(CreateAppWindow());
EXPECT_EQ(ShelfBackgroundType::kMaximized,
shelf_layout_manager->shelf_background_type());
ToggleOverview();
ToggleOverview();
EXPECT_EQ(ShelfBackgroundType::kMaximized,
shelf_layout_manager->shelf_background_type());
w3.reset();
EXPECT_EQ(ShelfBackgroundType::kMaximized,
shelf_layout_manager->shelf_background_type());
event_generator->MoveMouseTo(w1->GetBoundsInScreen().top_center());
aura::test::TestWindowDelegate().set_window_component(HTCAPTION);
event_generator->PressLeftButton();
event_generator->MoveMouseBy(50, 200);
EXPECT_TRUE(WindowState::Get(w1.get())->is_dragged());
event_generator->ReleaseLeftButton();
ASSERT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_EQ(ShelfBackgroundType::kDefaultBg,
shelf_layout_manager->shelf_background_type());
}
TEST_F(SnapGroupTest, DragSnappedWindowExitPointTest) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
SnapGroupController* snap_group_controller = SnapGroupController::Get();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
aura::test::TestWindowDelegate test_window_delegate;
event_generator->MoveMouseTo(w1->GetBoundsInScreen().top_center());
test_window_delegate.set_window_component(HTCAPTION);
event_generator->PressLeftButton();
event_generator->MoveMouseBy(50, 200);
EXPECT_TRUE(WindowState::Get(w1.get())->is_dragged());
EXPECT_TRUE(GetTopmostSnapGroupDivider());
event_generator->ReleaseLeftButton();
EXPECT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_FALSE(GetTopmostSnapGroupDivider());
MaximizeToClearTheSession(w2.get());
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
event_generator->MoveTouch(w1->GetBoundsInScreen().top_center());
test_window_delegate.set_window_component(HTCAPTION);
event_generator->PressTouch();
event_generator->MoveTouchBy(50, 200);
EXPECT_TRUE(WindowState::Get(w1.get())->is_dragged());
EXPECT_TRUE(GetTopmostSnapGroupDivider());
event_generator->ReleaseTouch();
EXPECT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_FALSE(GetTopmostSnapGroupDivider());
}
TEST_F(SnapGroupTest, DragSnappedWindowAndRejoin) {
UpdateDisplay("1200x900");
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w1.get(),
chromeos::WindowStateType::kPrimarySnapped,
chromeos::kTwoThirdSnapRatio);
ASSERT_TRUE(IsInOverviewSession());
ClickOverviewItem(GetEventGenerator(), w2.get());
SnapGroupController* snap_group_controller = SnapGroupController::Get();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_EQ(WindowStateType::kSecondarySnapped,
WindowState::Get(w2.get())->GetStateType());
auto* event_generator = GetEventGenerator();
event_generator->MoveMouseTo(w2->GetBoundsInScreen().top_center());
event_generator->MoveMouseBy(0, 5);
event_generator->PressLeftButton();
event_generator->MoveMouseBy(50, 200);
EXPECT_TRUE(WindowState::Get(w2.get())->is_dragged());
EXPECT_EQ(WindowState::Get(w2.get())->drag_details()->bounds_change,
WindowResizer::kBoundsChange_Repositions);
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
event_generator->MoveMouseTo(gfx::Point(1250, 0));
event_generator->ReleaseLeftButton();
EXPECT_EQ(WindowStateType::kSecondarySnapped,
WindowState::Get(w2.get())->GetStateType());
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
}
TEST_F(SnapGroupTest, SnapToTheOppositeSideToExit) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
SnapGroupController* snap_group_controller = SnapGroupController::Get();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
SnapOneTestWindow(w1.get(),
WindowStateType::kSecondarySnapped,
chromeos::kDefaultSnapRatio);
EXPECT_TRUE(OverviewController::Get()->InOverviewSession());
EXPECT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
ClickOverviewItem(event_generator, w2.get());
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
}
TEST_F(SnapGroupTest, DragWindowOutToBreakSnapGroup) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
aura::test::TestWindowDelegate test_window_delegate;
test_window_delegate.set_window_component(HTCAPTION);
std::unique_ptr<aura::Window> w2(CreateTestWindowInShell(
{.delegate = &test_window_delegate, .bounds = {400, 5, 100, 50}}));
w2->SetProperty(chromeos::kAppTypeKey, chromeos::AppType::CHROME_APP);
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
SnapGroupController* snap_group_controller = SnapGroupController::Get();
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_TRUE(GetTopmostSnapGroupDivider());
event_generator->set_current_screen_location(
w2->GetBoundsInScreen().top_center());
event_generator->DragMouseTo(GetWorkAreaBounds().CenterPoint());
EXPECT_FALSE(GetTopmostSnapGroupDivider());
EXPECT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
}
TEST_F(SnapGroupTest, KeyboardShortcutToSnap) {
auto* snap_group_controller = Shell::Get()->snap_group_controller();
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kKeyboardShortcutToSnap);
VerifyNotSplitViewOrOverviewSession(w1.get());
VerifyNotSplitViewOrOverviewSession(w2.get());
EXPECT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
SnapOneTestWindow(w2.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kKeyboardShortcutToSnap);
VerifyNotSplitViewOrOverviewSession(w1.get());
VerifyNotSplitViewOrOverviewSession(w2.get());
EXPECT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
SnapOneTestWindow(w2.get(), WindowStateType::kSecondarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kKeyboardShortcutToSnap);
VerifyNotSplitViewOrOverviewSession(w1.get());
VerifyNotSplitViewOrOverviewSession(w2.get());
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
}
class ToplevelWindowEventHandlerCrashSimulator : public SnapGroupObserver {
public:
ToplevelWindowEventHandlerCrashSimulator() {
SnapGroupController::Get()->AddObserver(this);
}
ToplevelWindowEventHandlerCrashSimulator(
const ToplevelWindowEventHandlerCrashSimulator&) = delete;
ToplevelWindowEventHandlerCrashSimulator& operator=(
const ToplevelWindowEventHandlerCrashSimulator&) = delete;
~ToplevelWindowEventHandlerCrashSimulator() override {
SnapGroupController::Get()->RemoveObserver(this);
}
void OnSnapGroupRemoving(SnapGroup* snap_group,
SnapGroupExitPoint exit_pint) override {
if (exit_pint != SnapGroupExitPoint::kDragWindowOut) {
return;
}
ToplevelWindowEventHandler* toplevel_window_event_handler =
Shell::Get()->toplevel_window_event_handler();
toplevel_window_event_handler->ResetWindowResizerForTesting();
}
};
TEST_F(SnapGroupTest, ToplevelWindowEventHandlerDragCrashFix) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
aura::test::TestWindowDelegate test_window_delegate;
test_window_delegate.set_window_component(HTCAPTION);
std::unique_ptr<aura::Window> w2(CreateTestWindowInShell(
{.delegate = &test_window_delegate, .bounds = {400, 5, 100, 50}}));
w2->SetProperty(chromeos::kAppTypeKey, chromeos::AppType::CHROME_APP);
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
SnapGroupController* snap_group_controller = SnapGroupController::Get();
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_TRUE(GetTopmostSnapGroupDivider());
ToplevelWindowEventHandlerCrashSimulator toplevel_window_drag_simulator;
event_generator->set_current_screen_location(
w2->GetBoundsInScreen().top_center());
event_generator->DragMouseTo(GetWorkAreaBounds().CenterPoint());
EXPECT_FALSE(GetTopmostSnapGroupDivider());
EXPECT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
}
class OverviewCrashSimulator : public OverviewObserver {
public:
OverviewCrashSimulator() { OverviewController::Get()->AddObserver(this); }
OverviewCrashSimulator(const OverviewCrashSimulator&) = delete;
OverviewCrashSimulator& operator=(const OverviewCrashSimulator&) = delete;
~OverviewCrashSimulator() override {
OverviewController::Get()->RemoveObserver(this);
}
void OnOverviewModeEnding(OverviewSession* overview_session) override {
auto* split_view_overview_session =
GetSplitViewOverviewSession(Shell::GetPrimaryRootWindow());
if (!split_view_overview_session) {
return;
}
aura::Window* window = split_view_overview_session->window();
const SetBoundsWMEvent event(gfx::Rect(100, 100), true);
WindowState::Get(window)->OnWMEvent(&event);
}
};
TEST_F(SnapGroupTest, NoCrashOnOverviewModeEnding) {
OverviewCrashSimulator overview_crash_simulator;
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
VerifySplitViewOverviewSession(w1.get());
OverviewController::Get()->EndOverview(OverviewEndAction::kTests);
}
TEST_F(SnapGroupTest, MaximizeSnappedWindowExitPointTest) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
SnapGroupController* snap_group_controller = SnapGroupController::Get();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
WindowState::Get(w2.get())->Maximize();
EXPECT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
}
TEST_F(SnapGroupTest, WindowDestroyToBreakSnapGroup) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
SnapGroupController* snap_group_controller = SnapGroupController::Get();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
const auto& snap_groups = snap_group_controller->snap_groups_for_testing();
const auto& window_to_snap_group_map =
snap_group_controller->window_to_snap_group_map_for_testing();
EXPECT_EQ(snap_groups.size(), 1u);
EXPECT_EQ(window_to_snap_group_map.size(), 2u);
w1.reset();
EXPECT_TRUE(snap_groups.empty());
EXPECT_TRUE(window_to_snap_group_map.empty());
}
TEST_F(SnapGroupTest, WindowStackingOrderTest) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
std::unique_ptr<aura::Window> w3(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
ASSERT_TRUE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w1.get(), w2.get()));
wm::ActivateWindow(w3.get());
wm::ActivateWindow(w1.get());
MruWindowTracker::WindowList window_list =
Shell::Get()->mru_window_tracker()->BuildMruWindowList(kActiveDesk);
EXPECT_EQ(window_list, aura::WindowTracker::WindowList({
w1.get(),
w3.get(),
w2.get(),
}));
EXPECT_TRUE(window_util::IsStackedBelow(w3.get(), w2.get()));
}
TEST_F(SnapGroupTest, AutoSnapNewWindow) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w1.get(),
WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
VerifySplitViewOverviewSession(w1.get());
std::unique_ptr<aura::Window> w3(CreateAppWindow());
EXPECT_EQ(WindowStateType::kSecondarySnapped,
WindowState::Get(w3.get())->GetStateType());
EXPECT_TRUE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w1.get(), w3.get()));
}
TEST_F(SnapGroupTest, DontAutoSnapNewWindowOutsideSplitViewOverview) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
EXPECT_TRUE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_FALSE(
RootWindowController::ForWindow(w1.get())->split_view_overview_session());
EXPECT_FALSE(OverviewController::Get()->InOverviewSession());
std::unique_ptr<aura::Window> w3(CreateAppWindow());
EXPECT_FALSE(OverviewController::Get()->InOverviewSession());
EXPECT_FALSE(WindowState::Get(w3.get())->IsSnapped());
EXPECT_TRUE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_TRUE(GetTopmostSnapGroupDivider()->divider_widget());
}
TEST_F(SnapGroupTest, SnapRatioTest) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
const gfx::Point hover_location =
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint();
GetTopmostSnapGroupDivider()->StartResizeWithDivider(hover_location);
const auto end_point =
hover_location + gfx::Vector2d(-GetWorkAreaBounds().width() / 6, 0);
GetTopmostSnapGroupDivider()->ResizeWithDivider(end_point);
GetTopmostSnapGroupDivider()->EndResizeWithDivider(end_point);
EXPECT_FALSE(GetSplitViewController()->InSplitViewMode());
EXPECT_NEAR(chromeos::kOneThirdSnapRatio,
WindowState::Get(w1.get())->snap_ratio().value(),
0.1);
EXPECT_NEAR(chromeos::kTwoThirdSnapRatio,
WindowState::Get(w2.get())->snap_ratio().value(),
0.1);
}
TEST_F(SnapGroupTest, ResizeWithSplitViewDividerToArbitraryLocations) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
for (const auto& shelf_alignment :
{ShelfAlignment::kBottom, ShelfAlignment::kLeft}) {
std::stringstream ss;
ss << shelf_alignment;
SCOPED_TRACE("Shelf alignment = " + ss.str());
GetPrimaryShelf()->SetAlignment(shelf_alignment);
auto* event_generator = GetEventGenerator();
const gfx::Point divider_center(
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint());
event_generator->MoveMouseTo(divider_center);
event_generator->PressLeftButton();
for (const int resize_delta : {-10, 6, -15}) {
const gfx::Point resize_point(divider_center +
gfx::Vector2d(resize_delta, 0));
event_generator->MoveMouseTo(resize_point, 2);
EXPECT_EQ(resize_point,
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
}
event_generator->ReleaseLeftButton();
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
}
}
TEST_F(SnapGroupTest, RespectWindowMinimumSizeWhileResizingWithDivider) {
UpdateDisplay("1200x900");
std::unique_ptr<aura::Window> window1(
CreateAppWindowWithMinSize(gfx::Size(300, 600)));
std::unique_ptr<aura::Window> window2(CreateAppWindow());
SnapTwoTestWindows(window1.get(), window2.get(), true,
GetEventGenerator());
GetTopmostSnapGroupDivider()->StartResizeWithDivider(
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint());
GetTopmostSnapGroupDivider()->ResizeWithDivider(gfx::Point(400, 200));
EXPECT_GT(GetTopmostSnapGroupDivider()->divider_position(), 300);
GetTopmostSnapGroupDivider()->EndResizeWithDivider(gfx::Point(400, 200));
EXPECT_GT(GetTopmostSnapGroupDivider()->divider_position(), 300);
GetTopmostSnapGroupDivider()->StartResizeWithDivider(
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint());
GetTopmostSnapGroupDivider()->ResizeWithDivider(gfx::Point(200, 200));
EXPECT_EQ(GetTopmostSnapGroupDivider()->divider_position(), 300);
GetTopmostSnapGroupDivider()->EndResizeWithDivider(gfx::Point(200, 200));
EXPECT_EQ(GetTopmostSnapGroupDivider()->divider_position(), 300);
}
TEST_F(SnapGroupTest, AutomaticallyCreateGroupOnTwoWindowsSnappedInClamshell) {
auto* snap_group_controller = SnapGroupController::Get();
ASSERT_TRUE(snap_group_controller);
const auto& snap_groups = snap_group_controller->snap_groups_for_testing();
const auto& window_to_snap_group_map =
snap_group_controller->window_to_snap_group_map_for_testing();
EXPECT_TRUE(snap_groups.empty());
EXPECT_TRUE(window_to_snap_group_map.empty());
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
EXPECT_EQ(snap_groups.size(), 1u);
EXPECT_EQ(window_to_snap_group_map.size(), 2u);
std::unique_ptr<aura::Window> w3(CreateAppWindow());
wm::ActivateWindow(w2.get());
EXPECT_TRUE(window_util::IsStackedBelow(w3.get(), w1.get()));
w1.reset();
EXPECT_FALSE(GetTopmostSnapGroupDivider());
EXPECT_TRUE(snap_groups.empty());
EXPECT_TRUE(window_to_snap_group_map.empty());
}
TEST_F(SnapGroupTest, EndSplitView) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
OverviewController* overview_controller = OverviewController::Get();
EXPECT_TRUE(overview_controller->InOverviewSession());
EXPECT_TRUE(GetSplitViewController()->primary_window());
PressAndReleaseKey(ui::VKEY_ESCAPE, ui::EF_NONE);
EXPECT_FALSE(GetSplitViewController()->primary_window());
EXPECT_FALSE(overview_controller->InOverviewSession());
wm::ActivateWindow(w2.get());
auto* event_generator = GetEventGenerator();
event_generator->set_current_screen_location(GetDragPoint(w2.get()));
event_generator->DragMouseTo(GetWorkAreaBounds().right_center());
EXPECT_EQ(WindowStateType::kSecondarySnapped,
WindowState::Get(w2.get())->GetStateType());
EXPECT_FALSE(GetSplitViewController()->secondary_window());
EXPECT_FALSE(overview_controller->InOverviewSession());
wm::ActivateWindow(w1.get());
}
TEST_F(SnapGroupTest, AutoSnapWindowWithMinimumSize) {
for (const auto& shelf_alignment :
{ShelfAlignment::kBottom, ShelfAlignment::kLeft}) {
std::stringstream ss;
ss << shelf_alignment;
SCOPED_TRACE("Shelf alignment = " + ss.str());
GetPrimaryShelf()->SetAlignment(shelf_alignment);
std::unique_ptr<aura::Window> w1(CreateAppWindow());
const gfx::Rect work_area(GetWorkAreaBounds());
const int min_width = work_area.width() * 0.4f;
std::unique_ptr<aura::Window> w2(
CreateAppWindowWithMinSize(gfx::Size(min_width, work_area.height())));
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kTwoThirdSnapRatio);
EXPECT_TRUE(OverviewController::Get()->InOverviewSession());
ClickOverviewItem(GetEventGenerator(), w2.get());
auto* snap_group_controller = SnapGroupController::Get();
EXPECT_TRUE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_GE(min_width, w2->GetBoundsInScreen().width());
EXPECT_NEAR(min_width, w2->GetBoundsInScreen().width(), 1);
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kTwoThirdSnapRatio);
EXPECT_TRUE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_GE(min_width, w2->GetBoundsInScreen().width());
EXPECT_NEAR(min_width, w2->GetBoundsInScreen().width(), 1);
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
SnapOneTestWindow(w2.get(), WindowStateType::kSecondarySnapped,
chromeos::kOneThirdSnapRatio);
EXPECT_TRUE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_GE(min_width, w2->GetBoundsInScreen().width());
EXPECT_NEAR(min_width, w2->GetBoundsInScreen().width(), 1);
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
}
}
TEST_F(SnapGroupTest, AutoSnapBothWindowsWithMinimumSizes) {
for (const auto& shelf_alignment :
{ShelfAlignment::kBottom, ShelfAlignment::kLeft}) {
std::stringstream ss;
ss << shelf_alignment;
SCOPED_TRACE("Shelf alignment = " + ss.str());
GetPrimaryShelf()->SetAlignment(shelf_alignment);
const gfx::Rect work_area(GetWorkAreaBounds());
const int min_width = work_area.width() * 0.6f;
std::unique_ptr<aura::Window> w1(
CreateAppWindowWithMinSize(gfx::Size(min_width, work_area.height())));
std::unique_ptr<aura::Window> w2(
CreateAppWindowWithMinSize(gfx::Size(min_width, work_area.height())));
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kTwoThirdSnapRatio);
const gfx::Rect w1_bounds = w1->GetBoundsInScreen();
EXPECT_TRUE(OverviewController::Get()->InOverviewSession());
ClickOverviewItem(GetEventGenerator(), w2.get());
EXPECT_NEAR(min_width, w2->GetBoundsInScreen().width(), 1);
EXPECT_EQ(w1_bounds.width(), w1->GetBoundsInScreen().width());
EXPECT_FALSE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w1.get(), w2.get()));
}
}
TEST_F(SnapGroupTest, DragToSnapInOverviewWithSnapGroup) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
std::unique_ptr<aura::Window> w3(CreateAppWindow(gfx::Rect()));
ToggleOverview();
ASSERT_TRUE(IsInOverviewSession());
DragItemToPoint(GetOverviewItemForWindow(w3.get()), gfx::Point(0, 400),
GetEventGenerator(), false,
true);
EXPECT_TRUE(IsInOverviewSession());
EXPECT_EQ(WindowStateType::kPrimarySnapped,
WindowState::Get(w3.get())->GetStateType());
gfx::Rect expected_overview_grid_bounds = GetWorkAreaBounds();
expected_overview_grid_bounds.Subtract(w3->GetBoundsInScreen());
EXPECT_EQ(expected_overview_grid_bounds,
GetOverviewGridBounds(Shell::GetPrimaryRootWindow()));
}
TEST_F(SnapGroupTest, OldPartialOverview) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
ToggleOverview();
ASSERT_TRUE(IsInOverviewSession());
auto* event_generator = GetEventGenerator();
DragGroupItemToPoint(GetOverviewItemForWindow(w1.get()), gfx::Point(0, 0),
event_generator, false,
true);
EXPECT_EQ(WindowStateType::kPrimarySnapped,
WindowState::Get(w1.get())->GetStateType());
VerifySplitViewOverviewSession(w1.get());
EXPECT_TRUE(GetSplitViewController()->InSplitViewMode());
ClickOverviewItem(event_generator, w2.get());
VerifyNotSplitViewOrOverviewSession(w1.get());
EXPECT_TRUE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w1.get(), w2.get()));
MaximizeToClearTheSession(w1.get());
ToggleOverview();
ASSERT_TRUE(IsInOverviewSession());
DragGroupItemToPoint(GetOverviewItemForWindow(w1.get()), gfx::Point(0, 0),
event_generator, false,
true);
VerifySplitViewOverviewSession(w1.get());
EXPECT_TRUE(GetSplitViewController()->InSplitViewMode());
DragGroupItemToPoint(GetOverviewItemForWindow(w2.get()),
GetWorkAreaBounds().top_right(), event_generator,
false,
true);
EXPECT_EQ(WindowStateType::kSecondarySnapped,
WindowState::Get(w2.get())->GetStateType());
VerifyNotSplitViewOrOverviewSession(w1.get());
EXPECT_TRUE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w1.get(), w2.get()));
MaximizeToClearTheSession(w1.get());
MaximizeToClearTheSession(w2.get());
SwitchToTabletMode();
EXPECT_FALSE(IsInOverviewSession());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
EXPECT_TRUE(IsInOverviewSession());
EXPECT_TRUE(GetSplitViewController()->InSplitViewMode());
}
TEST_F(SnapGroupTest, RecallSnapGroupWontStartPartialOverview) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
auto* snap_group_controller = SnapGroupController::Get();
auto* snap_group =
snap_group_controller->GetSnapGroupForGivenWindow(w1.get());
ASSERT_TRUE(snap_group);
std::unique_ptr<aura::Window> w3(CreateAppWindow(GetWorkAreaBounds()));
wm::ActivateWindow(w1.get());
auto* desk_container = desks_util::GetActiveDeskContainerForRoot(
Shell::Get()->GetPrimaryRootWindow());
EXPECT_THAT(desk_container->children(),
ElementsAre(w3.get(), w2.get(), w1.get(),
snap_group->snap_group_divider()
->divider_widget()
->GetNativeWindow()));
std::unique_ptr<aura::Window> w4(CreateAppWindow());
SnapOneTestWindow(w4.get(),
chromeos::WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
VerifyNotSplitViewOrOverviewSession(w4.get());
EXPECT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w2.get(), w4.get()));
}
TEST_F(SnapGroupTest, UseShortcutToGroupSnappedWindows) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
TRACE_CALL(SnapWindowsSideBySide(kUngrouped, w1.get(), w2.get()));
auto* event_generator = GetEventGenerator();
event_generator->PressAndReleaseKey(ui::VKEY_G,
ui::EF_SHIFT_DOWN | ui::EF_COMMAND_DOWN);
TRACE_CALL(ExpectWindowsSnappedSideBySide(kGrouped, w1.get(), w2.get()));
event_generator->PressAndReleaseKey(ui::VKEY_G,
ui::EF_SHIFT_DOWN | ui::EF_COMMAND_DOWN);
TRACE_CALL(ExpectWindowsSnappedSideBySide(kUngrouped, w1.get(), w2.get()));
}
TEST_F(SnapGroupTest, UnresizableWindowWontFormSnapGroup) {
std::unique_ptr<aura::Window> normal(CreateAppWindow());
std::unique_ptr<aura::Window> unresizable(CreateAppWindow());
unresizable->SetProperty(aura::client::kResizeBehaviorKey,
aura::client::kResizeBehaviorNone);
SnapOneTestWindow(normal.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kDragWindowToEdgeToSnap);
VerifySplitViewOverviewSession(normal.get());
ClickOverviewItem(GetEventGenerator(), unresizable.get());
EXPECT_TRUE(ToastManager::Get()->IsToastShown(kAppCannotSnapToastId));
auto* snap_group_controller = SnapGroupController::Get();
EXPECT_FALSE(snap_group_controller->AreWindowsInSnapGroup(normal.get(),
unresizable.get()));
SnapOneTestWindow(unresizable.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kDragWindowToEdgeToSnap);
VerifyNotSplitViewOrOverviewSession(unresizable.get());
SnapOneTestWindow(normal.get(), WindowStateType::kSecondarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kDragWindowToEdgeToSnap);
EXPECT_FALSE(snap_group_controller->AreWindowsInSnapGroup(normal.get(),
unresizable.get()));
}
TEST_F(SnapGroupTest, UnresizableCanSnapWindowWontFormSnapGroup) {
std::unique_ptr<aura::Window> normal(CreateAppWindow());
std::unique_ptr<aura::Window> unresizable(CreateAppWindow());
unresizable->SetProperty(aura::client::kResizeBehaviorKey,
aura::client::kResizeBehaviorNone);
unresizable->SetProperty(kUnresizableSnappedSizeKey, new gfx::Size(300, 0));
SnapOneTestWindow(normal.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kDragWindowToEdgeToSnap);
VerifySplitViewOverviewSession(normal.get());
auto* event_generator = GetEventGenerator();
ClickOverviewItem(event_generator, unresizable.get());
EXPECT_EQ(WindowStateType::kSecondarySnapped,
WindowState::Get(unresizable.get())->GetStateType());
auto* snap_group_controller = SnapGroupController::Get();
EXPECT_FALSE(snap_group_controller->AreWindowsInSnapGroup(normal.get(),
unresizable.get()));
SnapOneTestWindow(unresizable.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kDragWindowToEdgeToSnap);
VerifySplitViewOverviewSession(unresizable.get());
ClickOverviewItem(event_generator, normal.get());
EXPECT_EQ(WindowStateType::kSecondarySnapped,
WindowState::Get(normal.get())->GetStateType());
EXPECT_FALSE(snap_group_controller->AreWindowsInSnapGroup(normal.get(),
unresizable.get()));
}
TEST_F(SnapGroupTest, ReSnapToOppositeSnapRatio) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
SnapOneTestWindow(w1.get(), WindowStateType::kSecondarySnapped,
chromeos::kOneThirdSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
EXPECT_EQ(
std::round(GetWorkAreaBounds().width() * chromeos::kOneThirdSnapRatio),
w1->GetBoundsInScreen().width());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kTwoThirdSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
EXPECT_EQ(
std::round(GetWorkAreaBounds().width() * chromeos::kTwoThirdSnapRatio),
w1->GetBoundsInScreen().width());
SnapOneTestWindow(w1.get(), WindowStateType::kSecondarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
EXPECT_EQ(
std::round(GetWorkAreaBounds().width() * chromeos::kDefaultSnapRatio),
w1->GetBoundsInScreen().width());
}
TEST_F(SnapGroupTest, NoDumpWithoutCrashOnMinimize) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* snap_group_controller = SnapGroupController::Get();
for (const bool is_layout_primary : {true, false}) {
if (is_layout_primary) {
UpdateDisplay("800x600");
} else {
UpdateDisplay("800x600/u");
}
ASSERT_EQ(is_layout_primary, IsLayoutPrimary(w1.get()));
SnapOneTestWindow(w1.get(),
is_layout_primary ? WindowStateType::kPrimarySnapped
: WindowStateType::kSecondarySnapped,
chromeos::kDefaultSnapRatio);
SnapOneTestWindow(w2.get(),
is_layout_primary ? WindowStateType::kSecondarySnapped
: WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
ASSERT_TRUE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
const gfx::Rect w1_bounds(w1->GetBoundsInScreen());
const gfx::Rect w2_bounds(w2->GetBoundsInScreen());
auto* window_state1 = WindowState::Get(w1.get());
window_state1->Minimize();
ASSERT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
const gfx::Rect work_area(GetWorkAreaBounds());
gfx::Rect left_half, right_half;
work_area.SplitVertically(left_half, right_half);
EXPECT_TRUE(w2_bounds.ApproximatelyEqual(
w2->GetBoundsInScreen(),
kSplitviewDividerShortSideLength / 2));
EXPECT_EQ(right_half, w2->GetBoundsInScreen());
auto* window_state2 = WindowState::Get(w2.get());
EXPECT_EQ(chromeos::kDefaultSnapRatio, window_state2->snap_ratio());
window_state1->Unminimize();
ASSERT_TRUE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
EXPECT_EQ(w1_bounds, w1->GetBoundsInScreen());
EXPECT_EQ(w2_bounds, w2->GetBoundsInScreen());
MaximizeToClearTheSession(w1.get());
MaximizeToClearTheSession(w2.get());
}
}
TEST_F(SnapGroupTest, RestoreGrouped) {
auto* snap_group_controller = SnapGroupController::Get();
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* w1_state = WindowState::Get(w1.get());
TRACE_CALL(SnapWindowsSideBySide(kGrouped, w1.get(), w2.get()));
w1_state->Maximize();
ASSERT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
w1_state->Restore();
TRACE_CALL(ExpectWindowsSnappedSideBySide(kGrouped, w1.get(), w2.get()));
w1_state->Minimize();
ASSERT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
w1_state->Restore();
TRACE_CALL(ExpectWindowsSnappedSideBySide(kGrouped, w1.get(), w2.get()));
Shell::Get()->float_controller()->ToggleFloat(w1.get());
EXPECT_TRUE(WindowState::Get(w1.get())->IsFloated());
ASSERT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
w1_state->Restore();
TRACE_CALL(ExpectWindowsSnappedSideBySide(kGrouped, w1.get(), w2.get()));
const WMEvent fullscreen_event(WM_EVENT_FULLSCREEN);
w1_state->OnWMEvent(&fullscreen_event);
EXPECT_TRUE(WindowState::Get(w1.get())->IsFullscreen());
ASSERT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
w1_state->Restore();
TRACE_CALL(ExpectWindowsSnappedSideBySide(kGrouped, w1.get(), w2.get()));
}
TEST_F(SnapGroupTest, RestoreUngrouped) {
auto* snap_group_controller = SnapGroupController::Get();
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* w1_state = WindowState::Get(w1.get());
TRACE_CALL(SnapWindowsSideBySide(kUngrouped, w1.get(), w2.get()));
w1_state->Maximize();
ASSERT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
w1_state->Restore();
EXPECT_TRUE(w1_state->IsSnapped());
ASSERT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
w1_state->Minimize();
ASSERT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
w1_state->Restore();
EXPECT_TRUE(w1_state->IsSnapped());
ASSERT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
Shell::Get()->float_controller()->ToggleFloat(w1.get());
EXPECT_TRUE(w1_state->IsFloated());
ASSERT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
w1_state->Restore();
EXPECT_TRUE(w1_state->IsSnapped());
ASSERT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
const WMEvent fullscreen_event(WM_EVENT_FULLSCREEN);
w1_state->OnWMEvent(&fullscreen_event);
EXPECT_TRUE(WindowState::Get(w1.get())->IsFullscreen());
ASSERT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
w1_state->Restore();
EXPECT_TRUE(w1_state->IsSnapped());
ASSERT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
}
TEST_F(SnapGroupTest, NoCrashWhenReSnappingSecondaryToPrimaryWithTransient) {
std::unique_ptr<aura::Window> w0(CreateAppWindow(gfx::Rect(0, 0, 300, 300)));
std::unique_ptr<aura::Window> w1(
CreateAppWindow(gfx::Rect(500, 0, 300, 300)));
auto bubble_delegate1 = std::make_unique<views::BubbleDialogDelegateView>(
views::BubbleDialogDelegateView::CreatePassKey(),
FrameViewAsh::Get(w1.get()), views::BubbleBorder::TOP_RIGHT);
bubble_delegate1->set_close_on_deactivate(false);
bubble_delegate1->set_parent_window(w1.get());
views::Widget* bubble_widget1(views::BubbleDialogDelegateView::CreateBubble(
std::move(bubble_delegate1)));
bubble_widget1->Show();
aura::Window* bubble_window1 = bubble_widget1->GetNativeWindow();
ASSERT_TRUE(bubble_window1->IsVisible());
ASSERT_TRUE(window_util::AsBubbleDialogDelegate(bubble_window1));
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w0.get(), w1.get(), true, event_generator);
EXPECT_TRUE(bubble_window1->IsVisible());
EXPECT_FALSE(bubble_window1->GetProperty(chromeos::kIsShowingInOverviewKey));
EXPECT_FALSE(bubble_window1->GetProperty(kHideInOverviewKey));
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
VerifySplitViewOverviewSession(w1.get());
bubble_widget1->Hide();
bubble_widget1->Show();
EXPECT_TRUE(bubble_window1->IsVisible());
event_generator->MoveMouseTo(gfx::ToRoundedPoint(
GetOverviewItemForWindow(w0.get())->target_bounds().CenterPoint()));
event_generator->ClickLeftButton();
EXPECT_TRUE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w1.get(), w0.get()));
}
TEST_F(SnapGroupTest, Shutdown) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
TRACE_CALL(SnapWindowsSideBySide(kGrouped, w1.get(), w2.get()));
EXPECT_TRUE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w1.get(), w2.get()));
ASSERT_TRUE(w1.release());
ASSERT_TRUE(w2.release());
}
TEST_F(SnapGroupTest, NoCrashDuringSnapGroupShutdown) {
std::unique_ptr<aura::Window> w1 = CreateAppWindow();
std::unique_ptr<aura::Window> w2 = CreateAppWindow();
TRACE_CALL(SnapWindowsSideBySide(kGrouped, w1.get(), w2.get()));
EXPECT_TRUE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w1.get(), w2.get()));
auto* snap_group =
SnapGroupController::Get()->GetSnapGroupForGivenWindow(w1.get());
SplitViewDivider* divider = snap_group->snap_group_divider();
EXPECT_EQ(2u, divider->observed_windows().size());
auto* desk_container = desks_util::GetActiveDeskContainerForRoot(
Shell::Get()->GetPrimaryRootWindow());
EXPECT_THAT(desk_container->children(),
ElementsAre(w1.get(), w2.get(), divider->GetDividerWindow()));
views::Widget::GetWidgetForNativeView(divider->GetDividerWindow())
->CloseNow();
wm::ActivateWindow(w1.get());
EXPECT_THAT(desk_container->children(), ElementsAre(w2.get(), w1.get()));
w1.reset();
}
using SnapGroupPhantomBoundsTest = SnapGroupTest;
TEST_F(SnapGroupPhantomBoundsTest, SnapGroupPhantomBounds) {
UpdateDisplay("800x600");
std::unique_ptr<aura::Window> w1 = CreateAppWindow();
std::unique_ptr<aura::Window> w2 = CreateAppWindow();
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
auto* snap_group_controller = SnapGroupController::Get();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
auto* snap_group =
snap_group_controller->GetSnapGroupForGivenWindow(w1.get());
SplitViewDivider* snap_group_divider = snap_group->snap_group_divider();
const gfx::Point resize_point(
snap_group_divider->GetDividerBoundsInScreen(false)
.CenterPoint());
snap_group_divider->StartResizeWithDivider(resize_point);
snap_group_divider->ResizeWithDivider(resize_point - gfx::Vector2d(50, 0));
snap_group_divider->EndResizeWithDivider(resize_point - gfx::Vector2d(50, 0));
ASSERT_LE(WindowState::Get(w1.get())->snap_ratio().value() -
chromeos::kDefaultSnapRatio,
kSnapToReplaceRatioDiffThreshold);
std::unique_ptr<aura::Window> w3 = CreateAppWindow();
auto* event_generator = GetEventGenerator();
DragWindowTo(event_generator, w3.get(), gfx::Point(0, 100),
false);
EXPECT_TRUE(w1->GetBoundsInScreen().ApproximatelyEqual(
WorkspaceWindowResizerTestApi()
.GetSnapPhantomWindowController()
->GetTargetWindowBounds(),
kSplitviewDividerShortSideLength / 2));
const gfx::Rect work_area =
screen_util::GetDisplayWorkAreaBoundsInParent(w3.get());
DragWindowTo(event_generator, w3.get(),
gfx::Point(work_area.right(), work_area.y() + 100),
false);
EXPECT_TRUE(w2->GetBoundsInScreen().ApproximatelyEqual(
WorkspaceWindowResizerTestApi()
.GetSnapPhantomWindowController()
->GetTargetWindowBounds(),
kSplitviewDividerShortSideLength / 2));
std::unique_ptr<aura::Window> w4 = CreateAppWindow(work_area);
DragWindowTo(event_generator, w3.get(), gfx::Point(0, 100),
false);
EXPECT_EQ(gfx::Rect(0, 0, work_area.width() / 2, work_area.height()),
WorkspaceWindowResizerTestApi()
.GetSnapPhantomWindowController()
->GetTargetWindowBounds());
}
TEST_F(SnapGroupPhantomBoundsTest, ReflectOppositeSnappedWindow) {
UpdateDisplay("800x600");
std::unique_ptr<aura::Window> w1 = CreateAppWindow();
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
auto* event_generator = GetEventGenerator();
const gfx::Rect work_area =
screen_util::GetDisplayWorkAreaBoundsInParent(w1.get());
event_generator->MoveMouseTo(w1->GetBoundsInScreen().right_center());
event_generator->DragMouseTo(250, work_area.CenterPoint().y());
ASSERT_EQ(250, w1->GetBoundsInScreen().width());
std::unique_ptr<aura::Window> w2 = CreateAppWindow();
gfx::Rect expected_bounds(work_area);
expected_bounds.Subtract(w1->GetBoundsInScreen());
DragWindowTo(event_generator, w2.get(), work_area.right_center(),
false);
EXPECT_TRUE(expected_bounds.ApproximatelyEqual(
WorkspaceWindowResizerTestApi()
.GetSnapPhantomWindowController()
->GetTargetWindowBounds(),
kSplitviewDividerShortSideLength / 2));
event_generator->ReleaseLeftButton();
ASSERT_TRUE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w2.get(), w1.get()));
EXPECT_TRUE(expected_bounds.ApproximatelyEqual(
w2->GetBoundsInScreen(),
kSplitviewDividerShortSideLength / 2));
}
TEST_F(SnapGroupPhantomBoundsTest, SnapPhantomBoundsMultiDisplay) {
UpdateDisplay("800x600,1200x900");
std::unique_ptr<aura::Window> w1 =
CreateAppWindow(gfx::Rect(1200, 0, 400, 400));
SnapOneTestWindow(w1.get(), WindowStateType::kSecondarySnapped,
chromeos::kOneThirdSnapRatio);
const display::Display display2 = display_manager()->active_display_list()[1];
ASSERT_EQ(display2,
display::Screen::Get()->GetDisplayNearestWindow(w1.get()));
std::unique_ptr<aura::Window> w2 = CreateAppWindow();
const gfx::Rect work_area2 = display2.work_area();
auto* event_generator = GetEventGenerator();
DragWindowTo(event_generator, w2.get(), work_area2.left_center(),
false);
gfx::Rect expected_bounds(work_area2);
expected_bounds.Subtract(w1->GetBoundsInScreen());
EXPECT_TRUE(expected_bounds.ApproximatelyEqual(
WorkspaceWindowResizerTestApi()
.GetSnapPhantomWindowController()
->GetTargetWindowBounds(),
kSplitviewDividerShortSideLength / 2));
event_generator->ReleaseLeftButton();
ASSERT_TRUE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w2.get(), w1.get()));
EXPECT_TRUE(expected_bounds.ApproximatelyEqual(
w2->GetBoundsInScreen(),
kSplitviewDividerShortSideLength / 2));
}
TEST_F(SnapGroupPhantomBoundsTest, SnapPhantomBoundsPortraitMode) {
UpdateDisplay("600x900");
std::unique_ptr<aura::Window> w1(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kTwoThirdSnapRatio);
const gfx::Rect work_area =
screen_util::GetDisplayWorkAreaBoundsInParent(w1.get());
ASSERT_EQ(gfx::Rect(0, 0, work_area.width(),
work_area.height() * chromeos::kTwoThirdSnapRatio),
w1->GetBoundsInScreen());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
DragWindowTo(event_generator, w2.get(), work_area.bottom_center(),
false);
gfx::Rect expected_bounds(work_area);
expected_bounds.Subtract(w1->GetBoundsInScreen());
EXPECT_TRUE(expected_bounds.ApproximatelyEqual(
WorkspaceWindowResizerTestApi()
.GetSnapPhantomWindowController()
->GetTargetWindowBounds(),
kSplitviewDividerShortSideLength / 2));
event_generator->ReleaseLeftButton();
ASSERT_TRUE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w2.get(), w1.get()));
EXPECT_TRUE(expected_bounds.ApproximatelyEqual(
w2->GetBoundsInScreen(),
kSplitviewDividerShortSideLength / 2));
}
TEST_F(SnapGroupPhantomBoundsTest, SnapPhantomBoundsMinimumSize) {
UpdateDisplay("800x600");
std::unique_ptr<aura::Window> w1(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kTwoThirdSnapRatio);
const gfx::Rect work_area =
screen_util::GetDisplayWorkAreaBoundsInParent(w1.get());
ASSERT_EQ(gfx::Rect(0, 0, work_area.width() * chromeos::kTwoThirdSnapRatio,
work_area.height()),
w1->GetBoundsInScreen());
const gfx::Size min_size(work_area.width() * 0.4f, work_area.height());
std::unique_ptr<aura::Window> w2(CreateAppWindowWithMinSize(min_size));
auto* event_generator = GetEventGenerator();
DragWindowTo(event_generator, w2.get(), work_area.right_center(),
false);
gfx::Rect expected_bounds(work_area);
expected_bounds.set_x(work_area.right() - min_size.width());
expected_bounds.set_width(min_size.width());
EXPECT_TRUE(expected_bounds.ApproximatelyEqual(
WorkspaceWindowResizerTestApi()
.GetSnapPhantomWindowController()
->GetTargetWindowBounds(),
kSplitviewDividerShortSideLength / 2));
event_generator->ReleaseLeftButton();
ASSERT_TRUE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w2.get(), w1.get()));
EXPECT_TRUE(expected_bounds.ApproximatelyEqual(
w2->GetBoundsInScreen(),
kSplitviewDividerShortSideLength / 2));
}
TEST_F(SnapGroupPhantomBoundsTest, SnapRatioGapThreshold) {
UpdateDisplay("800x600");
std::unique_ptr<aura::Window> w1(CreateAppWindow());
const WindowSnapWMEvent snap_primary(
WM_EVENT_SNAP_PRIMARY, chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kDragWindowToEdgeToSnap);
WindowState::Get(w1.get())->OnWMEvent(&snap_primary);
auto* event_generator = GetEventGenerator();
const gfx::Rect work_area =
screen_util::GetDisplayWorkAreaBoundsInParent(w1.get());
event_generator->MoveMouseTo(w1->GetBoundsInScreen().right_center());
event_generator->DragMouseTo(105, work_area.CenterPoint().y());
const gfx::Rect w1_bounds(w1->GetBoundsInScreen());
ASSERT_EQ(105, w1_bounds.width());
ASSERT_GT(std::abs(1.f - *WindowState::Get(w1.get())->snap_ratio() -
chromeos::kDefaultSnapRatio),
kSnapToReplaceRatioDiffThreshold);
std::unique_ptr<aura::Window> w2(CreateAppWindow());
DragWindowTo(event_generator, w2.get(), work_area.right_center(),
false);
gfx::Rect expected_bounds(work_area);
expected_bounds.set_width(work_area.width() / 2);
expected_bounds.set_x(work_area.width() - expected_bounds.width());
EXPECT_EQ(expected_bounds, WorkspaceWindowResizerTestApi()
.GetSnapPhantomWindowController()
->GetTargetWindowBounds());
event_generator->ReleaseLeftButton();
EXPECT_FALSE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w2.get(), w1.get()));
EXPECT_EQ(expected_bounds, w2->GetBoundsInScreen());
EXPECT_EQ(w1_bounds, w1->GetBoundsInScreen());
}
TEST_F(SnapGroupPhantomBoundsTest, SnapPhantomBoundsAfterSnapToReplace) {
const gfx::Rect work_area(GetWorkAreaBounds());
std::unique_ptr<aura::Window> w1(CreateAppWindow(work_area));
std::unique_ptr<aura::Window> w2(CreateAppWindow(work_area));
std::unique_ptr<aura::Window> w3(CreateAppWindow(work_area));
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kTwoThirdSnapRatio);
ClickOverviewItem(GetEventGenerator(), w2.get());
auto* snap_group_controller = SnapGroupController::Get();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
ASSERT_NEAR(chromeos::kTwoThirdSnapRatio,
*WindowState::Get(w1.get())->snap_ratio(), 0.01);
ASSERT_NEAR(chromeos::kOneThirdSnapRatio,
*WindowState::Get(w2.get())->snap_ratio(), 0.01);
auto* event_generator = GetEventGenerator();
wm::ActivateWindow(w3.get());
event_generator->MoveMouseTo(GetDragPoint(w3.get()));
event_generator->PressLeftButton();
event_generator->MoveMouseTo(work_area.right_center());
ASSERT_TRUE(WindowState::Get(w3.get())->is_dragged());
auto* snap_phantom_window_controller =
WorkspaceWindowResizerTestApi().GetSnapPhantomWindowController();
ASSERT_TRUE(snap_phantom_window_controller);
EXPECT_TRUE(w2->GetBoundsInScreen().ApproximatelyEqual(
snap_phantom_window_controller->GetTargetWindowBounds(),
kSplitviewDividerShortSideLength / 2));
event_generator->ReleaseLeftButton();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w3.get()));
EXPECT_TRUE(w3->GetBoundsInScreen().ApproximatelyEqual(
w2->GetBoundsInScreen(),
kSplitviewDividerShortSideLength / 2));
EXPECT_NEAR(chromeos::kOneThirdSnapRatio,
*WindowState::Get(w3.get())->snap_ratio(), 0.01);
event_generator->DragMouseTo(work_area.CenterPoint());
ASSERT_FALSE(snap_group_controller->GetSnapGroupForGivenWindow(w3.get()));
wm::ActivateWindow(w2.get());
ASSERT_TRUE(window_util::IsStackedBelow(w3.get(), w2.get()));
wm::ActivateWindow(w1.get());
event_generator->MoveMouseTo(GetDragPoint(w1.get()));
event_generator->PressLeftButton();
event_generator->MoveMouseTo(work_area.origin());
ASSERT_TRUE(WindowState::Get(w1.get())->is_dragged());
snap_phantom_window_controller =
WorkspaceWindowResizerTestApi().GetSnapPhantomWindowController();
ASSERT_TRUE(snap_phantom_window_controller);
gfx::Rect expected_bounds(work_area);
expected_bounds.Subtract(w2->GetBoundsInScreen());
EXPECT_TRUE(expected_bounds.ApproximatelyEqual(
snap_phantom_window_controller->GetTargetWindowBounds(),
kSplitviewDividerShortSideLength / 2));
event_generator->ReleaseLeftButton();
EXPECT_TRUE(expected_bounds.ApproximatelyEqual(
w1->GetBoundsInScreen(),
kSplitviewDividerShortSideLength / 2));
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_NEAR(chromeos::kTwoThirdSnapRatio,
*WindowState::Get(w1.get())->snap_ratio(), 0.01);
EXPECT_NEAR(chromeos::kOneThirdSnapRatio,
*WindowState::Get(w2.get())->snap_ratio(), 0.01);
}
using SnapGroupFloatTest = SnapGroupTest;
TEST_F(SnapGroupFloatTest, SnapGroupCreationWithFloatedWindow) {
gfx::ScopedAnimationDurationScaleMode animation_scale(
gfx::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
std::unique_ptr<aura::Window> normal_window(CreateAppWindow());
std::unique_ptr<aura::Window> floated_window(CreateAppWindow());
PressAndReleaseKey(ui::VKEY_F, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN);
ASSERT_TRUE(WindowState::Get(floated_window.get())->IsFloated());
SnapOneTestWindow(normal_window.get(),
chromeos::WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
WaitForOverviewEntered();
VerifySplitViewOverviewSession(normal_window.get());
OverviewItemBase* floated_window_overview_item =
GetOverviewItemForWindow(floated_window.get());
auto* event_generator = GetEventGenerator();
event_generator->MoveMouseTo(gfx::ToRoundedPoint(
floated_window_overview_item->target_bounds().CenterPoint()));
event_generator->ClickLeftButton();
EXPECT_TRUE(floated_window->layer()->GetAnimator()->is_animating());
EXPECT_NE(floated_window->layer()->transform(),
floated_window->layer()->GetTargetTransform());
WaitForOverviewExitAnimation();
EXPECT_FALSE(GetSplitViewController()->InSplitViewMode());
EXPECT_FALSE(WindowState::Get(floated_window.get())->IsFloated());
SnapGroupController* snap_group_controller = SnapGroupController::Get();
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(
normal_window.get(), floated_window.get()));
UnionBoundsEqualToWorkAreaBounds(normal_window.get(), floated_window.get(),
GetTopmostSnapGroupDivider());
}
TEST_F(SnapGroupFloatTest, ReSnapFloatedWindow) {
gfx::ScopedAnimationDurationScaleMode animation_scale(
gfx::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w1.get(), chromeos::WindowStateType::kPrimarySnapped,
chromeos::kTwoThirdSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
WaitForOverviewEntered();
auto* event_generator = GetEventGenerator();
ClickOverviewItem(event_generator, w2.get());
SnapGroupController* snap_group_controller = SnapGroupController::Get();
WaitForOverviewExitAnimation();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
PressAndReleaseKey(ui::VKEY_F, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN);
ASSERT_TRUE(WindowState::Get(w2.get())->IsFloated());
ASSERT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
event_generator->MoveMouseTo(GetDragPoint(w2.get()));
event_generator->DragMouseTo(GetWorkAreaBounds().right_center());
EXPECT_TRUE(w2->layer()->GetAnimator()->is_animating());
EXPECT_NE(w2->layer()->transform(), w2->layer()->GetTargetTransform());
ASSERT_EQ(WindowStateType::kSecondarySnapped,
WindowState::Get(w2.get())->GetStateType());
EXPECT_EQ(
std::round(GetWorkAreaBounds().width() * chromeos::kTwoThirdSnapRatio),
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint().x());
EXPECT_NEAR(chromeos::kTwoThirdSnapRatio,
*WindowState::Get(w1.get())->snap_ratio(), 0.01);
EXPECT_NEAR(chromeos::kOneThirdSnapRatio,
*WindowState::Get(w2.get())->snap_ratio(), 0.01);
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
wm::ActivateWindow(w1.get());
PressAndReleaseKey(ui::VKEY_F, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN);
ASSERT_TRUE(WindowState::Get(w1.get())->IsFloated());
ASSERT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
event_generator->MoveMouseTo(GetDragPoint(w1.get()));
event_generator->DragMouseTo(GetWorkAreaBounds().left_center());
ASSERT_EQ(WindowStateType::kPrimarySnapped,
WindowState::Get(w1.get())->GetStateType());
EXPECT_EQ(
std::round(GetWorkAreaBounds().width() * chromeos::kTwoThirdSnapRatio),
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint().x());
EXPECT_NEAR(chromeos::kTwoThirdSnapRatio,
*WindowState::Get(w1.get())->snap_ratio(), 0.01);
EXPECT_NEAR(chromeos::kOneThirdSnapRatio,
*WindowState::Get(w2.get())->snap_ratio(), 0.01);
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
}
using SnapGroupDividerTest = SnapGroupTest;
TEST_F(SnapGroupDividerTest, HoverToEnlargeDivider) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
SplitViewDivider* divider = GetTopmostSnapGroupDivider();
auto* divider_widget = divider->divider_widget();
ASSERT_TRUE(divider_widget);
auto* divider_view = divider->divider_view_for_testing();
ASSERT_TRUE(divider_view);
auto* focus_ring = views::FocusRing::Get(divider_view);
ASSERT_TRUE(focus_ring);
auto* handler_view = divider_view->handler_view_for_testing();
ASSERT_TRUE(handler_view);
const auto divider_bounds_before_hover =
divider_widget->GetWindowBoundsInScreen();
EXPECT_EQ(kSplitviewDividerShortSideLength,
divider_bounds_before_hover.width());
const auto handler_view_bounds_before_hover =
divider_view->GetHandlerViewBoundsInScreenForTesting();
EXPECT_EQ(kDividerHandlerShortSideLength,
handler_view_bounds_before_hover.width());
EXPECT_EQ(kDividerHandlerLongSideLength,
handler_view_bounds_before_hover.height());
event_generator->MoveMouseTo(divider_bounds_before_hover.left_center());
EXPECT_EQ(kSplitviewDividerEnlargedShortSideLength, divider_view->width());
const auto handler_view_bounds_on_hover =
divider_view->GetHandlerViewBoundsInScreenForTesting();
EXPECT_EQ(kDividerHandlerEnlargedShortSideLength,
handler_view_bounds_on_hover.width());
EXPECT_EQ(kDividerHandlerEnlargedLongSideLength,
handler_view_bounds_on_hover.height());
EXPECT_FALSE(focus_ring->GetVisible());
event_generator->MoveMouseBy(-kSplitViewDividerExtraInset / 2, 0);
EXPECT_EQ(kSplitviewDividerEnlargedShortSideLength, divider_view->width());
const auto handler_view_bounds_on_drag =
divider_view->GetHandlerViewBoundsInScreenForTesting();
EXPECT_EQ(kDividerHandlerEnlargedShortSideLength,
handler_view_bounds_on_drag.width());
EXPECT_EQ(kDividerHandlerEnlargedLongSideLength,
handler_view_bounds_on_drag.height());
EXPECT_FALSE(focus_ring->GetVisible());
event_generator->MoveMouseTo(gfx::Point(0, 0));
EXPECT_EQ(kSplitviewDividerShortSideLength, divider_view->width());
const auto handler_view_bounds_after_hover =
divider_view->GetHandlerViewBoundsInScreenForTesting();
EXPECT_EQ(kDividerHandlerShortSideLength,
handler_view_bounds_after_hover.width());
EXPECT_EQ(kDividerHandlerLongSideLength,
handler_view_bounds_after_hover.height());
EXPECT_FALSE(focus_ring->GetVisible());
}
TEST_F(SnapGroupDividerTest, DividerStackingOrderTest) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
wm::ActivateWindow(w1.get());
aura::Window* divider_window =
GetTopmostSnapGroupDivider()->GetDividerWindow();
EXPECT_TRUE(window_util::IsStackedBelow(w2.get(), w1.get()));
EXPECT_TRUE(window_util::IsStackedBelow(w1.get(), divider_window));
EXPECT_TRUE(window_util::IsStackedBelow(w2.get(), divider_window));
std::unique_ptr<aura::Window> w3(
CreateAppWindow(gfx::Rect(100, 200, 300, 400)));
EXPECT_TRUE(window_util::IsStackedBelow(divider_window, w3.get()));
EXPECT_TRUE(window_util::IsStackedBelow(w1.get(), divider_window));
EXPECT_TRUE(window_util::IsStackedBelow(w2.get(), w1.get()));
wm::ActivateWindow(w2.get());
EXPECT_TRUE(window_util::IsStackedBelow(w3.get(), w1.get()));
EXPECT_TRUE(window_util::IsStackedBelow(w1.get(), w2.get()));
EXPECT_TRUE(window_util::IsStackedBelow(w2.get(), divider_window));
}
TEST_F(SnapGroupDividerTest, DividerStackingOrderWithTransientWindow) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
wm::ActivateWindow(w1.get());
aura::Window* divider_window =
GetTopmostSnapGroupDivider()->GetDividerWindow();
EXPECT_TRUE(window_util::IsStackedBelow(w2.get(), w1.get()));
EXPECT_TRUE(window_util::IsStackedBelow(w1.get(), divider_window));
EXPECT_TRUE(window_util::IsStackedBelow(w2.get(), divider_window));
auto w1_transient =
CreateTransientChildWindow(w1.get(), gfx::Rect(100, 200, 200, 200));
w1_transient->SetProperty(aura::client::kModalKey,
ui::mojom::ModalType::kWindow);
wm::SetModalParent(w1_transient.get(), w1.get());
EXPECT_TRUE(window_util::IsStackedBelow(divider_window, w1_transient.get()));
}
TEST_F(SnapGroupDividerTest, DividerStackingOrderWithTwoTransientWindows) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
aura::Window* divider_window =
GetTopmostSnapGroupDivider()->GetDividerWindow();
ASSERT_TRUE(window_util::IsStackedBelow(w1.get(), w2.get()));
ASSERT_TRUE(window_util::IsStackedBelow(w1.get(), divider_window));
ASSERT_TRUE(window_util::IsStackedBelow(w2.get(), divider_window));
std::unique_ptr<aura::Window> w1_transient(
CreateTransientChildWindow(w1.get(), gfx::Rect(10, 20, 20, 30)));
std::unique_ptr<aura::Window> w2_transient(
CreateTransientChildWindow(w2.get(), gfx::Rect(200, 20, 20, 30)));
w2_transient->SetProperty(aura::client::kModalKey,
ui::mojom::ModalType::kWindow);
wm::SetModalParent(w2_transient.get(), w2.get());
EXPECT_TRUE(window_util::IsStackedBelow(divider_window, w2_transient.get()));
EXPECT_TRUE(
window_util::IsStackedBelow(w1_transient.get(), w2_transient.get()));
EXPECT_TRUE(window_util::IsStackedBelow(w1_transient.get(), divider_window));
}
TEST_F(SnapGroupDividerTest,
DividerStackingOrderWithDialogTransientUndoStacking) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
ASSERT_TRUE(window_util::IsStackedBelow(w1.get(), w2.get()));
aura::Window* top_window = w2.get();
aura::Window* top_window_parent = top_window->parent();
SplitViewDivider* divider = GetTopmostSnapGroupDivider();
ASSERT_TRUE(divider);
auto* divider_widget = divider->divider_widget();
ASSERT_TRUE(divider_widget);
aura::Window* divider_window = divider_widget->GetNativeWindow();
ASSERT_TRUE(wm::HasTransientAncestor(divider_window, w2.get()));
ASSERT_EQ(top_window_parent, divider_window->parent());
ASSERT_TRUE(window_util::IsStackedBelow(w1.get(), w2.get()));
ASSERT_TRUE(window_util::IsStackedBelow(w1.get(), divider_window));
ASSERT_TRUE(window_util::IsStackedBelow(w2.get(), divider_window));
views::DialogDelegateView* delegate = new views::DialogDelegateView();
views::Widget* widget = views::DialogDelegate::CreateDialogWidget(
delegate, GetContext(), w2.get());
aura::Window* w2_transient = widget->GetNativeWindow();
ASSERT_TRUE(wm::HasTransientAncestor(w2_transient, w2.get()));
ASSERT_EQ(top_window_parent, w2_transient->parent());
top_window_parent->StackChildBelow(w2_transient, divider_window);
EXPECT_TRUE(window_util::IsStackedBelow(divider_window, w2_transient));
}
TEST_F(SnapGroupDividerTest, DividerStackingWhenResizingWithDialogTransient) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
ASSERT_TRUE(window_util::IsStackedBelow(w1.get(), w2.get()));
SplitViewDivider* divider = GetTopmostSnapGroupDivider();
ASSERT_TRUE(divider);
auto* divider_widget = divider->divider_widget();
ASSERT_TRUE(divider_widget);
aura::Window* divider_window = divider_widget->GetNativeWindow();
ASSERT_TRUE(wm::HasTransientAncestor(divider_window, w2.get()));
aura::Window* top_window = w2.get();
aura::Window* top_window_parent = top_window->parent();
ASSERT_EQ(top_window_parent, divider_window->parent());
ASSERT_TRUE(window_util::IsStackedBelow(w1.get(), w2.get()));
ASSERT_TRUE(window_util::IsStackedBelow(w1.get(), divider_window));
ASSERT_TRUE(window_util::IsStackedBelow(w2.get(), divider_window));
views::DialogDelegateView* delegate = new views::DialogDelegateView();
views::Widget* widget = views::DialogDelegate::CreateDialogWidget(
delegate, GetContext(), w2.get());
aura::Window* w2_transient = widget->GetNativeWindow();
ASSERT_TRUE(wm::HasTransientAncestor(w2_transient, w2.get()));
ASSERT_EQ(top_window_parent, w2_transient->parent());
ResizeDividerTo(event_generator,
gfx::Point(w2_transient->GetBoundsInScreen().CenterPoint()));
EXPECT_TRUE(window_util::IsStackedBelow(divider_window, w2_transient));
event_generator->MoveMouseTo(
divider_widget->GetWindowBoundsInScreen().top_center());
event_generator->ClickLeftButton();
EXPECT_TRUE(window_util::IsStackedBelow(divider_window, w2_transient));
EXPECT_TRUE(window_util::IsStackedBelow(w1.get(), divider_window));
EXPECT_TRUE(window_util::IsStackedBelow(w2.get(), divider_window));
}
TEST_F(SnapGroupDividerTest, SnapGroupDividerBoundsTest) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
for (const auto is_horizontal : {true, false}) {
if (is_horizontal) {
UpdateDisplay("900x600");
} else {
UpdateDisplay("600x900");
}
ASSERT_EQ(IsLayoutHorizontal(w1.get()), is_horizontal);
SnapTwoTestWindows(w1.get(), w2.get(), is_horizontal, event_generator);
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
MaximizeToClearTheSession(w1.get());
MaximizeToClearTheSession(w2.get());
ASSERT_FALSE(
SnapGroupController::Get()->GetSnapGroupForGivenWindow(w1.get()));
}
}
TEST_F(SnapGroupDividerTest,
SnapGroupDividerBoundsWithShelfAutoHideBehaviorChange) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
SplitViewDivider* divider = GetTopmostSnapGroupDivider();
auto* divider_widget = divider->divider_widget();
ASSERT_TRUE(divider_widget);
Shelf* shelf = GetPrimaryShelf();
ASSERT_EQ(shelf->auto_hide_behavior(), ShelfAutoHideBehavior::kNever);
shelf->SetAutoHideBehavior(ShelfAutoHideBehavior::kAlways);
EXPECT_EQ(divider_widget->GetWindowBoundsInScreen().height(),
GetWorkAreaBounds().height());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(), divider);
}
TEST_F(SnapGroupDividerTest, SnapGroupDividerBoundsWithShelfAlignmentChange) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
SplitViewDivider* divider = GetTopmostSnapGroupDivider();
auto* divider_widget = divider->divider_widget();
ASSERT_TRUE(divider_widget);
Shelf* shelf = GetPrimaryShelf();
ASSERT_EQ(shelf->alignment(), ShelfAlignment::kBottom);
for (auto alignment : {ShelfAlignment::kLeft, ShelfAlignment::kRight,
ShelfAlignment::kBottom}) {
shelf->SetAlignment(alignment);
const gfx::Rect divider_bounds = divider_widget->GetWindowBoundsInScreen();
EXPECT_EQ(divider_bounds.x(), w1->GetBoundsInScreen().right());
EXPECT_EQ(divider_bounds.right(), w2->GetBoundsInScreen().x());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(), divider);
}
}
TEST_F(SnapGroupDividerTest, CursorUpdateTest) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
auto* divider = GetTopmostSnapGroupDivider();
ASSERT_TRUE(divider->divider_widget());
auto divider_bounds = GetTopmostSnapGroupDividerBoundsInScreen();
auto outside_point = divider_bounds.CenterPoint();
outside_point.Offset(-kSplitviewDividerShortSideLength * 5, 0);
EXPECT_FALSE(divider_bounds.Contains(outside_point));
auto* cursor_manager = Shell::Get()->cursor_manager();
cursor_manager->SetCursor(CursorType::kPointer);
event_generator->MoveMouseTo(outside_point);
EXPECT_TRUE(cursor_manager->IsCursorVisible());
EXPECT_FALSE(cursor_manager->IsCursorLocked());
EXPECT_EQ(CursorType::kNull, cursor_manager->GetCursor().type());
const auto delta_vector = gfx::Vector2d(0, -10);
const gfx::Point cached_hover_point =
divider_bounds.CenterPoint() + delta_vector;
event_generator->MoveMouseTo(cached_hover_point);
EXPECT_EQ(CursorType::kColumnResize, cursor_manager->GetCursor().type());
event_generator->PressLeftButton();
const auto move_vector = gfx::Vector2d(20, 0);
event_generator->MoveMouseTo(cached_hover_point + move_vector);
event_generator->ReleaseLeftButton();
EXPECT_EQ(CursorType::kColumnResize, cursor_manager->GetCursor().type());
EXPECT_EQ(
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint() + delta_vector,
cached_hover_point + move_vector);
}
TEST_F(SnapGroupDividerTest, CursorUpdateAfterSnapToReplace) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
SnapGroupController* snap_group_controller = SnapGroupController::Get();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
std::unique_ptr<aura::Window> w3(CreateAppWindow());
SnapOneTestWindow(w3.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w3.get(), w2.get()));
EXPECT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
ASSERT_TRUE(GetTopmostSnapGroupDivider()->divider_widget());
auto divider_bounds = GetTopmostSnapGroupDividerBoundsInScreen();
auto outside_point = GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint();
outside_point.Offset(-kSplitviewDividerShortSideLength * 5, 0);
EXPECT_FALSE(divider_bounds.Contains(outside_point));
auto* cursor_manager = Shell::Get()->cursor_manager();
cursor_manager->SetCursor(CursorType::kPointer);
event_generator->MoveMouseTo(outside_point);
EXPECT_TRUE(cursor_manager->IsCursorVisible());
EXPECT_FALSE(cursor_manager->IsCursorLocked());
EXPECT_EQ(CursorType::kNull, cursor_manager->GetCursor().type());
const auto delta_vector = gfx::Vector2d(0, -10);
const gfx::Point cached_hover_point =
divider_bounds.CenterPoint() + delta_vector;
event_generator->MoveMouseTo(cached_hover_point);
EXPECT_EQ(CursorType::kColumnResize, cursor_manager->GetCursor().type());
event_generator->PressLeftButton();
const auto move_vector = gfx::Vector2d(20, 0);
event_generator->MoveMouseTo(cached_hover_point + move_vector);
event_generator->ReleaseLeftButton();
EXPECT_EQ(CursorType::kColumnResize, cursor_manager->GetCursor().type());
EXPECT_EQ(
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint() + delta_vector,
cached_hover_point + move_vector);
}
TEST_F(SnapGroupDividerTest, CursorUpdateOnHandlerViewInLandscape) {
UpdateDisplay("900x600");
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
auto* divider = GetTopmostSnapGroupDivider();
ASSERT_TRUE(divider->divider_widget());
auto divider_bounds = GetTopmostSnapGroupDividerBoundsInScreen();
auto* cursor_manager = Shell::Get()->cursor_manager();
const auto center_point = divider_bounds.CenterPoint();
event_generator->MoveMouseTo(center_point);
EXPECT_TRUE(cursor_manager->IsCursorVisible());
EXPECT_EQ(CursorType::kColumnResize, cursor_manager->GetCursor().type());
event_generator->MoveMouseTo(center_point + gfx::Vector2d(0, 20));
EXPECT_EQ(CursorType::kColumnResize, cursor_manager->GetCursor().type());
}
TEST_F(SnapGroupDividerTest, CursorUpdateOnHandlerViewInPortrait) {
UpdateDisplay("600x900");
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), false, event_generator);
auto* divider = GetTopmostSnapGroupDivider();
ASSERT_TRUE(divider->divider_widget());
auto divider_bounds = GetTopmostSnapGroupDividerBoundsInScreen();
auto* cursor_manager = Shell::Get()->cursor_manager();
const auto center_point = divider_bounds.CenterPoint();
event_generator->MoveMouseTo(center_point);
EXPECT_TRUE(cursor_manager->IsCursorVisible());
EXPECT_EQ(CursorType::kRowResize, cursor_manager->GetCursor().type());
event_generator->MoveMouseTo(center_point + gfx::Vector2d(20, 0));
EXPECT_EQ(CursorType::kRowResize, cursor_manager->GetCursor().type());
}
TEST_F(SnapGroupDividerTest, SnapGroupDividerEnlargedHitArea) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
const gfx::Point cached_divider_center_point =
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint();
gfx::Point hover_location =
cached_divider_center_point -
gfx::Vector2d(kSplitviewDividerShortSideLength / 2 +
kSplitViewDividerExtraInset / 2,
0);
event_generator->MoveMouseTo(hover_location);
event_generator->PressLeftButton();
const auto move_vector = -gfx::Vector2d(50, 0);
event_generator->MoveMouseTo(hover_location + move_vector);
event_generator->ReleaseLeftButton();
EXPECT_FALSE(GetSplitViewController()->InSplitViewMode());
EXPECT_EQ(hover_location + move_vector,
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint());
}
TEST_F(SnapGroupDividerTest, DoubleTapDividerBasic) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
SnapGroupController* snap_group_controller = SnapGroupController::Get();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
SplitViewDivider* divider = GetTopmostSnapGroupDivider();
auto* divider_widget = divider->divider_widget();
ASSERT_TRUE(divider_widget);
auto* divider_view = divider->divider_view_for_testing();
ASSERT_TRUE(divider_view);
auto* handler_view = divider_view->handler_view_for_testing();
ASSERT_TRUE(handler_view);
const auto handler_view_center =
divider_view->GetHandlerViewBoundsInScreenForTesting().CenterPoint();
event_generator->set_current_screen_location(handler_view_center);
event_generator->GestureTapAt(handler_view_center);
event_generator->GestureTapAt(handler_view_center);
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
SnapGroup* snap_group =
snap_group_controller->GetSnapGroupForGivenWindow(w1.get());
EXPECT_EQ(w1.get(), snap_group->window2());
EXPECT_EQ(w2.get(), snap_group->window1());
UnionBoundsEqualToWorkAreaBounds(snap_group);
}
TEST_F(SnapGroupDividerTest, DoubleTapDividerToSwapWindowsBounds) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
SnapGroupController* snap_group_controller = SnapGroupController::Get();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
SplitViewDivider* divider = GetTopmostSnapGroupDivider();
auto* divider_widget = divider->divider_widget();
ASSERT_TRUE(divider_widget);
auto* divider_view = divider->divider_view_for_testing();
ASSERT_TRUE(divider_view);
auto* handler_view = divider_view->handler_view_for_testing();
ASSERT_TRUE(handler_view);
auto handler_view_center =
divider_view->GetHandlerViewBoundsInScreenForTesting().CenterPoint();
event_generator->set_current_screen_location(handler_view_center);
event_generator->PressLeftButton();
event_generator->MoveMouseTo(gfx::Point(100, handler_view_center.y()),
2);
event_generator->ReleaseLeftButton();
const auto w1_snap_ratio_after_drag =
WindowState::Get(w1.get())->snap_ratio();
ASSERT_TRUE(w1_snap_ratio_after_drag);
EXPECT_NE(chromeos::kDefaultSnapRatio, *w1_snap_ratio_after_drag);
const auto w2_snap_ratio_after_drag =
WindowState::Get(w2.get())->snap_ratio();
ASSERT_TRUE(w2_snap_ratio_after_drag);
EXPECT_NE(chromeos::kDefaultSnapRatio, *w2_snap_ratio_after_drag);
const gfx::Rect w1_bounds_before_swap = w1->GetBoundsInScreen();
const gfx::Rect w2_bounds_before_swap = w2->GetBoundsInScreen();
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(), divider);
handler_view_center =
divider_view->GetHandlerViewBoundsInScreenForTesting().CenterPoint();
event_generator->GestureTapAt(handler_view_center);
event_generator->GestureTapAt(handler_view_center);
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
SnapGroup* snap_group =
snap_group_controller->GetSnapGroupForGivenWindow(w1.get());
EXPECT_EQ(w1.get(), snap_group->window2());
EXPECT_EQ(w2.get(), snap_group->window1());
EXPECT_EQ(w1_bounds_before_swap.width(),
snap_group->window2()->GetBoundsInScreen().width());
EXPECT_EQ(w2_bounds_before_swap.width(),
snap_group->window1()->GetBoundsInScreen().width());
UnionBoundsEqualToWorkAreaBounds(snap_group);
}
TEST_F(SnapGroupDividerTest, DoubleTapDividerWithTransient) {
UpdateDisplay("800x600");
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
SnapGroupController* snap_group_controller = SnapGroupController::Get();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
SplitViewDivider* divider = GetTopmostSnapGroupDivider();
auto* divider_widget = divider->divider_widget();
ASSERT_TRUE(divider_widget);
std::unique_ptr<aura::Window> w1_transient(
CreateTransientChildWindow(w1.get(), gfx::Rect(10, 20, 20, 30)));
std::unique_ptr<aura::Window> w2_transient(
CreateTransientChildWindow(w2.get(), gfx::Rect(510, 30, 50, 30)));
const auto divider_center_point =
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint();
event_generator->set_current_screen_location(divider_center_point);
event_generator->GestureTapAt(divider_center_point);
event_generator->GestureTapAt(divider_center_point);
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
SnapGroup* snap_group =
snap_group_controller->GetSnapGroupForGivenWindow(w1.get());
EXPECT_EQ(w1.get(), snap_group->window2());
EXPECT_TRUE(
wm::HasTransientAncestor(w1_transient.get(), snap_group->window2()));
EXPECT_EQ(w2.get(), snap_group->window1());
EXPECT_TRUE(
wm::HasTransientAncestor(w2_transient.get(), snap_group->window1()));
UnionBoundsEqualToWorkAreaBounds(snap_group);
}
TEST_F(SnapGroupDividerTest, DoubleTapWhileDraggingDivider) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
SnapGroupController* snap_group_controller = SnapGroupController::Get();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
SplitViewDivider* divider = GetTopmostSnapGroupDivider();
auto* divider_widget = divider->divider_widget();
ASSERT_TRUE(divider_widget);
const auto divider_center_point_0 =
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint();
event_generator->PressTouchId(
0, divider_center_point_0);
event_generator->MoveTouchId(gfx::Point(100, divider_center_point_0.y()), 0);
ASSERT_TRUE(divider->is_resizing_with_divider());
const auto divider_center_point_1 =
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint();
event_generator->PressTouchId(
1, divider_center_point_1);
event_generator->PressTouchId(
1, divider_center_point_1);
base::RunLoop().RunUntilIdle();
}
TEST_F(SnapGroupDividerTest, DoubleTapDividerInTablet) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
auto* snap_group = SnapGroupController::Get()->GetTopmostSnapGroup();
EXPECT_TRUE(snap_group);
auto* new_primary_window = snap_group->window1();
auto* new_secondary_window = snap_group->window2();
SwitchToTabletMode();
EXPECT_EQ(new_primary_window, GetSplitViewController()->primary_window());
EXPECT_EQ(new_secondary_window, GetSplitViewController()->secondary_window());
EXPECT_TRUE(GetSplitViewDivider()->divider_widget());
const gfx::Point divider_center =
GetSplitViewDivider()
->GetDividerBoundsInScreen(false)
.CenterPoint();
event_generator->GestureTapAt(divider_center);
event_generator->GestureTapAt(divider_center);
EXPECT_EQ(new_secondary_window, GetSplitViewController()->primary_window());
EXPECT_EQ(new_primary_window, GetSplitViewController()->secondary_window());
}
TEST_F(SnapGroupDividerTest, ResizeCursor) {
const int min_width = 300;
std::unique_ptr<aura::Window> w1(
CreateAppWindowWithMinSize(gfx::Size(min_width, min_width)));
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
auto* snap_group_divider = SnapGroupController::Get()
->GetSnapGroupForGivenWindow(w1.get())
->snap_group_divider();
for (const auto& display_specs : {"800x600", "600x800"}) {
UpdateDisplay(display_specs);
const auto display = display::Screen::Get()->GetPrimaryDisplay();
const gfx::Point divider_point(
snap_group_divider->GetDividerBoundsInScreen(false)
.CenterPoint());
event_generator->set_current_screen_location(divider_point);
event_generator->PressLeftButton();
const bool horizontal = IsLayoutHorizontal(display);
const gfx::Point resize_point1 = horizontal
? gfx::Point(10, divider_point.y())
: gfx::Point(divider_point.x(), 10);
event_generator->MoveMouseTo(resize_point1, 2);
ASSERT_TRUE(snap_group_divider->is_resizing_with_divider());
EXPECT_EQ(min_width, GetWindowLength(w1.get(), horizontal));
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(), snap_group_divider);
const gfx::Point resize_point2 = horizontal
? gfx::Point(150, divider_point.y())
: gfx::Point(divider_point.x(), 150);
event_generator->MoveMouseTo(resize_point2, 2);
EXPECT_EQ(min_width, GetWindowLength(w1.get(), horizontal));
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(), snap_group_divider);
const gfx::Point resize_point3 =
horizontal ? gfx::Point(min_width, divider_point.y())
: gfx::Point(divider_point.x(), min_width);
event_generator->MoveMouseTo(resize_point3,
2);
EXPECT_EQ(min_width, GetWindowLength(w1.get(), horizontal));
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(), snap_group_divider);
const gfx::Point resize_point4 = horizontal
? gfx::Point(600, divider_point.y())
: gfx::Point(divider_point.x(), 600);
event_generator->MoveMouseTo(resize_point4,
2);
EXPECT_EQ(
600,
horizontal
? GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint().x()
: GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint().y());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(), snap_group_divider);
event_generator->ReleaseLeftButton();
}
}
using SnapGroupOverviewTest = SnapGroupTest;
TEST_F(SnapGroupOverviewTest, OverviewEnterExitBasic) {
UpdateDisplay("800x600");
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
OverviewController* overview_controller = OverviewController::Get();
overview_controller->StartOverview(OverviewStartAction::kTests);
WaitForOverviewEnterAnimation();
EXPECT_TRUE(overview_controller->overview_session());
EXPECT_EQ(GetOverviewGridBounds(w1->GetRootWindow()), GetWorkAreaBounds());
EXPECT_FALSE(GetTopmostSnapGroupDivider()->divider_widget()->IsVisible());
EXPECT_EQ(WindowStateType::kPrimarySnapped,
WindowState::Get(w1.get())->GetStateType());
EXPECT_EQ(WindowStateType::kSecondarySnapped,
WindowState::Get(w2.get())->GetStateType());
ToggleOverview();
EXPECT_FALSE(overview_controller->overview_session());
SnapGroupController* snap_group_controller = SnapGroupController::Get();
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_EQ(WindowStateType::kPrimarySnapped,
WindowState::Get(w1.get())->GetStateType());
EXPECT_EQ(WindowStateType::kSecondarySnapped,
WindowState::Get(w2.get())->GetStateType());
EXPECT_TRUE(GetTopmostSnapGroupDivider()->divider_widget());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
}
TEST_F(SnapGroupOverviewTest, PartialOverview) {
UpdateDisplay("800x600");
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* root_window = w1->GetRootWindow();
for (const auto& snap_state :
{WindowStateType::kPrimarySnapped, WindowStateType::kSecondarySnapped}) {
SnapOneTestWindow(w1.get(), snap_state, chromeos::kDefaultSnapRatio);
WaitForOverviewEnterAnimation();
EXPECT_TRUE(OverviewController::Get()->overview_session());
EXPECT_NE(GetOverviewGridBounds(root_window), GetWorkAreaBounds());
EXPECT_NEAR(GetOverviewGridBounds(root_window).width(),
GetWorkAreaBounds().width() / 2.f,
kSplitviewDividerShortSideLength / 2.f);
}
}
TEST_F(SnapGroupOverviewTest, OverviewGroupItemCreationBasic) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
std::unique_ptr<aura::Window> w3(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(),
true, GetEventGenerator());
OverviewController* overview_controller = OverviewController::Get();
overview_controller->StartOverview(OverviewStartAction::kTests);
WaitForOverviewEnterAnimation();
ASSERT_TRUE(overview_controller->overview_session());
const auto* overview_grid =
GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
ASSERT_TRUE(overview_grid);
EXPECT_EQ(2u, overview_grid->item_list().size());
}
TEST_F(SnapGroupOverviewTest, DividerExitOverviewAnimation) {
gfx::ScopedAnimationDurationScaleMode animation_scale(
gfx::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
SplitViewDivider* divider = GetTopmostSnapGroupDivider();
ASSERT_TRUE(divider);
auto* divider_widget = divider->divider_widget();
ASSERT_TRUE(divider_widget);
ASSERT_TRUE(divider_widget->IsVisible());
OverviewController* overview_controller = OverviewController::Get();
overview_controller->StartOverview(OverviewStartAction::kOverviewButton);
WaitForOverviewEntered();
EXPECT_TRUE(divider_widget);
EXPECT_FALSE(divider_widget->IsVisible());
SendKeyUntilOverviewItemIsFocused(ui::VKEY_TAB, GetEventGenerator());
SendKey(ui::VKEY_RETURN, GetEventGenerator(), 0);
EXPECT_TRUE(divider_widget);
EXPECT_FALSE(divider_widget->IsVisible());
WaitForOverviewExitAnimation();
EXPECT_TRUE(divider_widget);
EXPECT_TRUE(divider_widget->IsVisible());
}
TEST_F(SnapGroupOverviewTest, WindowDestructionInOverview) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
std::unique_ptr<aura::Window> w3(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
OverviewController* overview_controller = OverviewController::Get();
overview_controller->StartOverview(OverviewStartAction::kTests);
WaitForOverviewEnterAnimation();
ASSERT_TRUE(overview_controller->overview_session());
const auto* overview_grid =
GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
ASSERT_TRUE(overview_grid);
ASSERT_EQ(2u, overview_grid->item_list().size());
w2.reset();
ASSERT_TRUE(overview_grid);
EXPECT_EQ(2u, overview_grid->item_list().size());
w1.reset();
ASSERT_TRUE(overview_grid);
EXPECT_EQ(1u, overview_grid->item_list().size());
}
TEST_F(SnapGroupOverviewTest, RefreshVisualsOnWindowDestructionInOverview) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
std::unique_ptr<aura::Window> w3(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
OverviewController* overview_controller = OverviewController::Get();
overview_controller->StartOverview(OverviewStartAction::kTests);
ASSERT_TRUE(overview_controller->overview_session());
const auto* overview_grid =
GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
ASSERT_TRUE(overview_grid);
const auto& overview_items = overview_grid->item_list();
ASSERT_EQ(2u, overview_items.size());
w2.reset();
ASSERT_TRUE(overview_grid);
EXPECT_EQ(2u, overview_grid->item_list().size());
for (const auto& overview_item : overview_items) {
const gfx::RoundedCornersF rounded_corners =
overview_item->GetRoundedCorners();
EXPECT_NEAR(rounded_corners.upper_left(), kWindowMiniViewCornerRadius,
0.01);
EXPECT_NEAR(rounded_corners.upper_right(), kWindowMiniViewCornerRadius,
0.01);
EXPECT_NEAR(rounded_corners.lower_right(), kWindowMiniViewCornerRadius,
0.01);
EXPECT_NEAR(rounded_corners.lower_left(), kWindowMiniViewCornerRadius,
0.01);
}
}
TEST_F(SnapGroupOverviewTest,
RemainingWindowBoundsRestoreAfterDestructionInOverview) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
std::unique_ptr<aura::Window> w3(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
ASSERT_TRUE(GetTopmostSnapGroupDivider()->divider_widget());
const gfx::Size w1_size_before_overview = w1->GetBoundsInScreen().size();
OverviewController* overview_controller = OverviewController::Get();
overview_controller->StartOverview(OverviewStartAction::kTests);
ASSERT_TRUE(overview_controller->InOverviewSession());
EXPECT_FALSE(w1->transform().IsIdentity());
EXPECT_FALSE(w2->transform().IsIdentity());
const auto* overview_grid =
GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
ASSERT_TRUE(overview_grid);
ASSERT_EQ(2u, overview_grid->item_list().size());
w2.reset();
ASSERT_TRUE(overview_grid);
EXPECT_EQ(2u, overview_grid->item_list().size());
ClickOverviewItem(GetEventGenerator(), w1.get());
EXPECT_FALSE(overview_controller->InOverviewSession());
EXPECT_FALSE(
SnapGroupController::Get()->GetSnapGroupForGivenWindow(w1.get()));
const gfx::Size w1_size_after_overview = w1->GetBoundsInScreen().size();
EXPECT_EQ(
w1_size_before_overview.width() + kSplitviewDividerShortSideLength / 2.f,
w1_size_after_overview.width());
EXPECT_TRUE(w1->transform().IsIdentity());
}
TEST_F(SnapGroupOverviewTest, OverviewItemTest) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
OverviewController* overview_controller = OverviewController::Get();
overview_controller->StartOverview(OverviewStartAction::kTests);
OverviewSession* overview_session = overview_controller->overview_session();
ASSERT_TRUE(overview_session);
EXPECT_EQ(overview_session->GetOverviewItemForWindow(w1.get()),
overview_session->GetOverviewItemForWindow(w2.get()));
}
TEST_F(SnapGroupOverviewTest, ReflectSnapRatioInOverviewGroupItem) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
ASSERT_TRUE(GetTopmostSnapGroupDivider()->divider_widget());
const gfx::Point hover_location =
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint();
GetTopmostSnapGroupDivider()->StartResizeWithDivider(hover_location);
const gfx::Vector2d drag_delta(-GetWorkAreaBounds().width() / 6, 0);
const auto end_point = hover_location + drag_delta;
GetTopmostSnapGroupDivider()->ResizeWithDivider(end_point);
GetTopmostSnapGroupDivider()->EndResizeWithDivider(end_point);
EXPECT_FALSE(GetSplitViewController()->InSplitViewMode());
EXPECT_NEAR(chromeos::kOneThirdSnapRatio,
WindowState::Get(w1.get())->snap_ratio().value(),
0.01);
EXPECT_NEAR(chromeos::kTwoThirdSnapRatio,
WindowState::Get(w2.get())->snap_ratio().value(),
0.01);
OverviewController* overview_controller = OverviewController::Get();
overview_controller->StartOverview(OverviewStartAction::kTests);
OverviewSession* overview_session = overview_controller->overview_session();
ASSERT_TRUE(overview_session);
OverviewGroupItem* overview_group_item =
static_cast<OverviewGroupItem*>(GetOverviewItemForWindow(w1.get()));
ASSERT_TRUE(overview_group_item);
const auto& overview_items =
overview_group_item->overview_items_for_testing();
ASSERT_EQ(2u, overview_items.size());
const gfx::RectF item1_bounds = overview_items[0]->target_bounds();
const gfx::RectF item2_bounds = overview_items[1]->target_bounds();
const float size_ratio =
static_cast<float>(item1_bounds.width()) / item2_bounds.width();
EXPECT_NEAR(size_ratio, 0.5, 0.05);
}
TEST_F(SnapGroupOverviewTest, RestoreSnapRatioOnOverviewExit) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
ASSERT_TRUE(GetTopmostSnapGroupDivider()->divider_widget());
const gfx::Point hover_location =
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint();
GetTopmostSnapGroupDivider()->StartResizeWithDivider(hover_location);
const gfx::Vector2d drag_delta(-GetWorkAreaBounds().width() / 6, 0);
const auto end_point = hover_location + drag_delta;
GetTopmostSnapGroupDivider()->ResizeWithDivider(end_point);
GetTopmostSnapGroupDivider()->EndResizeWithDivider(end_point);
WindowState* w1_window_state = WindowState::Get(w1.get());
WindowState* w2_window_state = WindowState::Get(w2.get());
auto w1_snap_ratio_before = w1_window_state->snap_ratio();
ASSERT_TRUE(w1_snap_ratio_before.has_value());
auto w2_snap_ratio_before = w2_window_state->snap_ratio();
ASSERT_TRUE(w2_snap_ratio_before.has_value());
EXPECT_NEAR(chromeos::kOneThirdSnapRatio, *w1_snap_ratio_before,
0.01);
EXPECT_NEAR(chromeos::kTwoThirdSnapRatio, *w2_snap_ratio_before,
0.01);
ToggleOverview();
ASSERT_TRUE(IsInOverviewSession());
ToggleOverview();
ASSERT_FALSE(IsInOverviewSession());
auto w1_snap_ratio_after = w1_window_state->snap_ratio();
ASSERT_TRUE(w1_snap_ratio_after.has_value());
auto w2_snap_ratio_after = w2_window_state->snap_ratio();
ASSERT_TRUE(w2_snap_ratio_after.has_value());
EXPECT_NEAR(chromeos::kOneThirdSnapRatio, *w1_snap_ratio_after,
0.01);
EXPECT_NEAR(chromeos::kTwoThirdSnapRatio, *w2_snap_ratio_after,
0.01);
}
TEST_F(SnapGroupOverviewTest, CloseIndividualWindowByCloseButton) {
ScopedOverviewTransformWindow::SetImmediateCloseForTests(true);
std::unique_ptr<aura::Window> w0(CreateAppWindow());
std::unique_ptr<aura::Window> w1(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w0.get(), w1.get(), true, event_generator);
OverviewController* overview_controller = OverviewController::Get();
overview_controller->StartOverview(OverviewStartAction::kTests,
OverviewEnterExitType::kImmediateEnter);
ASSERT_TRUE(overview_controller->InOverviewSession());
OverviewGroupItem* overview_group_item =
static_cast<OverviewGroupItem*>(GetOverviewItemForWindow(w0.get()));
ASSERT_TRUE(overview_group_item);
const auto& overview_items =
overview_group_item->overview_items_for_testing();
ASSERT_EQ(2u, overview_items.size());
w0.release();
const CloseButton* w0_close_button =
overview_items[0]->overview_item_view()->close_button();
event_generator->MoveMouseTo(
w0_close_button->GetBoundsInScreen().CenterPoint());
event_generator->ClickLeftButton();
base::RunLoop().RunUntilIdle();
ASSERT_EQ(overview_items.size(), 1u);
const gfx::RoundedCornersF rounded_corners =
GetOverviewItemForWindow(w1.get())->GetRoundedCorners();
EXPECT_NEAR(rounded_corners.upper_left(), kWindowMiniViewCornerRadius,
1);
EXPECT_NEAR(rounded_corners.upper_right(), kWindowMiniViewCornerRadius,
1);
EXPECT_NEAR(rounded_corners.lower_right(), kWindowMiniViewCornerRadius,
1);
EXPECT_NEAR(rounded_corners.lower_left(), kWindowMiniViewCornerRadius,
1);
}
TEST_F(SnapGroupOverviewTest, TabbingBasic) {
std::unique_ptr<aura::Window> w0(CreateAppWindow());
std::unique_ptr<aura::Window> w1(CreateAppWindow());
SnapTwoTestWindows(w0.get(), w1.get(), true,
GetEventGenerator());
OverviewController* overview_controller = OverviewController::Get();
overview_controller->StartOverview(OverviewStartAction::kTests,
OverviewEnterExitType::kImmediateEnter);
ASSERT_TRUE(overview_controller->InOverviewSession());
OverviewGroupItem* overview_group_item =
static_cast<OverviewGroupItem*>(GetOverviewItemForWindow(w0.get()));
ASSERT_TRUE(overview_group_item);
const auto& overview_items =
overview_group_item->overview_items_for_testing();
ASSERT_EQ(2u, overview_items.size());
OverviewFocusCycler* focus_cycler =
overview_controller->overview_session()->focus_cycler();
PressAndReleaseKey(ui::VKEY_TAB);
EXPECT_EQ(overview_items[0]->overview_item_view(),
focus_cycler->GetOverviewFocusedView());
PressAndReleaseKey(ui::VKEY_TAB);
EXPECT_EQ(overview_items[1]->overview_item_view(),
focus_cycler->GetOverviewFocusedView());
PressAndReleaseKey(ui::VKEY_RETURN);
EXPECT_FALSE(overview_controller->InOverviewSession());
EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
}
TEST_F(SnapGroupOverviewTest, CtrlPlusWToCloseFocusedItemInGroupInOverview) {
ScopedOverviewTransformWindow::SetImmediateCloseForTests(true);
std::unique_ptr<aura::Window> w0(CreateAppWindow());
std::unique_ptr<aura::Window> w1(CreateAppWindow());
SnapTwoTestWindows(w0.get(), w1.get(), true,
GetEventGenerator());
OverviewController* overview_controller = OverviewController::Get();
overview_controller->StartOverview(OverviewStartAction::kTests,
OverviewEnterExitType::kImmediateEnter);
ASSERT_TRUE(overview_controller->InOverviewSession());
OverviewSession* overview_session = overview_controller->overview_session();
ASSERT_TRUE(GetOverviewItemForWindow(w0.get()));
auto* event_generator = GetEventGenerator();
SendKeyUntilOverviewItemIsFocused(ui::VKEY_TAB, event_generator);
EXPECT_TRUE(overview_session->focus_cycler()->GetOverviewFocusedView());
w0.release();
SendKey(ui::VKEY_W, event_generator, ui::EF_CONTROL_DOWN);
EXPECT_FALSE(w0.get());
EXPECT_TRUE(w1.get());
base::RunLoop().RunUntilIdle();
SendKey(ui::VKEY_TAB, event_generator);
SendKey(ui::VKEY_RETURN, event_generator);
EXPECT_FALSE(overview_controller->InOverviewSession());
}
TEST_F(SnapGroupOverviewTest, OverviewItemBoundsTest) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
ASSERT_TRUE(wm::IsActiveWindow(w2.get()));
OverviewController* overview_controller = OverviewController::Get();
overview_controller->StartOverview(OverviewStartAction::kTests);
OverviewSession* overview_session = overview_controller->overview_session();
ASSERT_TRUE(overview_session);
auto* overview_group_item =
overview_session->GetOverviewItemForWindow(w1.get());
const gfx::RectF& group_item_bounds = overview_group_item->target_bounds();
gfx::RectF cumulative_bounds;
for (aura::Window* window : overview_group_item->GetWindows()) {
auto* overview_item = overview_session->GetOverviewItemForWindow(window);
cumulative_bounds.Union(overview_item->target_bounds());
EXPECT_GT(cumulative_bounds.width(), 0u);
EXPECT_TRUE(group_item_bounds.Contains(cumulative_bounds));
}
}
TEST_F(SnapGroupOverviewTest, OverviewGroupItemRoundedCornersInHorizontal) {
std::unique_ptr<aura::Window> window0 = CreateAppWindow();
std::unique_ptr<aura::Window> window1 = CreateAppWindow();
std::unique_ptr<aura::Window> window2 = CreateAppWindow(gfx::Rect(100, 100));
SnapTwoTestWindows(window0.get(), window1.get(), true,
GetEventGenerator());
OverviewController* overview_controller = OverviewController::Get();
overview_controller->StartOverview(OverviewStartAction::kTests,
OverviewEnterExitType::kImmediateEnter);
ASSERT_TRUE(overview_controller->InOverviewSession());
const auto* overview_grid =
GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
ASSERT_TRUE(overview_grid);
const auto& item_list = overview_grid->item_list();
ASSERT_EQ(2u, item_list.size());
for (const auto& overview_item : item_list) {
EXPECT_EQ(overview_item->GetRoundedCorners(),
gfx::RoundedCornersF(kWindowMiniViewCornerRadius));
}
}
TEST_F(SnapGroupOverviewTest, ReSnapSnappedWindowInOverview) {
UpdateDisplay("800x600");
std::unique_ptr<aura::Window> w1 = CreateAppWindow();
std::unique_ptr<aura::Window> w2 = CreateAppWindow();
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
WMEvent minimize_event(WM_EVENT_MINIMIZE);
WindowState::Get(w2.get())->OnWMEvent(&minimize_event);
SnapGroupController* snap_group_controller = SnapGroupController::Get();
EXPECT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
ToggleOverview();
ASSERT_TRUE(IsInOverviewSession());
OverviewItemBase* overview_item1 = GetOverviewItemForWindow(w1.get());
OverviewItemBase* overview_item2 = GetOverviewItemForWindow(w2.get());
DragItemToPoint(overview_item1, gfx::Point(0, 200), event_generator);
DragItemToPoint(overview_item2, gfx::Point(800, 200), event_generator);
VerifyNotSplitViewOrOverviewSession(w1.get());
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
}
TEST_F(SnapGroupOverviewTest, OverviewGroupItemRoundedCornersInVertical) {
UpdateDisplay("600x900");
std::unique_ptr<aura::Window> window0 = CreateAppWindow();
std::unique_ptr<aura::Window> window1 = CreateAppWindow();
std::unique_ptr<aura::Window> window2 = CreateAppWindow(gfx::Rect(100, 100));
SnapTwoTestWindows(window0.get(), window1.get(), false,
GetEventGenerator());
OverviewController* overview_controller = OverviewController::Get();
overview_controller->StartOverview(OverviewStartAction::kTests,
OverviewEnterExitType::kImmediateEnter);
ASSERT_TRUE(overview_controller->InOverviewSession());
const auto* overview_grid =
GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
ASSERT_TRUE(overview_grid);
const auto& item_list = overview_grid->item_list();
ASSERT_EQ(2u, item_list.size());
for (const auto& overview_item : item_list) {
EXPECT_EQ(overview_item->GetRoundedCorners(),
gfx::RoundedCornersF(kWindowMiniViewCornerRadius));
}
}
TEST_F(SnapGroupOverviewTest, OverviewGroupItemShadow) {
std::unique_ptr<aura::Window> w0(CreateAppWindow());
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow(gfx::Rect(100, 100)));
SnapTwoTestWindows(w0.get(), w1.get(), true,
GetEventGenerator());
OverviewController* overview_controller = OverviewController::Get();
overview_controller->StartOverview(OverviewStartAction::kTests,
OverviewEnterExitType::kImmediateEnter);
ASSERT_TRUE(overview_controller->overview_session());
const auto* overview_grid =
GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
ASSERT_TRUE(overview_grid);
const auto& item_list = overview_grid->item_list();
ASSERT_EQ(2u, item_list.size());
ShellTestApi().WaitForOverviewAnimationState(
OverviewAnimationState::kEnterAnimationComplete);
base::RunLoop().RunUntilIdle();
for (const auto& overview_item : item_list) {
const auto shadow_content_bounds =
overview_item->get_shadow_content_bounds_for_testing();
ASSERT_FALSE(shadow_content_bounds.IsEmpty());
EXPECT_EQ(shadow_content_bounds.size(),
gfx::ToRoundedSize(overview_item->target_bounds().size()));
}
}
TEST_F(SnapGroupOverviewTest, CorrectShadowBoundsOnRemainingItemInOverview) {
std::unique_ptr<aura::Window> w0(CreateAppWindow());
std::unique_ptr<aura::Window> w1(CreateAppWindow());
SnapTwoTestWindows(w0.get(), w1.get(), true,
GetEventGenerator());
std::unique_ptr<aura::Window> w2(
CreateAppWindow(gfx::Rect(100, 100, 200, 100)));
std::unique_ptr<aura::Window> w3(
CreateAppWindow(gfx::Rect(200, 200, 100, 200)));
std::unique_ptr<aura::Window> w4(
CreateAppWindow(gfx::Rect(100, 200, 200, 300)));
std::unique_ptr<aura::Window> w5(
CreateAppWindow(gfx::Rect(200, 100, 300, 200)));
OverviewController* overview_controller = Shell::Get()->overview_controller();
overview_controller->StartOverview(OverviewStartAction::kTests,
OverviewEnterExitType::kImmediateEnter);
ASSERT_TRUE(overview_controller->overview_session());
const auto* overview_grid =
GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
ASSERT_TRUE(overview_grid);
const auto& item_list = overview_grid->item_list();
ASSERT_EQ(5u, item_list.size());
OverviewGroupItem* overview_group_item =
static_cast<OverviewGroupItem*>(item_list[4].get());
const auto& overview_items =
overview_group_item->overview_items_for_testing();
ASSERT_EQ(2u, overview_items.size());
w0.reset();
EXPECT_EQ(item_list.size(), 5u);
EXPECT_EQ(overview_items.size(), 1u);
auto* group_shadow = overview_group_item->shadow_for_testing();
EXPECT_FALSE(group_shadow);
auto* window1_shadow = overview_items[0]->shadow_for_testing();
ASSERT_TRUE(window1_shadow);
EXPECT_EQ(gfx::ToRoundedSize(overview_group_item->target_bounds().size()),
window1_shadow->GetContentBounds().size());
}
TEST_F(SnapGroupOverviewTest, GroupItemActivation) {
std::unique_ptr<aura::Window> window0 = CreateAppWindow();
std::unique_ptr<aura::Window> window1 = CreateAppWindow();
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(window0.get(), window1.get(), true,
event_generator);
ASSERT_TRUE(wm::IsActiveWindow(window1.get()));
std::unique_ptr<aura::Window> window2 = CreateAppWindow(gfx::Rect(100, 100));
ASSERT_TRUE(wm::IsActiveWindow(window2.get()));
struct {
bool use_touch;
gfx::Vector2d offset;
raw_ptr<aura::Window> expected_activated_window;
} kTestCases[]{
{false, gfx::Vector2d(-10, 0), window0.get()},
{true, gfx::Vector2d(-10, 0), window0.get()},
{false, gfx::Vector2d(10, 0), window1.get()},
{true, gfx::Vector2d(10, 0), window1.get()},
};
OverviewController* overview_controller = OverviewController::Get();
for (const auto& test : kTestCases) {
overview_controller->StartOverview(OverviewStartAction::kTests,
OverviewEnterExitType::kImmediateEnter);
ASSERT_TRUE(overview_controller->InOverviewSession());
const auto* overview_grid =
GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
ASSERT_TRUE(overview_grid);
const auto& item_list = overview_grid->item_list();
ASSERT_EQ(2u, item_list.size());
OverviewSession* overview_session = overview_controller->overview_session();
auto* overview_item =
overview_session->GetOverviewItemForWindow(window0.get());
const auto hover_point =
gfx::ToRoundedPoint(overview_item->target_bounds().CenterPoint()) +
test.offset;
event_generator->set_current_screen_location(hover_point);
if (test.use_touch) {
event_generator->PressTouch();
event_generator->ReleaseTouch();
} else {
event_generator->ClickLeftButton();
}
EXPECT_FALSE(overview_controller->InOverviewSession());
EXPECT_TRUE(wm::IsActiveWindow(test.expected_activated_window));
}
}
TEST_F(SnapGroupOverviewTest, DragAndDropBasic) {
auto* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
ASSERT_EQ(2u, desks_controller->desks().size());
std::unique_ptr<aura::Window> window0 = CreateAppWindow();
std::unique_ptr<aura::Window> window1 = CreateAppWindow();
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(window0.get(), window1.get(), true,
event_generator);
OverviewController* overview_controller = OverviewController::Get();
overview_controller->StartOverview(OverviewStartAction::kTests,
OverviewEnterExitType::kImmediateEnter);
ASSERT_TRUE(overview_controller->InOverviewSession());
const auto* overview_grid =
GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
ASSERT_TRUE(overview_grid);
const auto& item_list = overview_grid->item_list();
ASSERT_EQ(1u, item_list.size());
OverviewSession* overview_session = overview_controller->overview_session();
auto* overview_item =
overview_session->GetOverviewItemForWindow(window0.get());
const auto target_bounds_before_dragging = overview_item->target_bounds();
for (const bool by_touch : {false, true}) {
DragGroupItemToPoint(
overview_item,
Shell::GetPrimaryRootWindow()->GetBoundsInScreen().CenterPoint(),
event_generator, by_touch, false);
EXPECT_NE(overview_item->target_bounds(), target_bounds_before_dragging);
if (by_touch) {
event_generator->ReleaseTouch();
} else {
event_generator->ReleaseLeftButton();
}
EXPECT_TRUE(overview_controller->InOverviewSession());
EXPECT_EQ(overview_item->target_bounds(), target_bounds_before_dragging);
}
}
TEST_F(SnapGroupOverviewTest, DropTargetBoundsForGroupItem) {
auto* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
ASSERT_EQ(2u, desks_controller->desks().size());
std::unique_ptr<aura::Window> window0 = CreateAppWindow();
std::unique_ptr<aura::Window> window1 = CreateAppWindow();
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(window0.get(), window1.get(), true,
event_generator);
OverviewController* overview_controller = OverviewController::Get();
overview_controller->StartOverview(OverviewStartAction::kTests,
OverviewEnterExitType::kImmediateEnter);
ASSERT_TRUE(overview_controller->InOverviewSession());
aura::Window* primary_root_window = Shell::GetPrimaryRootWindow();
auto* overview_grid = GetOverviewGridForRoot(primary_root_window);
ASSERT_TRUE(overview_grid);
const auto& item_list = overview_grid->item_list();
ASSERT_EQ(1u, item_list.size());
OverviewSession* overview_session = overview_controller->overview_session();
auto* overview_item =
overview_session->GetOverviewItemForWindow(window0.get());
const gfx::RectF target_bounds_before_dragging =
overview_item->target_bounds();
for (const bool by_touch : {false, true}) {
DragGroupItemToPoint(
overview_item,
Shell::GetPrimaryRootWindow()->GetBoundsInScreen().CenterPoint(),
event_generator, by_touch, false);
EXPECT_TRUE(overview_controller->InOverviewSession());
auto* drop_target = overview_grid->drop_target();
ASSERT_TRUE(drop_target);
EXPECT_EQ(gfx::RectF(drop_target->target_bounds()),
target_bounds_before_dragging);
if (by_touch) {
event_generator->ReleaseTouch();
} else {
event_generator->ReleaseLeftButton();
}
}
}
TEST_F(SnapGroupOverviewTest, StackingOrderWhileDraggingInOverview) {
auto* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
ASSERT_EQ(2u, desks_controller->desks().size());
std::unique_ptr<aura::Window> w0 = CreateAppWindow();
std::unique_ptr<aura::Window> w1 = CreateAppWindow();
std::unique_ptr<aura::Window> w2 = CreateAppWindow(gfx::Rect(100, 100));
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w0.get(), w1.get(), true, event_generator);
OverviewController* overview_controller = OverviewController::Get();
overview_controller->StartOverview(OverviewStartAction::kTests,
OverviewEnterExitType::kImmediateEnter);
ASSERT_TRUE(overview_controller->InOverviewSession());
const auto* overview_grid =
GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
ASSERT_TRUE(overview_grid);
const auto& item_list = overview_grid->item_list();
ASSERT_EQ(2u, item_list.size());
OverviewSession* overview_session = overview_controller->overview_session();
auto* group_item = overview_session->GetOverviewItemForWindow(w0.get());
auto* group_item_widget = group_item->item_widget();
auto* w2_item_pre_drag = GetOverviewItemForWindow(w2.get());
EXPECT_TRUE(window_util::IsStackedBelow(
w2_item_pre_drag->item_widget()->GetNativeWindow(),
group_item_widget->GetNativeWindow()));
DragGroupItemToPoint(
group_item,
Shell::GetPrimaryRootWindow()->GetBoundsInScreen().CenterPoint(),
event_generator, false, false);
EXPECT_TRUE(overview_controller->InOverviewSession());
auto* w2_item_during_drag = GetOverviewItemForWindow(w2.get());
auto* w2_item_window_during_drag =
w2_item_during_drag->item_widget()->GetNativeWindow();
EXPECT_TRUE(window_util::IsStackedBelow(
w2_item_window_during_drag, group_item_widget->GetNativeWindow()));
EXPECT_TRUE(
window_util::IsStackedBelow(w2_item_window_during_drag, w0.get()));
EXPECT_TRUE(
window_util::IsStackedBelow(w2_item_window_during_drag, w1.get()));
event_generator->ReleaseLeftButton();
DragGroupItemToPoint(
group_item,
Shell::GetPrimaryRootWindow()->GetBoundsInScreen().CenterPoint(),
event_generator, false, true);
EXPECT_TRUE(overview_controller->InOverviewSession());
}
TEST_F(SnapGroupOverviewTest, HideCloseButtonsOnDragStart) {
std::unique_ptr<aura::Window> window0 = CreateAppWindow();
auto* window_widget0 = views::Widget::GetWidgetForNativeView(window0.get());
views::test::TestWidgetObserver observer0(window_widget0);
std::unique_ptr<aura::Window> window1 = CreateAppWindow();
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(window0.get(), window1.get(), true,
event_generator);
OverviewController* overview_controller = OverviewController::Get();
overview_controller->StartOverview(OverviewStartAction::kTests,
OverviewEnterExitType::kImmediateEnter);
ASSERT_TRUE(overview_controller->InOverviewSession());
OverviewGroupItem* overview_group_item =
static_cast<OverviewGroupItem*>(GetOverviewItemForWindow(window0.get()));
ASSERT_TRUE(overview_group_item);
const auto& overview_items =
overview_group_item->overview_items_for_testing();
ASSERT_EQ(2u, overview_items.size());
DragGroupItemToPoint(
overview_group_item,
Shell::GetPrimaryRootWindow()->GetBoundsInScreen().CenterPoint(),
event_generator, false, false);
for (const auto& item : overview_items) {
auto* close_button = item->overview_item_view()->close_button();
ASSERT_TRUE(item->overview_item_view()->close_button());
EXPECT_EQ(close_button->layer()->GetTargetOpacity(), 0.f);
EXPECT_FALSE(close_button->GetEnabled());
}
event_generator->ReleaseLeftButton();
for (const auto& item : overview_items) {
auto* close_button = item->overview_item_view()->close_button();
ASSERT_TRUE(item->overview_item_view()->close_button());
EXPECT_EQ(close_button->layer()->GetTargetOpacity(), 1.f);
EXPECT_TRUE(close_button->GetEnabled());
}
}
TEST_F(SnapGroupOverviewTest, ClearFocusOnDragStart) {
auto* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
ASSERT_EQ(2u, desks_controller->desks().size());
std::unique_ptr<aura::Window> w0 = CreateAppWindow();
std::unique_ptr<aura::Window> w1 = CreateAppWindow();
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w0.get(), w1.get(), true, event_generator);
OverviewController* overview_controller = OverviewController::Get();
overview_controller->StartOverview(OverviewStartAction::kTests,
OverviewEnterExitType::kImmediateEnter);
ASSERT_TRUE(overview_controller->InOverviewSession());
const auto* overview_grid =
GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
ASSERT_TRUE(overview_grid);
const auto& item_list = overview_grid->item_list();
ASSERT_EQ(1u, item_list.size());
auto* group_item =
static_cast<OverviewGroupItem*>(GetOverviewItemForWindow(w0.get()));
const auto& overview_items = group_item->overview_items_for_testing();
ASSERT_EQ(2u, overview_items.size());
OverviewFocusCycler* focus_cycler =
overview_controller->overview_session()->focus_cycler();
SendKeyUntilOverviewItemIsFocused(ui::VKEY_TAB, event_generator);
EXPECT_EQ(overview_items[0]->overview_item_view(),
focus_cycler->GetOverviewFocusedView());
DragGroupItemToPoint(
group_item,
Shell::GetPrimaryRootWindow()->GetBoundsInScreen().CenterPoint(),
event_generator, false, false);
EXPECT_TRUE(overview_controller->InOverviewSession());
EXPECT_FALSE(focus_cycler->GetOverviewFocusedView());
event_generator->ReleaseLeftButton();
}
TEST_F(SnapGroupOverviewTest, ConvertToTabletModeWhileDragging) {
std::unique_ptr<aura::Window> w0 = CreateAppWindow();
std::unique_ptr<aura::Window> w1 = CreateAppWindow();
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w0.get(), w1.get(), true, event_generator);
OverviewController* overview_controller = OverviewController::Get();
overview_controller->StartOverview(OverviewStartAction::kTests,
OverviewEnterExitType::kImmediateEnter);
ASSERT_TRUE(overview_controller->InOverviewSession());
const auto* overview_grid =
GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
ASSERT_TRUE(overview_grid);
const auto& item_list = overview_grid->item_list();
ASSERT_EQ(1u, item_list.size());
auto* group_item =
static_cast<OverviewGroupItem*>(GetOverviewItemForWindow(w0.get()));
const auto& overview_items = group_item->overview_items_for_testing();
ASSERT_EQ(2u, overview_items.size());
DragGroupItemToPoint(
group_item,
Shell::GetPrimaryRootWindow()->GetBoundsInScreen().CenterPoint(),
event_generator, false, false);
EXPECT_TRUE(overview_controller->InOverviewSession());
SwitchToTabletMode();
base::RunLoop().RunUntilIdle();
}
TEST_F(SnapGroupOverviewTest, FlingToCloseGroupItem) {
ScopedOverviewTransformWindow::SetImmediateCloseForTests(true);
auto* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
ASSERT_EQ(2u, desks_controller->desks().size());
std::unique_ptr<aura::Window> window0 = CreateAppWindow();
auto* window_widget0 = views::Widget::GetWidgetForNativeView(window0.get());
views::test::TestWidgetObserver observer0(window_widget0);
std::unique_ptr<aura::Window> window1 = CreateAppWindow();
auto* window_widget1 = views::Widget::GetWidgetForNativeView(window1.get());
views::test::TestWidgetObserver observer1(window_widget1);
SnapTwoTestWindows(window0.get(), window1.get(), true,
GetEventGenerator());
OverviewController* overview_controller = OverviewController::Get();
overview_controller->StartOverview(OverviewStartAction::kOverviewButton,
OverviewEnterExitType::kImmediateEnter);
OverviewSession* overview_session = overview_controller->overview_session();
ASSERT_TRUE(overview_session);
OverviewGroupItem* overview_group_item =
static_cast<OverviewGroupItem*>(GetOverviewItemForWindow(window0.get()));
ASSERT_TRUE(overview_group_item);
const auto& overview_items =
overview_group_item->overview_items_for_testing();
ASSERT_EQ(2u, overview_items.size());
window0.release();
window1.release();
gfx::PointF location = overview_group_item->target_bounds().CenterPoint();
location.Offset(-10, 0);
overview_session->InitiateDrag(overview_group_item, location,
true,
overview_items[0].get());
overview_session->Fling(overview_group_item, location, 0,
2500);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(observer0.widget_closed());
EXPECT_TRUE(observer1.widget_closed());
EXPECT_FALSE(IsInOverviewSession());
}
TEST_F(SnapGroupOverviewTest, GroupItemSnapBehaviorInOverview) {
auto* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
ASSERT_EQ(2u, desks_controller->desks().size());
std::unique_ptr<aura::Window> window0 = CreateAppWindow();
std::unique_ptr<aura::Window> window1 = CreateAppWindow();
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(window0.get(), window1.get(), true,
event_generator);
OverviewController* overview_controller = OverviewController::Get();
overview_controller->StartOverview(OverviewStartAction::kTests,
OverviewEnterExitType::kImmediateEnter);
ASSERT_TRUE(overview_controller->InOverviewSession());
auto* overview_grid = GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
ASSERT_TRUE(overview_grid);
const auto& item_list = overview_grid->item_list();
ASSERT_EQ(1u, item_list.size());
OverviewSession* overview_session = overview_controller->overview_session();
auto* overview_item =
overview_session->GetOverviewItemForWindow(window0.get());
const auto target_bounds_before_dragging = overview_item->target_bounds();
const auto drag_point =
Shell::GetPrimaryRootWindow()->GetBoundsInScreen().left_center();
DragGroupItemToPoint(overview_item, drag_point, event_generator,
false, true);
DragGroupItemToPoint(overview_item, drag_point, event_generator,
false, true);
EXPECT_FALSE(overview_item->get_cannot_snap_widget_for_testing());
EXPECT_TRUE(overview_controller->InOverviewSession());
EXPECT_EQ(overview_item->target_bounds(), target_bounds_before_dragging);
window0.reset();
DragGroupItemToPoint(
overview_session->GetOverviewItemForWindow(window1.get()), drag_point,
event_generator, false, true);
EXPECT_EQ(WindowState::Get(window1.get())->GetStateType(),
WindowStateType::kPrimarySnapped);
}
TEST_F(SnapGroupOverviewTest, OverviewGroupItemForNonPrimaryScreenOrientation) {
UpdateDisplay("1200x900/r");
display::DisplayManager* display_manager = Shell::Get()->display_manager();
const auto& displays = display_manager->active_display_list();
ASSERT_EQ(1U, displays.size());
ASSERT_EQ(chromeos::OrientationType::kPortraitSecondary,
chromeos::GetDisplayCurrentOrientation(displays[0]));
std::unique_ptr<aura::Window> window2 = CreateAppWindow(gfx::Rect(200, 200));
std::unique_ptr<aura::Window> window1 = CreateAppWindow(gfx::Rect(100, 100));
std::unique_ptr<aura::Window> window0 = CreateAppWindow(gfx::Rect(10, 10));
SnapOneTestWindow(window0.get(), WindowStateType::kSecondarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kDragWindowToEdgeToSnap);
ASSERT_TRUE(IsInOverviewSession());
auto* event_generator = GetEventGenerator();
SendKeyUntilOverviewItemIsFocused(ui::VKEY_TAB, event_generator);
event_generator->PressKey(ui::VKEY_RETURN, 0);
ASSERT_TRUE(SnapGroupController::Get()->AreWindowsInSnapGroup(window0.get(),
window1.get()));
gfx::Rect work_area = GetWorkAreaBoundsForWindow(window0.get());
const gfx::Rect divider_bounds =
GetTopmostSnapGroupDivider()->divider_widget()->GetWindowBoundsInScreen();
EXPECT_EQ(gfx::Rect(work_area.x(), work_area.y(), work_area.width(),
divider_bounds.y()),
window0->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(work_area.x(), divider_bounds.bottom(), work_area.width(),
work_area.height() - divider_bounds.bottom()),
window1->GetBoundsInScreen());
EXPECT_TRUE(wm::IsActiveWindow(window1.get()));
std::unique_ptr<aura::Window> window3 =
CreateAppWindow(gfx::Rect(300, 300), chromeos::AppType::CHROME_APP);
EXPECT_TRUE(wm::IsActiveWindow(window3.get()));
ToggleOverview();
SendKeyUntilOverviewItemIsFocused(ui::VKEY_TAB, event_generator);
event_generator->PressKey(ui::VKEY_TAB, 0);
event_generator->PressKey(ui::VKEY_RETURN, 0);
EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
}
TEST_F(SnapGroupOverviewTest, SkipPairingInOverviewWhenClickingEmptyArea) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
WaitForOverviewEnterAnimation();
OverviewController* overview_controller = OverviewController::Get();
EXPECT_TRUE(overview_controller->InOverviewSession());
EXPECT_EQ(WindowState::Get(w1.get())->GetStateType(),
WindowStateType::kPrimarySnapped);
ASSERT_EQ(1u, GetOverviewSession()->grid_list().size());
auto* w2_overview_item = GetOverviewItemForWindow(w2.get());
EXPECT_TRUE(w2_overview_item);
const gfx::Point outside_point =
gfx::ToRoundedPoint(
w2_overview_item->GetTransformedBounds().bottom_right()) +
gfx::Vector2d(20, 20);
auto* event_generator = GetEventGenerator();
event_generator->MoveMouseTo(outside_point);
event_generator->ClickLeftButton();
EXPECT_FALSE(overview_controller->InOverviewSession());
EXPECT_EQ(WindowState::Get(w1.get())->GetStateType(),
WindowStateType::kPrimarySnapped);
EXPECT_FALSE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w1.get(), w2.get()));
}
TEST_F(SnapGroupOverviewTest, SkipPairingInOverviewWithEscapeKey) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
OverviewController* overview_controller = OverviewController::Get();
EXPECT_TRUE(overview_controller->InOverviewSession());
EXPECT_TRUE(GetOverviewSession()->IsWindowInOverview(w2.get()));
EXPECT_EQ(WindowState::Get(w1.get())->GetStateType(),
WindowStateType::kPrimarySnapped);
ASSERT_EQ(1u, GetOverviewSession()->grid_list().size());
GetEventGenerator()->PressAndReleaseKey(ui::VKEY_ESCAPE, ui::EF_NONE);
EXPECT_FALSE(overview_controller->InOverviewSession());
EXPECT_EQ(WindowState::Get(w1.get())->GetStateType(),
WindowStateType::kPrimarySnapped);
EXPECT_FALSE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w1.get(), w2.get()));
}
TEST_F(SnapGroupOverviewTest, OverviewItemFillMode) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
const gfx::Point divider_center(
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint());
event_generator->MoveMouseTo(divider_center);
event_generator->PressLeftButton();
event_generator->MoveMouseTo(gfx::Point(10, 200), 2);
event_generator->ReleaseLeftButton();
ToggleOverview();
ASSERT_TRUE(IsInOverviewSession());
OverviewGroupItem* overview_group_item =
static_cast<OverviewGroupItem*>(GetOverviewItemForWindow(w1.get()));
const auto& overview_items =
overview_group_item->overview_items_for_testing();
for (const auto& overview_item : overview_items) {
EXPECT_EQ(OverviewItemFillMode::kNormal,
overview_item->GetOverviewItemFillMode());
}
}
TEST_F(SnapGroupOverviewTest, BubbleTransientIsVisibleInOverview) {
std::unique_ptr<aura::Window> w0(CreateAppWindow(gfx::Rect(0, 0, 300, 300)));
std::unique_ptr<aura::Window> w1(
CreateAppWindow(gfx::Rect(500, 20, 200, 200)));
SnapTwoTestWindows(w0.get(), w1.get(), true,
GetEventGenerator());
auto bubble_delegate0 = std::make_unique<views::BubbleDialogDelegateView>(
views::BubbleDialogDelegateView::CreatePassKey(), nullptr,
views::BubbleBorder::NONE);
bubble_delegate0->set_close_on_deactivate(false);
bubble_delegate0->set_parent_window(w0.get());
views::Widget* bubble_widget0(views::BubbleDialogDelegateView::CreateBubble(
std::move(bubble_delegate0)));
aura::Window* bubble_window0 = bubble_widget0->GetNativeWindow();
ASSERT_TRUE(window_util::AsBubbleDialogDelegate(bubble_window0));
bubble_widget0->Show();
EXPECT_TRUE(wm::HasTransientAncestor(bubble_window0, w0.get()));
std::unique_ptr<aura::Window> w1_transient(
CreateTransientChildWindow(w1.get(), gfx::Rect(510, 30, 50, 30)));
wm::AddTransientChild(w1.get(), w1_transient.get());
ToggleOverview();
ASSERT_TRUE(IsInOverviewSession());
EXPECT_TRUE(bubble_window0->IsVisible());
EXPECT_TRUE(w1_transient->IsVisible());
ToggleOverview();
ASSERT_FALSE(IsInOverviewSession());
EXPECT_TRUE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w0.get(), w1.get()));
EXPECT_TRUE(bubble_window0->IsVisible());
EXPECT_TRUE(w1_transient->IsVisible());
}
TEST_F(SnapGroupOverviewTest, NoDuplicateGroupItemsWithActivatableTransient) {
UpdateDisplay("900x600");
std::unique_ptr<aura::Window> w0(CreateAppWindow());
std::unique_ptr<aura::Window> w1(CreateAppWindow());
SnapTwoTestWindows(w0.get(), w1.get(), true,
GetEventGenerator());
auto w1_transient =
CreateTransientChildWindow(w1.get(), gfx::Rect(600, 200, 200, 200));
w1_transient->SetProperty(aura::client::kModalKey,
ui::mojom::ModalType::kWindow);
wm::SetModalParent(w1_transient.get(), w1.get());
wm::ActivateWindow(w0.get());
ToggleOverview();
ASSERT_TRUE(IsInOverviewSession());
auto* overview_grid = GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
ASSERT_TRUE(overview_grid);
EXPECT_EQ(1u, overview_grid->item_list().size());
}
TEST_F(SnapGroupOverviewTest, UngroupDuringDragInOverview) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
ASSERT_FALSE(IsInOverviewSession());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
auto* snap_group_controller = SnapGroupController::Get();
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
ToggleOverview();
ASSERT_TRUE(IsInOverviewSession());
OverviewItemBase* overview_group_item = GetOverviewItemForWindow(w1.get());
ASSERT_TRUE(overview_group_item);
DragItemToPoint(overview_group_item, gfx::Point(200, 100),
GetEventGenerator(),
false, false);
ASSERT_TRUE(overview_group_item->IsDragItem());
ASSERT_TRUE(IsInOverviewSession());
::wm::Restore(w1.get());
EXPECT_TRUE(IsInOverviewSession());
EXPECT_NE(overview_group_item, GetOverviewItemForWindow(w1.get()));
}
using SnapGroupDesksTest = SnapGroupTest;
TEST_F(SnapGroupDesksTest, DragOverviewGroupItemToAnotherDesk) {
auto* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
ASSERT_EQ(2u, desks_controller->desks().size());
std::unique_ptr<aura::Window> window0 = CreateAppWindow();
std::unique_ptr<aura::Window> window1 = CreateAppWindow();
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(window0.get(), window1.get(), true,
event_generator);
ASSERT_TRUE(EnterOverview(OverviewEnterExitType::kImmediateEnter));
auto* overview_grid = GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
ASSERT_TRUE(overview_grid);
const auto& item_list = overview_grid->item_list();
ASSERT_EQ(1u, item_list.size());
const auto* desks_bar_view = overview_grid->desks_bar_view();
ASSERT_TRUE(desks_bar_view);
const auto& mini_views = desks_bar_view->mini_views();
ASSERT_EQ(2u, mini_views.size());
const Desk* desk0 = desks_controller->GetDeskAtIndex(0);
const Desk* desk1 = desks_controller->GetDeskAtIndex(1);
ASSERT_EQ(desks_util::GetDeskForContext(window0.get()), desk0);
ASSERT_EQ(desks_util::GetDeskForContext(window1.get()), desk0);
OverviewController* overview_controller = Shell::Get()->overview_controller();
DragGroupItemToPoint(
overview_controller->overview_session()->GetOverviewItemForWindow(
window0.get()),
mini_views[1]->GetBoundsInScreen().CenterPoint(), event_generator,
false,
true);
EXPECT_TRUE(overview_controller->InOverviewSession());
ASSERT_EQ(desks_util::GetDeskForContext(window0.get()), desk1);
ASSERT_EQ(desks_util::GetDeskForContext(window1.get()), desk1);
EXPECT_TRUE(SnapGroupController::Get()->AreWindowsInSnapGroup(window0.get(),
window1.get()));
ActivateDesk(desk1);
EXPECT_TRUE(GetTopmostSnapGroupDivider()->divider_widget());
EXPECT_EQ(desks_util::GetDeskForContext(
GetTopmostSnapGroupDivider()->GetDividerWindow()),
desk1);
}
TEST_F(SnapGroupDesksTest,
NoCrashWhenDraggingOverviewGroupItemWithBubbleToAnotherDesk) {
auto* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
ASSERT_EQ(2u, desks_controller->desks().size());
std::unique_ptr<aura::Window> w0(CreateAppWindow(gfx::Rect(0, 0, 300, 300)));
std::unique_ptr<aura::Window> w1(
CreateAppWindow(gfx::Rect(500, 20, 200, 200)));
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w0.get(), w1.get(), true, event_generator);
views::Widget* w0_widget = views::Widget::GetWidgetForNativeWindow(w0.get());
auto bubble_delegate = std::make_unique<views::BubbleDialogDelegateView>(
views::BubbleDialogDelegateView::CreatePassKey(), nullptr,
views::BubbleBorder::NONE);
bubble_delegate->set_parent_window(w0_widget->GetNativeWindow());
bubble_delegate->set_close_on_deactivate(false);
views::Widget* bubble_widget(views::BubbleDialogDelegateView::CreateBubble(
std::move(bubble_delegate)));
aura::Window* bubble_window = bubble_widget->GetNativeWindow();
wm::AddTransientChild(w0.get(), bubble_window);
bubble_widget->Show();
EXPECT_TRUE(wm::HasTransientAncestor(bubble_window, w0.get()));
ASSERT_TRUE(EnterOverview(OverviewEnterExitType::kImmediateEnter));
auto* overview_grid = GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
ASSERT_TRUE(overview_grid);
const auto& item_list = overview_grid->item_list();
ASSERT_EQ(1u, item_list.size());
const auto* desks_bar_view = overview_grid->desks_bar_view();
ASSERT_TRUE(desks_bar_view);
const auto& mini_views = desks_bar_view->mini_views();
ASSERT_EQ(2u, mini_views.size());
const Desk* desk0 = desks_controller->GetDeskAtIndex(0);
const Desk* desk1 = desks_controller->GetDeskAtIndex(1);
ASSERT_EQ(desks_util::GetDeskForContext(w0.get()), desk0);
ASSERT_EQ(desks_util::GetDeskForContext(w1.get()), desk0);
OverviewController* overview_controller = Shell::Get()->overview_controller();
DragGroupItemToPoint(
overview_controller->overview_session()->GetOverviewItemForWindow(
w0.get()),
mini_views[1]->GetBoundsInScreen().CenterPoint(), event_generator,
false,
true);
EXPECT_TRUE(overview_controller->InOverviewSession());
ASSERT_EQ(desks_util::GetDeskForContext(w0.get()), desk1);
ASSERT_EQ(desks_util::GetDeskForContext(w1.get()), desk1);
EXPECT_TRUE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w0.get(), w1.get()));
}
TEST_F(SnapGroupDesksTest, DragOverviewGroupItemToAnotherDeskWithSnapGroup) {
auto* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
ASSERT_EQ(2u, desks_controller->desks().size());
const Desk* desk0 = desks_controller->GetDeskAtIndex(0);
const Desk* desk1 = desks_controller->GetDeskAtIndex(1);
std::unique_ptr<aura::Window> w0(CreateAppWindow(gfx::Rect(0, 0, 300, 300)));
std::unique_ptr<aura::Window> w1(
CreateAppWindow(gfx::Rect(500, 20, 200, 200)));
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w0.get(), w1.get(), true, event_generator);
ASSERT_EQ(desks_util::GetDeskForContext(w0.get()), desk0);
ASSERT_EQ(desks_util::GetDeskForContext(w1.get()), desk0);
ActivateDesk(desk1);
std::unique_ptr<aura::Window> w2(CreateAppWindow(gfx::Rect(0, 0, 100, 100)));
std::unique_ptr<aura::Window> w3(
CreateAppWindow(gfx::Rect(200, 20, 100, 200)));
SnapTwoTestWindows(w2.get(), w3.get(), true, event_generator);
ASSERT_EQ(desks_util::GetDeskForContext(w2.get()), desk1);
ASSERT_EQ(desks_util::GetDeskForContext(w3.get()), desk1);
ASSERT_TRUE(EnterOverview());
auto* overview_grid = GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
ASSERT_TRUE(overview_grid);
const auto* desks_bar_view = overview_grid->desks_bar_view();
ASSERT_TRUE(desks_bar_view);
const auto& mini_views = desks_bar_view->mini_views();
ASSERT_EQ(mini_views.size(), 2u);
OverviewController* overview_controller = Shell::Get()->overview_controller();
DragGroupItemToPoint(
overview_controller->overview_session()->GetOverviewItemForWindow(
w3.get()),
mini_views[0]->GetBoundsInScreen().CenterPoint(), event_generator,
false,
true);
EXPECT_TRUE(overview_controller->InOverviewSession());
EXPECT_EQ(desks_util::GetDeskForContext(w2.get()), desk0);
EXPECT_EQ(desks_util::GetDeskForContext(w3.get()), desk0);
EXPECT_TRUE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w0.get(), w1.get()));
EXPECT_TRUE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w2.get(), w3.get()));
ActivateDesk(desk0);
}
TEST_F(SnapGroupDesksTest, WindowDeskContainerChange) {
auto* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
ASSERT_EQ(2u, desks_controller->desks().size());
const Desk* desk0 = desks_controller->GetDeskAtIndex(0);
const Desk* desk1 = desks_controller->GetDeskAtIndex(1);
ASSERT_TRUE(desk0->is_active());
std::unique_ptr<aura::Window> w0(CreateAppWindow());
std::unique_ptr<aura::Window> w1(CreateAppWindow());
SnapTwoTestWindows(w0.get(), w1.get(), true,
GetEventGenerator());
SnapGroup* snap_group =
SnapGroupController::Get()->GetSnapGroupForGivenWindow(w0.get());
ASSERT_TRUE(snap_group);
ASSERT_EQ(desks_util::GetDeskForContext(w0.get()), desk0);
ASSERT_EQ(desks_util::GetDeskForContext(w1.get()), desk0);
PressAndReleaseKey(ui::VKEY_OEM_6, ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN);
EXPECT_EQ(desks_util::GetDeskForContext(w0.get()), desk1);
EXPECT_EQ(desks_util::GetDeskForContext(w1.get()), desk1);
ActivateDesk(desk1);
PressAndReleaseKey(ui::VKEY_OEM_4, ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN);
EXPECT_EQ(desks_util::GetDeskForContext(w0.get()), desk0);
EXPECT_EQ(desks_util::GetDeskForContext(w1.get()), desk0);
}
TEST_F(SnapGroupDesksTest, DeskSwitchingInOverview) {
auto* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
ASSERT_EQ(2u, desks_controller->desks().size());
const Desk* desk0 = desks_controller->GetDeskAtIndex(0);
const Desk* desk1 = desks_controller->GetDeskAtIndex(1);
ASSERT_TRUE(desk0->is_active());
std::unique_ptr<aura::Window> w0(CreateAppWindow());
std::unique_ptr<aura::Window> w1(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w0.get(), w1.get(), true, event_generator);
SnapGroup* snap_group =
SnapGroupController::Get()->GetSnapGroupForGivenWindow(w0.get());
ASSERT_TRUE(snap_group);
ASSERT_EQ(desks_util::GetDeskForContext(w0.get()), desk0);
ASSERT_EQ(desks_util::GetDeskForContext(w1.get()), desk0);
OverviewController* overview_controller = OverviewController::Get();
overview_controller->StartOverview(OverviewStartAction::kOverviewButton);
ASSERT_TRUE(IsInOverviewSession());
PressAndReleaseKey(ui::VKEY_OEM_6, ui::EF_COMMAND_DOWN);
DeskSwitchAnimationWaiter().Wait();
ASSERT_TRUE(IsInOverviewSession());
EXPECT_TRUE(desk1->is_active());
PressAndReleaseKey(ui::VKEY_OEM_4, ui::EF_COMMAND_DOWN);
DeskSwitchAnimationWaiter().Wait();
ASSERT_TRUE(IsInOverviewSession());
EXPECT_TRUE(desk0->is_active());
auto* overview_group_item = GetOverviewItemForWindow(w0.get());
ASSERT_TRUE(overview_group_item);
SendKeyUntilOverviewItemIsFocused(ui::VKEY_TAB, event_generator);
PressAndReleaseKey(ui::VKEY_RETURN);
UnionBoundsEqualToWorkAreaBounds(w0.get(), w1.get(),
GetTopmostSnapGroupDivider());
}
TEST_F(SnapGroupDesksTest, DesksSwitchingThenMergingInOverview) {
auto* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
ASSERT_EQ(2u, desks_controller->desks().size());
const Desk* desk0 = desks_controller->GetDeskAtIndex(0);
const Desk* desk1 = desks_controller->GetDeskAtIndex(1);
ASSERT_TRUE(desk0->is_active());
std::unique_ptr<aura::Window> w0(CreateAppWindow());
std::unique_ptr<aura::Window> w1(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w0.get(), w1.get(), true, event_generator);
SnapGroup* snap_group =
SnapGroupController::Get()->GetSnapGroupForGivenWindow(w0.get());
ASSERT_TRUE(snap_group);
ASSERT_EQ(desk0, desks_util::GetDeskForContext(w0.get()));
ASSERT_EQ(desk0, desks_util::GetDeskForContext(w1.get()));
ToggleOverview();
WaitForOverviewEntered();
ASSERT_TRUE(IsInOverviewSession());
PressAndReleaseKey(ui::VKEY_OEM_6, ui::EF_COMMAND_DOWN);
DeskSwitchAnimationWaiter().Wait();
ASSERT_TRUE(IsInOverviewSession());
RemoveDesk(desk0, DeskCloseType::kCombineDesks);
ASSERT_TRUE(IsInOverviewSession());
auto* overview_group_item = GetOverviewItemForWindow(w0.get());
ASSERT_TRUE(overview_group_item);
EXPECT_EQ(1u, desks_controller->desks().size());
ASSERT_EQ(desk1, desks_util::GetDeskForContext(w0.get()));
ASSERT_EQ(desk1, desks_util::GetDeskForContext(w1.get()));
SendKeyUntilOverviewItemIsFocused(ui::VKEY_TAB, event_generator);
PressAndReleaseKey(ui::VKEY_RETURN);
UnionBoundsEqualToWorkAreaBounds(w0.get(), w1.get(),
GetTopmostSnapGroupDivider());
}
TEST_F(SnapGroupDesksTest,
DesksSwitchingThenMergingWithOneSnapGroupPerDeskInOverview) {
auto* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
ASSERT_EQ(2u, desks_controller->desks().size());
const Desk* desk0 = desks_controller->GetDeskAtIndex(0);
const Desk* desk1 = desks_controller->GetDeskAtIndex(1);
ASSERT_TRUE(desk0->is_active());
std::unique_ptr<aura::Window> w0(CreateAppWindow());
std::unique_ptr<aura::Window> w1(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w0.get(), w1.get(), true, event_generator);
SnapGroup* snap_group0 =
SnapGroupController::Get()->GetSnapGroupForGivenWindow(w0.get());
ASSERT_TRUE(snap_group0);
ASSERT_EQ(desk0, desks_util::GetDeskForContext(w0.get()));
ASSERT_EQ(desk0, desks_util::GetDeskForContext(w1.get()));
ActivateDesk(desk1);
std::unique_ptr<aura::Window> w2(CreateAppWindow(gfx::Rect(0, 0, 100, 100)));
std::unique_ptr<aura::Window> w3(
CreateAppWindow(gfx::Rect(200, 20, 100, 200)));
SnapTwoTestWindows(w2.get(), w3.get(), true, event_generator);
SnapGroup* snap_group1 =
SnapGroupController::Get()->GetSnapGroupForGivenWindow(w2.get());
ASSERT_TRUE(snap_group1);
ASSERT_EQ(desk1, desks_util::GetDeskForContext(w2.get()));
ASSERT_EQ(desk1, desks_util::GetDeskForContext(w3.get()));
ToggleOverview();
ASSERT_TRUE(IsInOverviewSession());
PressAndReleaseKey(ui::VKEY_OEM_4, ui::EF_COMMAND_DOWN);
DeskSwitchAnimationWaiter().Wait();
ASSERT_TRUE(IsInOverviewSession());
RemoveDesk(desk0, DeskCloseType::kCombineDesks);
ASSERT_TRUE(IsInOverviewSession());
auto* overview_group_item = GetOverviewItemForWindow(w0.get());
ASSERT_TRUE(overview_group_item);
EXPECT_EQ(1u, desks_controller->desks().size());
ASSERT_EQ(desk1, desks_util::GetDeskForContext(w0.get()));
ASSERT_EQ(desk1, desks_util::GetDeskForContext(w1.get()));
ASSERT_EQ(desk1, desks_util::GetDeskForContext(w2.get()));
ASSERT_EQ(desk1, desks_util::GetDeskForContext(w3.get()));
}
TEST_F(SnapGroupDesksTest, DeskSwitchingWithKeyboardShortcut) {
auto* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
ASSERT_EQ(2u, desks_controller->desks().size());
const Desk* desk0 = desks_controller->GetDeskAtIndex(0);
const Desk* desk1 = desks_controller->GetDeskAtIndex(1);
ASSERT_TRUE(desk0->is_active());
ASSERT_FALSE(desk1->is_active());
std::unique_ptr<aura::Window> w0(CreateAppWindow());
std::unique_ptr<aura::Window> w1(CreateAppWindow());
SnapTwoTestWindows(w0.get(), w1.get(), true,
GetEventGenerator());
SnapGroup* snap_group =
SnapGroupController::Get()->GetSnapGroupForGivenWindow(w0.get());
ASSERT_TRUE(snap_group);
ASSERT_EQ(desks_util::GetDeskForContext(w0.get()), desk0);
ASSERT_EQ(desks_util::GetDeskForContext(w1.get()), desk0);
PressAndReleaseKey(ui::VKEY_OEM_6, ui::EF_COMMAND_DOWN);
EXPECT_EQ(desks_util::GetDeskForContext(w0.get()), desk0);
EXPECT_EQ(desks_util::GetDeskForContext(w1.get()), desk0);
EXPECT_TRUE(w0->TargetVisibility());
EXPECT_TRUE(w1->TargetVisibility());
PressAndReleaseKey(ui::VKEY_OEM_4, ui::EF_COMMAND_DOWN);
EXPECT_EQ(desks_util::GetDeskForContext(w0.get()), desk0);
EXPECT_EQ(desks_util::GetDeskForContext(w1.get()), desk0);
EXPECT_TRUE(w0->TargetVisibility());
EXPECT_TRUE(w1->TargetVisibility());
}
TEST_F(SnapGroupDesksTest, ResizeThenMoveGroupToAnotherDesk) {
auto* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
ASSERT_EQ(2u, desks_controller->desks().size());
const Desk* desk0 = desks_controller->GetDeskAtIndex(0);
const Desk* desk1 = desks_controller->GetDeskAtIndex(1);
ASSERT_TRUE(desk0->is_active());
ASSERT_FALSE(desk1->is_active());
std::unique_ptr<aura::Window> w0(CreateAppWindow());
std::unique_ptr<aura::Window> w1(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w0.get(), w1.get(), true, event_generator);
SnapGroup* snap_group =
SnapGroupController::Get()->GetSnapGroupForGivenWindow(w0.get());
ASSERT_TRUE(snap_group);
ASSERT_EQ(desks_util::GetDeskForContext(w0.get()), desk0);
ASSERT_EQ(desks_util::GetDeskForContext(w1.get()), desk0);
auto* divider = GetTopmostSnapGroupDivider();
ASSERT_TRUE(divider);
auto* divider_widget = divider->divider_widget();
ASSERT_TRUE(divider_widget);
event_generator->MoveMouseTo(
divider_widget->GetWindowBoundsInScreen().CenterPoint());
event_generator->DragMouseBy(100, 0);
const gfx::Rect cached_w0_bounds(w0->GetBoundsInScreen());
const gfx::Rect cached_w1_bounds(w1->GetBoundsInScreen());
ASSERT_TRUE(EnterOverview(OverviewEnterExitType::kImmediateEnter));
auto* overview_grid = GetOverviewGridForRoot(w0->GetRootWindow());
ASSERT_TRUE(overview_grid);
const auto& item_list = overview_grid->item_list();
ASSERT_EQ(1u, item_list.size());
const auto* desks_bar_view = overview_grid->desks_bar_view();
ASSERT_TRUE(desks_bar_view);
const auto& mini_views = desks_bar_view->mini_views();
ASSERT_EQ(2u, mini_views.size());
OverviewController* overview_controller = Shell::Get()->overview_controller();
DragGroupItemToPoint(
overview_controller->overview_session()->GetOverviewItemForWindow(
w0.get()),
mini_views[1]->GetBoundsInScreen().CenterPoint(), event_generator,
false,
true);
EXPECT_TRUE(overview_controller->InOverviewSession());
ASSERT_EQ(desks_util::GetDeskForContext(w0.get()), desk1);
ASSERT_EQ(desks_util::GetDeskForContext(w1.get()), desk1);
EXPECT_TRUE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w0.get(), w1.get()));
ActivateDesk(desk1);
ASSERT_TRUE(divider_widget);
EXPECT_EQ(desks_util::GetDeskForContext(divider_widget->GetNativeWindow()),
desk1);
EXPECT_EQ(cached_w0_bounds, w0->GetBoundsInScreen());
EXPECT_EQ(cached_w1_bounds, w1->GetBoundsInScreen());
UnionBoundsEqualToWorkAreaBounds(w0.get(), w1.get(), divider);
}
TEST_F(SnapGroupDesksTest, CloseAll) {
auto* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
ASSERT_EQ(2u, desks_controller->desks().size());
const Desk* desk0 = desks_controller->GetDeskAtIndex(0);
ASSERT_TRUE(desk0->is_active());
std::unique_ptr<aura::Window> w0(CreateAppWindow());
std::unique_ptr<aura::Window> w1(CreateAppWindow());
SnapTwoTestWindows(w0.get(), w1.get(), true,
GetEventGenerator());
SnapGroup* snap_group =
SnapGroupController::Get()->GetSnapGroupForGivenWindow(w0.get());
ASSERT_TRUE(snap_group);
ASSERT_EQ(desks_util::GetDeskForContext(w0.get()), desk0);
ASSERT_EQ(desks_util::GetDeskForContext(w1.get()), desk0);
ToggleOverview();
ASSERT_TRUE(IsInOverviewSession());
auto* window_widget0 = views::Widget::GetWidgetForNativeView(w0.get());
views::test::TestWidgetObserver observer0(window_widget0);
auto* window_widget1 = views::Widget::GetWidgetForNativeView(w1.get());
views::test::TestWidgetObserver observer1(window_widget1);
w0.release();
w1.release();
RemoveDesk(desk0, DeskCloseType::kCloseAllWindows);
EXPECT_EQ(1u, desks_controller->desks().size());
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(observer0.widget_closed());
EXPECT_TRUE(observer1.widget_closed());
}
TEST_F(SnapGroupDesksTest, DeskRemovalAndUndo) {
auto* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
ASSERT_EQ(2u, desks_controller->desks().size());
const Desk* desk0 = desks_controller->GetDeskAtIndex(0);
const Desk* desk1 = desks_controller->GetDeskAtIndex(1);
ASSERT_TRUE(desk0->is_active());
ASSERT_FALSE(desk1->is_active());
std::unique_ptr<aura::Window> w0(CreateAppWindow());
std::unique_ptr<aura::Window> w1(CreateAppWindow());
SnapTwoTestWindows(w0.get(), w1.get(), true,
GetEventGenerator());
SnapGroup* snap_group =
SnapGroupController::Get()->GetSnapGroupForGivenWindow(w0.get());
ASSERT_TRUE(snap_group);
ASSERT_EQ(desk0, desks_util::GetDeskForContext(w0.get()));
ASSERT_EQ(desk0, desks_util::GetDeskForContext(w1.get()));
ToggleOverview();
ASSERT_TRUE(IsInOverviewSession());
RemoveDesk(desk0, DeskCloseType::kCloseAllWindowsAndWait);
ASSERT_TRUE(desk0->is_desk_being_removed());
EXPECT_FALSE(w0->IsVisible());
EXPECT_FALSE(w1->IsVisible());
views::LabelButton* dismiss_button =
DesksTestApi::GetCloseAllUndoToastDismissButton();
ASSERT_TRUE(dismiss_button);
LeftClickOn(dismiss_button);
EXPECT_TRUE(w0->IsVisible());
EXPECT_TRUE(w1->IsVisible());
EXPECT_EQ(desk0, desks_util::GetDeskForContext(w0.get()));
EXPECT_EQ(desk0, desks_util::GetDeskForContext(w1.get()));
}
TEST_F(SnapGroupDesksTest, DeskRemovalAndEnterOverview) {
auto* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
ASSERT_EQ(2u, desks_controller->desks().size());
const Desk* desk0 = desks_controller->GetDeskAtIndex(0);
const Desk* desk1 = desks_controller->GetDeskAtIndex(1);
ASSERT_TRUE(desk0->is_active());
ASSERT_FALSE(desk1->is_active());
std::unique_ptr<aura::Window> w0(CreateAppWindow());
std::unique_ptr<aura::Window> w1(CreateAppWindow());
SnapTwoTestWindows(w0.get(), w1.get(), true,
GetEventGenerator());
SnapGroup* snap_group =
SnapGroupController::Get()->GetSnapGroupForGivenWindow(w0.get());
ASSERT_TRUE(snap_group);
ASSERT_EQ(desk0, desks_util::GetDeskForContext(w0.get()));
ASSERT_EQ(desk0, desks_util::GetDeskForContext(w1.get()));
DeskSwitchAnimationWaiter waiter;
PressAndReleaseKey(ui::VKEY_OEM_MINUS,
ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN);
waiter.Wait();
EXPECT_TRUE(desk1->is_active());
EXPECT_EQ(1u, desks_controller->desks().size());
EXPECT_EQ(desk1, desks_util::GetDeskForContext(w0.get()));
EXPECT_EQ(desk1, desks_util::GetDeskForContext(w1.get()));
EXPECT_TRUE(snap_group);
auto* divider = snap_group->snap_group_divider();
auto* divider_widget = divider->divider_widget();
EXPECT_TRUE(divider_widget);
EXPECT_TRUE(divider_widget->IsVisible());
UnionBoundsEqualToWorkAreaBounds(w0.get(), w1.get(), divider);
ToggleOverview();
ASSERT_TRUE(IsInOverviewSession());
EXPECT_FALSE(divider_widget->IsVisible());
ToggleOverview();
ASSERT_FALSE(IsInOverviewSession());
EXPECT_TRUE(divider_widget->IsVisible());
UnionBoundsEqualToWorkAreaBounds(w0.get(), w1.get(), divider);
}
TEST_F(SnapGroupDesksTest, DeskRemovalWithOneAciveDesk) {
auto* desks_controller = DesksController::Get();
ASSERT_EQ(1u, desks_controller->desks().size());
const Desk* desk0 = desks_controller->GetDeskAtIndex(0);
std::unique_ptr<aura::Window> w0(CreateAppWindow());
std::unique_ptr<aura::Window> w1(CreateAppWindow());
SnapTwoTestWindows(w0.get(), w1.get(), true,
GetEventGenerator());
SnapGroup* snap_group =
SnapGroupController::Get()->GetSnapGroupForGivenWindow(w0.get());
ASSERT_TRUE(snap_group);
ASSERT_EQ(desk0, desks_util::GetDeskForContext(w0.get()));
ASSERT_EQ(desk0, desks_util::GetDeskForContext(w1.get()));
PressAndReleaseKey(ui::VKEY_OEM_MINUS,
ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN);
EXPECT_EQ(1u, desks_controller->desks().size());
EXPECT_EQ(desk0, desks_util::GetDeskForContext(w0.get()));
EXPECT_EQ(desk0, desks_util::GetDeskForContext(w1.get()));
EXPECT_TRUE(snap_group);
auto* divider = snap_group->snap_group_divider();
auto* divider_widget = divider->divider_widget();
EXPECT_TRUE(divider_widget);
EXPECT_TRUE(divider_widget->IsVisible());
UnionBoundsEqualToWorkAreaBounds(w0.get(), w1.get(), divider);
}
TEST_F(SnapGroupDesksTest, DesksMerge) {
auto* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
ASSERT_EQ(2u, desks_controller->desks().size());
const Desk* desk0 = desks_controller->GetDeskAtIndex(0);
const Desk* desk1 = desks_controller->GetDeskAtIndex(1);
ActivateDesk(desk1);
ASSERT_TRUE(desk1->is_active());
ASSERT_FALSE(desk0->is_active());
std::unique_ptr<aura::Window> w0(CreateAppWindow());
std::unique_ptr<aura::Window> w1(CreateAppWindow());
SnapTwoTestWindows(w0.get(), w1.get(), true,
GetEventGenerator());
SnapGroup* snap_group =
SnapGroupController::Get()->GetSnapGroupForGivenWindow(w0.get());
ASSERT_TRUE(snap_group);
ASSERT_EQ(desk1, desks_util::GetDeskForContext(w0.get()));
ASSERT_EQ(desk1, desks_util::GetDeskForContext(w1.get()));
ToggleOverview();
ASSERT_TRUE(IsInOverviewSession());
RemoveDesk(desk1, DeskCloseType::kCombineDesks);
EXPECT_EQ(1u, desks_controller->desks().size());
ASSERT_EQ(desk0, desks_util::GetDeskForContext(w0.get()));
ASSERT_EQ(desk0, desks_util::GetDeskForContext(w1.get()));
}
TEST_F(SnapGroupDesksTest, OneSnapGroupOnEachDesk) {
auto* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
ASSERT_EQ(2u, desks_controller->desks().size());
const Desk* desk0 = desks_controller->GetDeskAtIndex(0);
const Desk* desk1 = desks_controller->GetDeskAtIndex(1);
ASSERT_TRUE(desk0->is_active());
ASSERT_FALSE(desk1->is_active());
std::unique_ptr<aura::Window> w0(CreateAppWindow());
std::unique_ptr<aura::Window> w1(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w0.get(), w1.get(), true, event_generator);
SnapGroup* snap_group0 =
SnapGroupController::Get()->GetSnapGroupForGivenWindow(w0.get());
ASSERT_TRUE(snap_group0);
ASSERT_EQ(desk0, desks_util::GetDeskForContext(w0.get()));
ASSERT_EQ(desk0, desks_util::GetDeskForContext(w1.get()));
ActivateDesk(desk1);
ASSERT_TRUE(desk1->is_active());
ASSERT_FALSE(desk0->is_active());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
std::unique_ptr<aura::Window> w3(CreateAppWindow());
SnapTwoTestWindows(w2.get(), w3.get(), true, event_generator);
SnapGroup* snap_group1 =
SnapGroupController::Get()->GetSnapGroupForGivenWindow(w0.get());
ASSERT_TRUE(snap_group1);
ASSERT_EQ(desk1, desks_util::GetDeskForContext(w2.get()));
ASSERT_EQ(desk1, desks_util::GetDeskForContext(w3.get()));
ActivateDesk(desk0);
EXPECT_TRUE(w0->IsVisible());
EXPECT_TRUE(w1->IsVisible());
ActivateDesk(desk1);
EXPECT_TRUE(w2->IsVisible());
EXPECT_TRUE(w3->IsVisible());
}
TEST_F(SnapGroupDesksTest, OnlyHideSnapGroupOnActiveDesk) {
auto* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
ASSERT_EQ(2u, desks_controller->desks().size());
const Desk* desk0 = desks_controller->GetDeskAtIndex(0);
const Desk* desk1 = desks_controller->GetDeskAtIndex(1);
ASSERT_TRUE(desk0->is_active());
ASSERT_FALSE(desk1->is_active());
std::unique_ptr<aura::Window> w0(CreateAppWindow());
std::unique_ptr<aura::Window> w1(CreateAppWindow());
SnapTwoTestWindows(w0.get(), w1.get(), true,
GetEventGenerator());
SnapGroup* snap_group0 =
SnapGroupController::Get()->GetSnapGroupForGivenWindow(w0.get());
ASSERT_TRUE(snap_group0);
ASSERT_EQ(desk0, desks_util::GetDeskForContext(w0.get()));
ASSERT_EQ(desk0, desks_util::GetDeskForContext(w1.get()));
ActivateDesk(desk1);
ASSERT_TRUE(desk1->is_active());
ASSERT_FALSE(desk0->is_active());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
std::unique_ptr<aura::Window> w3(CreateAppWindow());
SnapOneTestWindow(w2.get(), WindowStateType::kPrimarySnapped,
chromeos::kTwoThirdSnapRatio);
VerifySplitViewOverviewSession(w2.get());
EXPECT_TRUE(w0->TargetVisibility());
EXPECT_TRUE(w1->TargetVisibility());
}
TEST_F(SnapGroupDesksTest, SaveDeskForSnapGroupWithAnotherSavedDesk) {
saved_desk_test_helper()->WaitForDeskModels();
OverviewController* overview_controller = OverviewController::Get();
base::AutoReset<bool> disable_app_id_check =
overview_controller->SetDisableAppIdCheckForTests();
CreateAppWindow(gfx::Rect(500, 300)).release();
overview_controller->StartOverview(OverviewStartAction::kOverviewButton);
views::MenuItemView* menu_item =
DesksTestApi::OpenDeskContextMenuAndGetMenuItem(
Shell::GetPrimaryRootWindow(), DeskBarViewBase::Type::kOverview,
0u, DeskActionContextMenu::CommandId::kSaveForLater);
LeftClickOn(menu_item);
WaitForSavedDeskUI();
WaitForSavedDeskUI();
ASSERT_TRUE(GetLibraryButton());
overview_controller->EndOverview(OverviewEndAction::kOverviewButton);
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
overview_controller->StartOverview(OverviewStartAction::kOverviewButton);
auto* group_item = GetOverviewItemForWindow(w1.get());
ASSERT_TRUE(group_item);
ASSERT_FALSE(GetTopmostSnapGroupDivider()->divider_widget()->IsVisible());
const gfx::RectF cached_group_item_bounds = group_item->target_bounds();
auto* library_button = GetLibraryButton();
LeftClickOn(library_button);
const gfx::Point click_point = gfx::ToRoundedPoint(
cached_group_item_bounds.bottom_right() + gfx::Vector2d(20, 0));
event_generator->MoveMouseTo(click_point);
event_generator->ClickLeftButton();
EXPECT_FALSE(IsInOverviewSession());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
}
TEST_F(SnapGroupDesksTest, MoveToAllDesksToBreakTheGroup) {
auto* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
ASSERT_EQ(2u, desks_controller->desks().size());
const Desk* desk0 = desks_controller->GetDeskAtIndex(0);
const Desk* desk1 = desks_controller->GetDeskAtIndex(1);
ASSERT_TRUE(desk0->is_active());
ASSERT_FALSE(desk1->is_active());
std::unique_ptr<aura::Window> w0(CreateAppWindow());
auto* window_widget0 = views::Widget::GetWidgetForNativeView(w0.get());
std::unique_ptr<aura::Window> w1(CreateAppWindow());
SnapTwoTestWindows(w0.get(), w1.get(), true,
GetEventGenerator());
SnapGroup* snap_group =
SnapGroupController::Get()->GetSnapGroupForGivenWindow(w0.get());
ASSERT_TRUE(snap_group);
ASSERT_EQ(desk0, desks_util::GetDeskForContext(w0.get()));
ASSERT_EQ(desk0, desks_util::GetDeskForContext(w1.get()));
window_widget0->SetVisibleOnAllWorkspaces(true);
SnapGroupController* snap_group_controller = SnapGroupController::Get();
EXPECT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w0.get(), w1.get()));
EXPECT_FALSE(snap_group_controller->GetSnapGroupForGivenWindow(w0.get()));
EXPECT_FALSE(snap_group_controller->GetSnapGroupForGivenWindow(w1.get()));
}
TEST_F(SnapGroupDesksTest, DeskRemovalAfterMovingSnapGroupToAllDesks) {
auto* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
ASSERT_EQ(2u, desks_controller->desks().size());
const Desk* desk0 = desks_controller->GetDeskAtIndex(0);
const Desk* desk1 = desks_controller->GetDeskAtIndex(1);
ASSERT_TRUE(desk0->is_active());
ASSERT_FALSE(desk1->is_active());
std::unique_ptr<aura::Window> w0(CreateAppWindow());
auto* window_widget0 = views::Widget::GetWidgetForNativeView(w0.get());
std::unique_ptr<aura::Window> w1(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w0.get(), w1.get(), true, event_generator);
SnapGroupController* snap_group_controller = SnapGroupController::Get();
SnapGroup* snap_group0 =
snap_group_controller->GetSnapGroupForGivenWindow(w0.get());
ASSERT_TRUE(snap_group0);
ASSERT_EQ(desk0, desks_util::GetDeskForContext(w0.get()));
ASSERT_EQ(desk0, desks_util::GetDeskForContext(w1.get()));
ActivateDesk(desk1);
ASSERT_TRUE(desk1->is_active());
ASSERT_FALSE(desk0->is_active());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
std::unique_ptr<aura::Window> w3(CreateAppWindow());
SnapTwoTestWindows(w2.get(), w3.get(), true, event_generator);
SnapGroup* snap_group1 =
snap_group_controller->GetSnapGroupForGivenWindow(w0.get());
ASSERT_TRUE(snap_group1);
ASSERT_EQ(desk1, desks_util::GetDeskForContext(w2.get()));
ASSERT_EQ(desk1, desks_util::GetDeskForContext(w3.get()));
ActivateDesk(desk0);
window_widget0->SetVisibleOnAllWorkspaces(true);
EXPECT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w0.get(), w1.get()));
w0.release();
w1.release();
ToggleOverview();
ASSERT_TRUE(IsInOverviewSession());
RemoveDesk(desk0, DeskCloseType::kCloseAllWindowsAndWait);
ASSERT_TRUE(desk0->is_desk_being_removed());
base::RunLoop().RunUntilIdle();
}
class SnapGroupWindowCycleTest : public SnapGroupTest {
public:
SnapGroupWindowCycleTest() {
WindowCycleList::SetDisableInitialDelayForTesting(true);
}
~SnapGroupWindowCycleTest() override = default;
void AltTabNTimes(int n) {
WindowCycleController* window_cycle_controller =
Shell::Get()->window_cycle_controller();
auto* event_generator = GetEventGenerator();
for (int i = 0; i < n; i++) {
event_generator->PressKey(ui::VKEY_TAB, ui::EF_ALT_DOWN);
event_generator->ReleaseKey(ui::VKEY_TAB, ui::EF_ALT_DOWN);
EXPECT_TRUE(window_cycle_controller->IsCycling());
}
event_generator->ReleaseKey(ui::VKEY_MENU, ui::EF_NONE);
EXPECT_FALSE(window_cycle_controller->IsCycling());
}
};
TEST_F(SnapGroupWindowCycleTest, WindowReorderInAltTabInPrimaryOrientation) {
std::unique_ptr<aura::Window> window0(
CreateTestWindowInShell({.window_id = 0}));
window0->SetProperty(chromeos::kAppTypeKey, chromeos::AppType::CHROME_APP);
std::unique_ptr<aura::Window> window1(
CreateTestWindowInShell({.window_id = 1}));
window1->SetProperty(chromeos::kAppTypeKey, chromeos::AppType::CHROME_APP);
std::unique_ptr<aura::Window> window2(
CreateTestWindowInShell({.window_id = 2}));
SnapTwoTestWindows(window0.get(), window1.get(), true,
GetEventGenerator());
wm::ActivateWindow(window2.get());
ASSERT_TRUE(wm::IsActiveWindow(window2.get()));
WindowCycleController* window_cycle_controller =
Shell::Get()->window_cycle_controller();
CycleWindow(WindowCyclingDirection::kForward, 1);
const auto& windows =
window_cycle_controller->window_cycle_list()->windows_for_testing();
ASSERT_EQ(windows.size(), 3u);
EXPECT_EQ(windows.at(0), window2.get());
EXPECT_EQ(windows.at(1), window0.get());
EXPECT_EQ(windows.at(2), window1.get());
CompleteWindowCycling();
EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
CycleWindow(WindowCyclingDirection::kForward, 2);
CompleteWindowCycling();
EXPECT_TRUE(wm::IsActiveWindow(window2.get()));
}
TEST_F(SnapGroupWindowCycleTest, WindowCycleViewTest) {
std::unique_ptr<aura::Window> window0(
CreateTestWindowInShell({.window_id = 0}));
window0->SetProperty(chromeos::kAppTypeKey, chromeos::AppType::CHROME_APP);
std::unique_ptr<aura::Window> window1(
CreateTestWindowInShell({.window_id = 1}));
window1->SetProperty(chromeos::kAppTypeKey, chromeos::AppType::CHROME_APP);
std::unique_ptr<aura::Window> window2(
CreateTestWindowInShell({.window_id = 2}));
SnapTwoTestWindows(window0.get(), window1.get(), true,
GetEventGenerator());
WindowCycleController* window_cycle_controller =
Shell::Get()->window_cycle_controller();
CycleWindow(WindowCyclingDirection::kForward, 3);
const auto* window_cycle_list = window_cycle_controller->window_cycle_list();
const auto& windows = window_cycle_list->windows_for_testing();
EXPECT_EQ(windows.size(), 3u);
const WindowCycleView* cycle_view = window_cycle_list->cycle_view();
ASSERT_TRUE(cycle_view);
EXPECT_EQ(cycle_view->mirror_container_for_testing()->children().size(), 2u);
CompleteWindowCycling();
}
TEST_F(SnapGroupWindowCycleTest, WindowInSnapGroupDestructionInAltTab) {
std::unique_ptr<aura::Window> window0(
CreateTestWindowInShell({.window_id = 0}));
window0->SetProperty(chromeos::kAppTypeKey, chromeos::AppType::CHROME_APP);
std::unique_ptr<aura::Window> window1(
CreateTestWindowInShell({.window_id = 1}));
window1->SetProperty(chromeos::kAppTypeKey, chromeos::AppType::CHROME_APP);
std::unique_ptr<aura::Window> window2(
CreateTestWindowInShell({.window_id = 2}));
SnapTwoTestWindows(window0.get(), window1.get(), true,
GetEventGenerator());
WindowCycleController* window_cycle_controller =
Shell::Get()->window_cycle_controller();
CycleWindow(WindowCyclingDirection::kForward, 3);
const auto* window_cycle_list = window_cycle_controller->window_cycle_list();
const auto& windows = window_cycle_list->windows_for_testing();
EXPECT_EQ(windows.size(), 3u);
const WindowCycleView* cycle_view = window_cycle_list->cycle_view();
ASSERT_TRUE(cycle_view);
EXPECT_EQ(cycle_view->mirror_container_for_testing()->children().size(), 2u);
window0.reset();
EXPECT_TRUE(window_cycle_controller->IsCycling());
const auto* updated_window_cycle_list =
window_cycle_controller->window_cycle_list();
const auto& updated_windows =
updated_window_cycle_list->windows_for_testing();
EXPECT_EQ(updated_windows.size(), 2u);
EXPECT_EQ(cycle_view->mirror_container_for_testing()->children().size(), 2u);
}
TEST_F(SnapGroupWindowCycleTest, SteppingInWindowCycleView) {
std::unique_ptr<aura::Window> window3 =
CreateAppWindow(gfx::Rect(300, 300), chromeos::AppType::CHROME_APP);
std::unique_ptr<aura::Window> window2 =
CreateAppWindow(gfx::Rect(200, 200), chromeos::AppType::CHROME_APP);
std::unique_ptr<aura::Window> window1 =
CreateAppWindow(gfx::Rect(100, 100), chromeos::AppType::BROWSER);
std::unique_ptr<aura::Window> window0 =
CreateAppWindow(gfx::Rect(10, 10), chromeos::AppType::BROWSER);
SnapTwoTestWindows(window0.get(), window1.get(), true,
GetEventGenerator());
EXPECT_TRUE(wm::IsActiveWindow(window1.get()));
WindowState::Get(window3.get())->Activate();
EXPECT_TRUE(wm::IsActiveWindow(window3.get()));
CycleWindow(WindowCyclingDirection::kForward, 2);
CompleteWindowCycling();
EXPECT_TRUE(wm::IsActiveWindow(window1.get()));
CycleWindow(WindowCyclingDirection::kForward, 1);
CompleteWindowCycling();
EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
CycleWindow(WindowCyclingDirection::kForward, 3);
CompleteWindowCycling();
EXPECT_TRUE(wm::IsActiveWindow(window2.get()));
CycleWindow(WindowCyclingDirection::kBackward, 1);
CompleteWindowCycling();
EXPECT_TRUE(wm::IsActiveWindow(window3.get()));
}
TEST_F(SnapGroupWindowCycleTest, QuickSwitch) {
WindowCycleList::SetDisableInitialDelayForTesting(false);
std::unique_ptr<aura::Window> w0(CreateAppWindow());
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w0.get(), w1.get(), true,
GetEventGenerator());
EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
WindowCycleController* window_cycle_controller =
Shell::Get()->window_cycle_controller();
auto* event_generator = GetEventGenerator();
event_generator->PressKey(ui::VKEY_TAB, ui::EF_ALT_DOWN);
event_generator->ReleaseKey(ui::VKEY_TAB, ui::EF_ALT_DOWN);
EXPECT_TRUE(window_cycle_controller->IsCycling());
const auto* window_cycle_list0 = window_cycle_controller->window_cycle_list();
ASSERT_TRUE(window_cycle_list0);
EXPECT_FALSE(window_cycle_list0->cycle_view());
event_generator->ReleaseKey(ui::VKEY_MENU, ui::EF_NONE);
EXPECT_TRUE(wm::IsActiveWindow(w0.get()));
event_generator->PressKey(ui::VKEY_TAB, ui::EF_ALT_DOWN);
event_generator->ReleaseKey(ui::VKEY_TAB, ui::EF_ALT_DOWN);
EXPECT_TRUE(window_cycle_controller->IsCycling());
const auto* window_cycle_list1 = window_cycle_controller->window_cycle_list();
ASSERT_TRUE(window_cycle_list1);
EXPECT_FALSE(window_cycle_list1->cycle_view());
event_generator->ReleaseKey(ui::VKEY_MENU, ui::EF_NONE);
EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
}
TEST_F(SnapGroupWindowCycleTest, AllDesksWindowCycling) {
auto* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
ASSERT_EQ(2u, desks_controller->desks().size());
const Desk* desk0 = desks_controller->GetDeskAtIndex(0);
const Desk* desk1 = desks_controller->GetDeskAtIndex(1);
ASSERT_TRUE(desk0->is_active());
std::unique_ptr<aura::Window> w0(CreateAppWindow());
std::unique_ptr<aura::Window> w1(CreateAppWindow());
SnapTwoTestWindows(w0.get(), w1.get(), true,
GetEventGenerator());
SnapGroup* snap_group1 =
SnapGroupController::Get()->GetSnapGroupForGivenWindow(w0.get());
ASSERT_TRUE(snap_group1);
ASSERT_EQ(desks_util::GetDeskForContext(w0.get()), desk0);
ASSERT_EQ(desks_util::GetDeskForContext(w1.get()), desk0);
ActivateDesk(desk1);
ASSERT_TRUE(desk1->is_active());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
std::unique_ptr<aura::Window> w3(CreateAppWindow());
SnapTwoTestWindows(w2.get(), w3.get(), true,
GetEventGenerator());
SnapGroup* snap_group2 =
SnapGroupController::Get()->GetSnapGroupForGivenWindow(w0.get());
ASSERT_TRUE(snap_group2);
ASSERT_EQ(desks_util::GetDeskForContext(w2.get()), desk1);
ASSERT_EQ(desks_util::GetDeskForContext(w3.get()), desk1);
EXPECT_TRUE(wm::IsActiveWindow(w3.get()));
AltTabNTimes(3);
EXPECT_TRUE(wm::IsActiveWindow(w0.get()));
DeskSwitchAnimationWaiter().Wait();
EXPECT_TRUE(desk0->is_active());
AltTabNTimes(2);
EXPECT_TRUE(wm::IsActiveWindow(w2.get()));
DeskSwitchAnimationWaiter().Wait();
EXPECT_TRUE(desk1->is_active());
}
TEST_F(SnapGroupWindowCycleTest, PerDeskWindowCycling) {
PrefService* active_user_prefs =
Shell::Get()->session_controller()->GetActivePrefService();
DCHECK(active_user_prefs);
active_user_prefs->SetBoolean(prefs::kAltTabPerDesk, true);
auto* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
ASSERT_EQ(2u, desks_controller->desks().size());
const Desk* desk0 = desks_controller->GetDeskAtIndex(0);
const Desk* desk1 = desks_controller->GetDeskAtIndex(1);
ASSERT_TRUE(desk0->is_active());
std::unique_ptr<aura::Window> w0(CreateAppWindow());
std::unique_ptr<aura::Window> w1(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w0.get(), w1.get(), true, event_generator);
SnapGroup* snap_group1 =
SnapGroupController::Get()->GetSnapGroupForGivenWindow(w0.get());
ASSERT_TRUE(snap_group1);
ASSERT_EQ(desks_util::GetDeskForContext(w0.get()), desk0);
ASSERT_EQ(desks_util::GetDeskForContext(w1.get()), desk0);
ActivateDesk(desk1);
ASSERT_TRUE(desk1->is_active());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
std::unique_ptr<aura::Window> w3(CreateAppWindow());
SnapTwoTestWindows(w2.get(), w3.get(), true, event_generator);
SnapGroup* snap_group2 =
SnapGroupController::Get()->GetSnapGroupForGivenWindow(w0.get());
ASSERT_TRUE(snap_group2);
ASSERT_EQ(desks_util::GetDeskForContext(w2.get()), desk1);
ASSERT_EQ(desks_util::GetDeskForContext(w3.get()), desk1);
EXPECT_TRUE(wm::IsActiveWindow(w3.get()));
AltTabNTimes(3);
EXPECT_TRUE(wm::IsActiveWindow(w2.get()));
EXPECT_FALSE(desks_controller->AreDesksBeingModified());
ActivateDesk(desk0);
ASSERT_TRUE(desk0->is_active());
AltTabNTimes(5);
EXPECT_TRUE(wm::IsActiveWindow(w0.get()));
EXPECT_FALSE(desks_controller->AreDesksBeingModified());
}
TEST_F(SnapGroupWindowCycleTest, WindowCycleItemRoundedCorners) {
std::unique_ptr<aura::Window> window0 =
CreateAppWindow(gfx::Rect(100, 200), chromeos::AppType::BROWSER);
std::unique_ptr<aura::Window> window1 =
CreateAppWindow(gfx::Rect(200, 300), chromeos::AppType::BROWSER);
std::unique_ptr<aura::Window> window2 =
CreateAppWindow(gfx::Rect(300, 400), chromeos::AppType::BROWSER);
SnapTwoTestWindows(window0.get(), window1.get(), true,
GetEventGenerator());
WindowCycleController* window_cycle_controller =
Shell::Get()->window_cycle_controller();
CycleWindow(WindowCyclingDirection::kForward, 3);
EXPECT_TRUE(window_cycle_controller->IsCycling());
const auto* window_cycle_list = window_cycle_controller->window_cycle_list();
const auto* cycle_view = window_cycle_list->cycle_view();
auto& cycle_item_views = cycle_view->cycle_views_for_testing();
ASSERT_EQ(cycle_item_views.size(), 2u);
for (ash::WindowMiniViewBase* cycle_item_view : cycle_item_views) {
EXPECT_EQ(cycle_item_view->GetRoundedCorners(),
gfx::RoundedCornersF(kWindowMiniViewCornerRadius));
}
window0.reset();
auto& new_cycle_item_views = cycle_view->cycle_views_for_testing();
EXPECT_EQ(new_cycle_item_views.size(), 2u);
for (ash::WindowMiniViewBase* cycle_item_view : new_cycle_item_views) {
EXPECT_EQ(cycle_item_view->GetRoundedCorners(),
gfx::RoundedCornersF(kWindowMiniViewCornerRadius));
}
CompleteWindowCycling();
}
TEST_F(SnapGroupWindowCycleTest, WindowCycleItemRoundedCornersInPortait) {
UpdateDisplay("600x900");
std::unique_ptr<aura::Window> window0 =
CreateAppWindow(gfx::Rect(100, 200), chromeos::AppType::BROWSER);
std::unique_ptr<aura::Window> window1 =
CreateAppWindow(gfx::Rect(200, 300), chromeos::AppType::BROWSER);
std::unique_ptr<aura::Window> window2 =
CreateAppWindow(gfx::Rect(300, 400), chromeos::AppType::BROWSER);
SnapTwoTestWindows(window0.get(), window1.get(), false,
GetEventGenerator());
WindowCycleController* window_cycle_controller =
Shell::Get()->window_cycle_controller();
CycleWindow(WindowCyclingDirection::kForward, 3);
EXPECT_TRUE(window_cycle_controller->IsCycling());
const auto* window_cycle_list = window_cycle_controller->window_cycle_list();
const auto* cycle_view = window_cycle_list->cycle_view();
auto& cycle_item_views = cycle_view->cycle_views_for_testing();
ASSERT_EQ(cycle_item_views.size(), 2u);
for (ash::WindowMiniViewBase* cycle_item_view : cycle_item_views) {
EXPECT_EQ(cycle_item_view->GetRoundedCorners(),
gfx::RoundedCornersF(kWindowMiniViewCornerRadius));
}
}
TEST_F(SnapGroupWindowCycleTest,
WindowCycleItemForNonPrimaryScreenOrientation) {
UpdateDisplay("1200x900/r");
display::DisplayManager* display_manager = Shell::Get()->display_manager();
const auto& displays = display_manager->active_display_list();
ASSERT_EQ(1U, displays.size());
ASSERT_EQ(chromeos::OrientationType::kPortraitSecondary,
chromeos::GetDisplayCurrentOrientation(displays[0]));
std::unique_ptr<aura::Window> window2 =
CreateAppWindow(gfx::Rect(200, 200), chromeos::AppType::CHROME_APP);
std::unique_ptr<aura::Window> window1 =
CreateAppWindow(gfx::Rect(100, 100), chromeos::AppType::BROWSER);
std::unique_ptr<aura::Window> window0 =
CreateAppWindow(gfx::Rect(10, 10), chromeos::AppType::BROWSER);
SnapOneTestWindow(window0.get(), WindowStateType::kSecondarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kDragWindowToEdgeToSnap);
ASSERT_TRUE(IsInOverviewSession());
SendKeyUntilOverviewItemIsFocused(ui::VKEY_TAB, GetEventGenerator());
GetEventGenerator()->PressKey(ui::VKEY_RETURN, 0);
ASSERT_TRUE(SnapGroupController::Get()->AreWindowsInSnapGroup(window0.get(),
window1.get()));
gfx::Rect work_area = GetWorkAreaBoundsForWindow(window0.get());
const gfx::Rect divider_bounds =
GetTopmostSnapGroupDivider()->divider_widget()->GetWindowBoundsInScreen();
EXPECT_EQ(gfx::Rect(work_area.x(), work_area.y(), work_area.width(),
divider_bounds.y()),
window0->GetBoundsInScreen());
EXPECT_EQ(gfx::Rect(work_area.x(), divider_bounds.bottom(), work_area.width(),
work_area.height() - divider_bounds.bottom()),
window1->GetBoundsInScreen());
EXPECT_TRUE(wm::IsActiveWindow(window1.get()));
std::unique_ptr<aura::Window> window3 =
CreateAppWindow(gfx::Rect(300, 300), chromeos::AppType::CHROME_APP);
EXPECT_TRUE(wm::IsActiveWindow(window3.get()));
CycleWindow(WindowCyclingDirection::kForward, 2);
CompleteWindowCycling();
EXPECT_TRUE(wm::IsActiveWindow(window1.get()));
CycleWindow(WindowCyclingDirection::kForward, 1);
CompleteWindowCycling();
EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
CycleWindow(WindowCyclingDirection::kForward, 3);
CompleteWindowCycling();
EXPECT_TRUE(wm::IsActiveWindow(window2.get()));
CycleWindow(WindowCyclingDirection::kBackward, 1);
CompleteWindowCycling();
EXPECT_TRUE(wm::IsActiveWindow(window3.get()));
}
TEST_F(SnapGroupWindowCycleTest, SameAppWindowCycle) {
struct app_id_pair {
const char* trace_message;
const std::string app_id_2;
const std::string app_id_3;
const size_t windows_size;
const size_t cycle_views_count;
} kTestCases[]{
{"Windows in snap group with same app id",
"A", "A", 4u,
3u},
{"Windows in snap group with different app ids",
"A", "B", 3u,
3u},
};
std::unique_ptr<aura::Window> w0(CreateTestWindowWithAppID(std::string("A")));
std::unique_ptr<aura::Window> w1(CreateTestWindowWithAppID(std::string("A")));
std::unique_ptr<aura::Window> w2(CreateTestWindowWithAppID(std::string("A")));
std::unique_ptr<aura::Window> w3(CreateTestWindowWithAppID(std::string("A")));
SnapTwoTestWindows(w2.get(), w3.get(), true,
GetEventGenerator());
WindowCycleController* window_cycle_controller =
Shell::Get()->window_cycle_controller();
for (const auto& test_case : kTestCases) {
w2->SetProperty(kAppIDKey, std::move(test_case.app_id_2));
w3->SetProperty(kAppIDKey, std::move(test_case.app_id_3));
wm::ActivateWindow(w2.get());
ASSERT_TRUE(wm::IsActiveWindow(w2.get()));
auto* event_generator = GetEventGenerator();
event_generator->PressKey(ui::VKEY_MENU, ui::EF_NONE);
event_generator->PressAndReleaseKey(ui::VKEY_OEM_3, ui::EF_ALT_DOWN);
const auto* window_cycle_list =
window_cycle_controller->window_cycle_list();
ASSERT_TRUE(window_cycle_list->same_app_only());
const auto& windows = window_cycle_list->windows_for_testing();
EXPECT_EQ(windows.size(), test_case.windows_size);
EXPECT_TRUE(window_cycle_controller->IsCycling());
const auto* cycle_view = window_cycle_list->cycle_view();
ASSERT_TRUE(cycle_view);
auto& cycle_item_views = cycle_view->cycle_views_for_testing();
EXPECT_EQ(cycle_item_views.size(), test_case.cycle_views_count);
event_generator->ReleaseKey(ui::VKEY_MENU, ui::EF_NONE);
}
}
TEST_F(SnapGroupWindowCycleTest, WindowDestructionDuringSameAppWindowCycle) {
std::unique_ptr<aura::Window> w0(CreateTestWindowWithAppID(std::string("A")));
std::unique_ptr<aura::Window> w1(CreateTestWindowWithAppID(std::string("A")));
std::unique_ptr<aura::Window> w2(CreateTestWindowWithAppID(std::string("A")));
SnapTwoTestWindows(w0.get(), w1.get(), true,
GetEventGenerator());
auto* event_generator = GetEventGenerator();
event_generator->PressKey(ui::VKEY_MENU, ui::EF_NONE);
event_generator->PressAndReleaseKey(ui::VKEY_OEM_3, ui::EF_ALT_DOWN);
WindowCycleController* window_cycle_controller =
Shell::Get()->window_cycle_controller();
const auto* window_cycle_list = window_cycle_controller->window_cycle_list();
ASSERT_TRUE(window_cycle_list->same_app_only());
const auto* cycle_view = window_cycle_list->cycle_view();
ASSERT_TRUE(cycle_view);
const auto& windows = window_cycle_list->windows_for_testing();
EXPECT_EQ(windows.size(), 3u);
w0.reset();
ASSERT_TRUE(cycle_view);
const auto& updated_windows = window_cycle_list->windows_for_testing();
EXPECT_EQ(updated_windows.size(), 2u);
CompleteWindowCycling();
}
TEST_F(SnapGroupWindowCycleTest, MruWindowForSameApp) {
std::unique_ptr<aura::Window> w0(CreateTestWindowWithAppID(std::string("A")));
std::unique_ptr<aura::Window> w1(CreateTestWindowWithAppID(std::string("B")));
std::unique_ptr<aura::Window> w2(CreateTestWindowWithAppID(std::string("A")));
std::unique_ptr<aura::Window> w3(CreateTestWindowWithAppID(std::string("A")));
std::unique_ptr<aura::Window> w4(CreateTestWindowWithAppID(std::string("B")));
SnapTwoTestWindows(w0.get(), w1.get(), true,
GetEventGenerator());
wm::ActivateWindow(w1.get());
auto* event_generator = GetEventGenerator();
event_generator->PressKey(ui::VKEY_MENU, ui::EF_NONE);
event_generator->PressAndReleaseKey(ui::VKEY_OEM_3, ui::EF_ALT_DOWN);
WindowCycleController* window_cycle_controller =
Shell::Get()->window_cycle_controller();
const auto* window_cycle_list = window_cycle_controller->window_cycle_list();
ASSERT_TRUE(window_cycle_list->same_app_only());
const auto& windows = window_cycle_list->windows_for_testing();
EXPECT_EQ(windows.size(), 2u);
CompleteWindowCycling();
}
using SnapGroupTabletConversionTest = SnapGroupTest;
TEST_F(SnapGroupTabletConversionTest, NoCrashWhenRemovingGroupInTabletMode) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
SwitchToTabletMode();
w2.reset();
SnapGroupController* snap_group_controller =
Shell::Get()->snap_group_controller();
EXPECT_FALSE(snap_group_controller->GetSnapGroupForGivenWindow(w1.get()));
EXPECT_FALSE(snap_group_controller->GetSnapGroupForGivenWindow(w2.get()));
EXPECT_EQ(GetSplitViewController()->primary_window(), w1.get());
EXPECT_TRUE(OverviewController::Get()->InOverviewSession());
}
TEST_F(SnapGroupTabletConversionTest,
ClamshellTabletTransitionWithOneSnapGroup) {
std::unique_ptr<aura::Window> window1(
CreateTestWindowInShell({.window_id = 0}));
window1->SetProperty(chromeos::kAppTypeKey, chromeos::AppType::CHROME_APP);
std::unique_ptr<aura::Window> window2(
CreateTestWindowInShell({.window_id = 1}));
window2->SetProperty(chromeos::kAppTypeKey, chromeos::AppType::CHROME_APP);
SnapTwoTestWindows(window1.get(), window2.get(), true,
GetEventGenerator());
EXPECT_TRUE(GetTopmostSnapGroupDivider()->divider_widget());
UnionBoundsEqualToWorkAreaBounds(window1.get(), window2.get(),
GetTopmostSnapGroupDivider());
SwitchToTabletMode();
EXPECT_FALSE(GetTopmostSnapGroupDivider());
EXPECT_TRUE(GetSplitViewDivider()->divider_widget());
auto* snap_group_controller = SnapGroupController::Get();
EXPECT_FALSE(
snap_group_controller->GetSnapGroupForGivenWindow(window1.get()));
EXPECT_EQ(window1.get(), GetSplitViewController()->primary_window());
EXPECT_EQ(window2.get(), GetSplitViewController()->secondary_window());
UnionBoundsEqualToWorkAreaBounds(window1.get(), window2.get(),
GetSplitViewDivider());
EXPECT_NEAR(chromeos::kDefaultSnapRatio,
*WindowState::Get(window1.get())->snap_ratio(), 0.05);
EXPECT_NEAR(chromeos::kDefaultSnapRatio,
*WindowState::Get(window2.get())->snap_ratio(), 0.05);
ExitTabletMode();
EXPECT_TRUE(SnapGroupController::Get()->AreWindowsInSnapGroup(window1.get(),
window2.get()));
EXPECT_NEAR(chromeos::kDefaultSnapRatio,
*WindowState::Get(window1.get())->snap_ratio(), 0.05);
EXPECT_NEAR(chromeos::kDefaultSnapRatio,
*WindowState::Get(window2.get())->snap_ratio(), 0.05);
UnionBoundsEqualToWorkAreaBounds(window1.get(), window2.get(),
GetTopmostSnapGroupDivider());
EXPECT_TRUE(GetTopmostSnapGroupDivider()->divider_widget());
}
TEST_F(SnapGroupTabletConversionTest,
ClamshellTabletTransitionGetClosestFixedRatio) {
UpdateDisplay("900x600");
std::unique_ptr<aura::Window> window1(
CreateTestWindowInShell({.window_id = 0}));
window1->SetProperty(chromeos::kAppTypeKey, chromeos::AppType::CHROME_APP);
std::unique_ptr<aura::Window> window2(
CreateTestWindowInShell({.window_id = 1}));
window2->SetProperty(chromeos::kAppTypeKey, chromeos::AppType::CHROME_APP);
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(window1.get(), window2.get(), true,
event_generator);
ASSERT_TRUE(GetTopmostSnapGroupDivider()->divider_widget());
EXPECT_EQ(*WindowState::Get(window1.get())->snap_ratio(),
chromeos::kDefaultSnapRatio);
struct {
int distance_delta;
float expected_snap_ratio;
} kTestCases[]{{-200, chromeos::kOneThirdSnapRatio},
{400, chromeos::kTwoThirdSnapRatio},
{-180, chromeos::kDefaultSnapRatio}};
const gfx::Rect work_area_bounds_in_screen =
screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
GetSplitViewController()->root_window()->GetChildById(
desks_util::GetActiveDeskContainerId()));
for (const auto test_case : kTestCases) {
event_generator->set_current_screen_location(
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint());
event_generator->DragMouseBy(test_case.distance_delta, 0);
GetTopmostSnapGroupDivider()->EndResizeWithDivider(
event_generator->current_screen_location());
SwitchToTabletMode();
EXPECT_TRUE(GetSplitViewDivider() && !GetTopmostSnapGroupDivider());
const auto current_divider_position =
GetSplitViewDividerBoundsInScreen().x();
const auto expected_divider_position = std::round(
work_area_bounds_in_screen.width() * test_case.expected_snap_ratio -
kSplitviewDividerShortSideLength / 2);
EXPECT_NEAR(current_divider_position, expected_divider_position,
1);
EXPECT_NEAR(float(window1->GetBoundsInScreen().width()) /
work_area_bounds_in_screen.width(),
test_case.expected_snap_ratio, 1);
ExitTabletMode();
}
}
TEST_F(SnapGroupTabletConversionTest, TransitionToTabletInOverview) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
auto* snap_group_controller = SnapGroupController::Get();
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
ToggleOverview();
ASSERT_TRUE(IsInOverviewSession());
const auto* overview_grid =
GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
ASSERT_TRUE(overview_grid);
EXPECT_EQ(1u, overview_grid->item_list().size());
SwitchToTabletMode();
EXPECT_EQ(2u, overview_grid->item_list().size());
OverviewItemBase* overview_item1 = GetOverviewItemForWindow(w1.get());
OverviewItemBase* overview_item2 = GetOverviewItemForWindow(w2.get());
EXPECT_NE(overview_item1, overview_item2);
const gfx::RectF overview_item1_bounds_tablet =
overview_item1->target_bounds();
const gfx::RectF overview_item2_bounds_tablet =
overview_item2->target_bounds();
EXPECT_FALSE(
overview_item1_bounds_tablet.Intersects(overview_item2_bounds_tablet));
EXPECT_FALSE(
overview_item1_bounds_tablet.Contains(overview_item2_bounds_tablet));
EXPECT_FALSE(
overview_item2_bounds_tablet.Contains(overview_item1_bounds_tablet));
ExitTabletMode();
EXPECT_EQ(2u, overview_grid->item_list().size());
overview_item1 = GetOverviewItemForWindow(w1.get());
overview_item2 = GetOverviewItemForWindow(w2.get());
EXPECT_NE(overview_item1, overview_item2);
const gfx::RectF overview_item1_bounds_clamshell =
overview_item1->target_bounds();
const gfx::RectF overview_item2_bounds_clamshell =
overview_item2->target_bounds();
EXPECT_FALSE(overview_item1_bounds_clamshell.Intersects(
overview_item2_bounds_clamshell));
EXPECT_FALSE(overview_item1_bounds_clamshell.Contains(
overview_item2_bounds_clamshell));
EXPECT_FALSE(overview_item2_bounds_clamshell.Contains(
overview_item1_bounds_clamshell));
}
TEST_F(SnapGroupTabletConversionTest, TransitionToTabletInPartialOverview) {
UpdateDisplay("800x600");
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
std::unique_ptr<aura::Window> w3(
CreateAppWindow(gfx::Rect(200, 200, 200, 200)));
std::unique_ptr<aura::Window> w4(CreateAppWindow());
SnapOneTestWindow(w4.get(), WindowStateType::kSecondarySnapped,
chromeos::kOneThirdSnapRatio);
VerifySplitViewOverviewSession(w4.get());
auto* root_window = Shell::GetPrimaryRootWindow();
const auto* overview_grid = GetOverviewGridForRoot(root_window);
ASSERT_TRUE(overview_grid);
EXPECT_EQ(2u, overview_grid->item_list().size());
EXPECT_TRUE(w1->IsVisible());
EXPECT_TRUE(w2->IsVisible());
SwitchToTabletMode();
EXPECT_EQ(3u, overview_grid->item_list().size());
}
using SnapGroupMultipleSnapGroupsTest = SnapGroupTest;
TEST_F(SnapGroupMultipleSnapGroupsTest, MultipleSnapGroups) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
auto* snap_group_controller = SnapGroupController::Get();
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
auto* snap_group1 =
snap_group_controller->GetSnapGroupForGivenWindow(w2.get());
auto* snap_group_divider1 = snap_group1->snap_group_divider();
std::unique_ptr<aura::Window> w3(CreateAppWindow(gfx::Rect(0, 0, 800, 600)));
std::unique_ptr<aura::Window> w4(CreateAppWindow());
std::unique_ptr<aura::Window> w5(CreateAppWindow());
SnapOneTestWindow(w4.get(), WindowStateType::kPrimarySnapped,
chromeos::kTwoThirdSnapRatio);
ClickOverviewItem(GetEventGenerator(), w5.get());
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w4.get(), w5.get()));
auto* snap_group2 =
snap_group_controller->GetSnapGroupForGivenWindow(w4.get());
auto* snap_group_divider2 = snap_group2->snap_group_divider();
EXPECT_EQ(2u, snap_group_controller->snap_groups_for_testing().size());
EXPECT_NE(snap_group_divider1, snap_group_divider2);
aura::Window* divider1_window = snap_group_divider1->GetDividerWindow();
aura::Window* divider2_window = snap_group_divider2->GetDividerWindow();
base::RunLoop().RunUntilIdle();
auto* desk_container = desks_util::GetActiveDeskContainerForRoot(
Shell::Get()->GetPrimaryRootWindow());
EXPECT_THAT(desk_container->children(),
ElementsAre( w1.get(), w2.get(), divider1_window,
w3.get(), w4.get(),
w5.get(), divider2_window));
}
TEST_F(SnapGroupMultipleSnapGroupsTest, MultipleSnapGroupsRecall) {
UpdateDisplay("900x600");
auto* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w1.get(),
chromeos::WindowStateType::kPrimarySnapped,
chromeos::kTwoThirdSnapRatio);
ASSERT_TRUE(IsInOverviewSession());
ClickOverviewItem(GetEventGenerator(), w2.get());
auto* snap_group_controller = SnapGroupController::Get();
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
SnapGroup* snap_group1 =
snap_group_controller->GetSnapGroupForGivenWindow(w1.get());
SplitViewDivider* snap_group_divider1 = snap_group1->snap_group_divider();
const int divider_position1 = snap_group_divider1->divider_position();
std::unique_ptr<aura::Window> w3(CreateAppWindow(gfx::Rect(0, 0, 800, 600)));
std::unique_ptr<aura::Window> w4(CreateAppWindow());
std::unique_ptr<aura::Window> w5(CreateAppWindow());
SnapTwoTestWindows(w4.get(), w5.get(), true,
GetEventGenerator());
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w4.get(), w5.get()));
auto* snap_group2 =
snap_group_controller->GetSnapGroupForGivenWindow(w4.get());
auto* snap_group_divider2 = snap_group2->snap_group_divider();
EXPECT_EQ(GetWorkAreaBounds().width() * chromeos::kDefaultSnapRatio -
kSplitviewDividerShortSideLength / 2,
snap_group_divider2->divider_position());
wm::ActivateWindow(w2.get());
EXPECT_EQ(2u, snap_group_controller->snap_groups_for_testing().size());
EXPECT_EQ(snap_group1, snap_group_controller->GetTopmostSnapGroup());
base::RunLoop().RunUntilIdle();
auto* desk_container = desks_util::GetActiveDeskContainerForRoot(
Shell::Get()->GetPrimaryRootWindow());
aura::Window* divider1 = snap_group_divider1->GetDividerWindow();
aura::Window* divider2 = snap_group_divider2->GetDividerWindow();
EXPECT_THAT(desk_container->children(),
ElementsAre( w3.get(), w4.get(),
w5.get(), divider2,
w1.get(), w2.get(), divider1));
EXPECT_EQ(divider_position1, snap_group_divider1->divider_position());
EXPECT_EQ(divider_position1, w1->GetBoundsInScreen().width());
EXPECT_EQ(divider_position1 + kSplitviewDividerShortSideLength,
w2->GetBoundsInScreen().x());
}
TEST_F(SnapGroupMultipleSnapGroupsTest,
SelectWindowInSnapGroupInFasterPartialOverview) {
UpdateDisplay("800x600");
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
std::unique_ptr<aura::Window> w3(
CreateAppWindow(gfx::Rect(200, 200, 200, 200)));
std::unique_ptr<aura::Window> w4(CreateAppWindow());
SnapOneTestWindow(w4.get(), WindowStateType::kSecondarySnapped,
chromeos::kOneThirdSnapRatio);
ASSERT_TRUE(IsInOverviewSession());
VerifySplitViewOverviewSession(w4.get());
auto* root_window = Shell::GetPrimaryRootWindow();
const auto* overview_grid = GetOverviewGridForRoot(root_window);
ASSERT_TRUE(overview_grid);
EXPECT_EQ(2u, overview_grid->item_list().size());
EXPECT_TRUE(w1->IsVisible());
EXPECT_TRUE(w2->IsVisible());
ActivateWindowInOverviewGroupItem(w1.get(), event_generator,
false);
EXPECT_EQ(WindowStateType::kPrimarySnapped,
WindowState::Get(w1.get())->GetStateType());
ASSERT_FALSE(IsInOverviewSession());
VerifyNotSplitViewOrOverviewSession(w1.get());
SnapGroupController* snap_group_controller = SnapGroupController::Get();
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w4.get()));
UnionBoundsEqualToWorkAreaBounds(w1.get(), w4.get(),
GetTopmostSnapGroupDivider());
EXPECT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
wm::ActivateWindow(w3.get());
w3->SetBounds(gfx::Rect(500, 200, 100, 200));
wm::ActivateWindow(w2.get());
SnapOneTestWindow(w2.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
ASSERT_TRUE(IsInOverviewSession());
VerifySplitViewOverviewSession(w2.get());
overview_grid = GetOverviewGridForRoot(root_window);
ASSERT_TRUE(overview_grid);
EXPECT_EQ(2u, overview_grid->item_list().size());
EXPECT_TRUE(w2->IsVisible());
EXPECT_TRUE(w4->IsVisible());
ActivateWindowInOverviewGroupItem(w1.get(), event_generator,
false);
EXPECT_EQ(WindowStateType::kSecondarySnapped,
WindowState::Get(w1.get())->GetStateType());
ASSERT_FALSE(IsInOverviewSession());
VerifyNotSplitViewOrOverviewSession(w2.get());
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w2.get(), w1.get()));
UnionBoundsEqualToWorkAreaBounds(
snap_group_controller->GetSnapGroupForGivenWindow(w1.get()));
}
TEST_F(SnapGroupMultipleSnapGroupsTest,
SelectWindowInSnapGroupInManualPartialOverview) {
UpdateDisplay("800x600");
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
std::unique_ptr<aura::Window> w3(
CreateAppWindow(gfx::Rect(200, 200, 200, 200)));
ToggleOverview();
ASSERT_TRUE(IsInOverviewSession());
auto* root_window = Shell::GetPrimaryRootWindow();
const auto* overview_grid = GetOverviewGridForRoot(root_window);
ASSERT_TRUE(overview_grid);
EXPECT_EQ(2u, overview_grid->item_list().size());
EXPECT_TRUE(w1->IsVisible());
EXPECT_TRUE(w2->IsVisible());
DragItemToPoint(GetOverviewItemForWindow(w3.get()), gfx::Point(0, 300),
event_generator, false, true);
ASSERT_TRUE(IsInOverviewSession());
VerifySplitViewOverviewSession(w3.get());
overview_grid = GetOverviewGridForRoot(root_window);
ASSERT_TRUE(overview_grid);
EXPECT_EQ(1u, overview_grid->item_list().size());
EXPECT_TRUE(w1->IsVisible());
EXPECT_TRUE(w2->IsVisible());
ActivateWindowInOverviewGroupItem(w2.get(), event_generator,
false);
EXPECT_EQ(WindowStateType::kSecondarySnapped,
WindowState::Get(w2.get())->GetStateType());
ASSERT_FALSE(IsInOverviewSession());
VerifyNotSplitViewOrOverviewSession(w3.get());
SnapGroupController* snap_group_controller = SnapGroupController::Get();
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w3.get(), w2.get()));
UnionBoundsEqualToWorkAreaBounds(w3.get(), w2.get(),
GetTopmostSnapGroupDivider());
EXPECT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
ToggleOverview();
ASSERT_TRUE(IsInOverviewSession());
overview_grid = GetOverviewGridForRoot(root_window);
ASSERT_TRUE(overview_grid);
EXPECT_EQ(2u, overview_grid->item_list().size());
EXPECT_TRUE(w1->IsVisible());
EXPECT_TRUE(w2->IsVisible());
DragItemToPoint(GetOverviewItemForWindow(w1.get()), gfx::Point(800, 300),
event_generator, false, true);
ASSERT_TRUE(IsInOverviewSession());
VerifySplitViewOverviewSession(w1.get());
overview_grid = GetOverviewGridForRoot(root_window);
ASSERT_TRUE(overview_grid);
EXPECT_EQ(1u, overview_grid->item_list().size());
EXPECT_TRUE(w1->IsVisible());
EXPECT_TRUE(w2->IsVisible());
ActivateWindowInOverviewGroupItem(w2.get(), event_generator,
false);
EXPECT_EQ(WindowStateType::kPrimarySnapped,
WindowState::Get(w2.get())->GetStateType());
ASSERT_FALSE(IsInOverviewSession());
VerifyNotSplitViewOrOverviewSession(w1.get());
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w2.get(), w1.get()));
UnionBoundsEqualToWorkAreaBounds(
snap_group_controller->GetSnapGroupForGivenWindow(w1.get()));
EXPECT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w3.get(), w2.get()));
}
TEST_F(SnapGroupMultipleSnapGroupsTest,
NoCrashWhenLongTappingOnGroupItemInPartialOverview) {
UpdateDisplay("800x600");
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
std::unique_ptr<aura::Window> w3(
CreateAppWindow(gfx::Rect(200, 200, 200, 200)));
std::unique_ptr<aura::Window> w4(CreateAppWindow());
SnapOneTestWindow(w4.get(), WindowStateType::kSecondarySnapped,
chromeos::kOneThirdSnapRatio);
OverviewSession* overview_session =
OverviewController::Get()->overview_session();
CHECK(overview_session);
VerifySplitViewOverviewSession(w4.get());
auto* root_window = Shell::GetPrimaryRootWindow();
const auto* overview_grid = GetOverviewGridForRoot(root_window);
ASSERT_TRUE(overview_grid);
EXPECT_EQ(2u, overview_grid->item_list().size());
EXPECT_TRUE(w1->IsVisible());
EXPECT_TRUE(w2->IsVisible());
OverviewItemBase* group_item = GetOverviewItemForWindow(w1.get());
gfx::Point location =
gfx::ToRoundedPoint(group_item->target_bounds().CenterPoint());
location.Offset(10, 0);
event_generator->set_current_screen_location(location);
overview_session->InitiateDrag(group_item, gfx::PointF(location),
true, group_item);
LongTapAt(event_generator, location);
base::RunLoop().RunUntilIdle();
}
using SnapGroupSnapToReplaceTest = SnapGroupTest;
TEST_F(SnapGroupSnapToReplaceTest, SnapToReplaceBasic) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
ASSERT_FALSE(GetSplitViewController()->InSplitViewMode());
SnapGroupController* snap_group_controller = SnapGroupController::Get();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
std::unique_ptr<aura::Window> w3(CreateAppWindow());
SnapOneTestWindow(w3.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w3.get(), w2.get()));
EXPECT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_FALSE(GetSplitViewController()->InSplitViewMode());
}
TEST_F(SnapGroupSnapToReplaceTest, WindowWithMinimumSize) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
SnapGroupController* snap_group_controller = SnapGroupController::Get();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
const gfx::Size min_size(GetWorkAreaBounds().width() * 0.6f, 0);
std::unique_ptr<aura::Window> w3 = CreateAppWindowWithMinSize(min_size);
event_generator->set_current_screen_location(GetDragPoint(w3.get()));
event_generator->DragMouseTo(0, 100);
EXPECT_EQ(WindowStateType::kPrimarySnapped,
WindowState::Get(w3.get())->GetStateType());
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w3.get(), w2.get()));
EXPECT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_EQ(min_size.width(), w3->GetBoundsInScreen().width());
UnionBoundsEqualToWorkAreaBounds(w3.get(), w2.get(),
GetTopmostSnapGroupDivider());
}
TEST_F(SnapGroupSnapToReplaceTest, BothWindowsMinimumSizes) {
const int work_area_length = GetWorkAreaBounds().width();
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(
CreateAppWindowWithMinSize(gfx::Size(work_area_length * 0.45f, 0)));
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
SnapGroupController* snap_group_controller = SnapGroupController::Get();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
std::unique_ptr<aura::Window> w3(
CreateAppWindowWithMinSize(gfx::Size(work_area_length * 0.6f, 0)));
event_generator->set_current_screen_location(GetDragPoint(w3.get()));
event_generator->DragMouseTo(0, 100);
EXPECT_EQ(WindowStateType::kPrimarySnapped,
WindowState::Get(w3.get())->GetStateType());
EXPECT_TRUE(w2->GetBoundsInScreen().Intersects(w3->GetBoundsInScreen()));
EXPECT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w2.get(), w3.get()));
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
}
TEST_F(SnapGroupSnapToReplaceTest,
SnapToReplaceWithNonWindowLayoutSnapActionSource) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
SnapGroupController* snap_group_controller = SnapGroupController::Get();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
const gfx::Rect w1_bounds(w1->GetBoundsInScreen());
const gfx::Rect w2_bounds(w2->GetBoundsInScreen());
std::unique_ptr<aura::Window> w3(CreateAppWindow());
const float w3_snap_ratio = 0.15f;
SnapOneTestWindow(w3.get(), WindowStateType::kPrimarySnapped, w3_snap_ratio,
WindowSnapActionSource::kDragWindowToEdgeToSnap);
ASSERT_GT(std::abs(1.f - *WindowState::Get(w3.get())->snap_ratio() -
*WindowState::Get(w2.get())->snap_ratio()),
kSnapToReplaceRatioDiffThreshold);
EXPECT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w3.get(), w2.get()));
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_EQ(w1_bounds, w1->GetBoundsInScreen());
EXPECT_EQ(w2_bounds, w2->GetBoundsInScreen());
}
TEST_F(SnapGroupSnapToReplaceTest, SnapToReplaceWithRatioMargin) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
SnapGroupController* snap_group_controller = SnapGroupController::Get();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
const float w1_snap_ratio = *WindowState::Get(w1.get())->snap_ratio();
EXPECT_EQ(w1_snap_ratio, chromeos::kDefaultSnapRatio);
EXPECT_EQ(*WindowState::Get(w1.get())->snap_ratio(),
chromeos::kDefaultSnapRatio);
std::unique_ptr<aura::Window> w3(CreateAppWindow());
SnapOneTestWindow(w3.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
EXPECT_LT(std::abs(w1_snap_ratio - chromeos::kDefaultSnapRatio),
kSnapToReplaceRatioDiffThreshold);
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w3.get(), w2.get()));
EXPECT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_EQ(*WindowState::Get(w3.get())->snap_ratio(),
chromeos::kDefaultSnapRatio);
EXPECT_EQ(*WindowState::Get(w2.get())->snap_ratio(),
chromeos::kDefaultSnapRatio);
ResizeDividerTo(event_generator, gfx::Point(
GetWorkAreaBounds().width() * 0.8f,
GetWorkAreaBounds().CenterPoint().y()));
std::unique_ptr<aura::Window> w4(CreateAppWindow());
SnapOneTestWindow(w4.get(), WindowStateType::kPrimarySnapped,
chromeos::kOneThirdSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
EXPECT_GT(std::abs(1.f - *WindowState::Get(w2.get())->snap_ratio() -
*WindowState::Get(w4.get())->snap_ratio()),
kSnapToReplaceRatioDiffThreshold);
EXPECT_FALSE(snap_group_controller->GetSnapGroupForGivenWindow(w4.get()));
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w2.get(), w3.get()));
}
TEST_F(SnapGroupSnapToReplaceTest, DoNotSnapToReplaceSnapGroupInOverview) {
std::unique_ptr<aura::Window> w0(
CreateAppWindow(gfx::Rect(10, 10, 200, 100)));
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
ASSERT_FALSE(GetSplitViewController()->InSplitViewMode());
SnapGroupController* snap_group_controller = SnapGroupController::Get();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
OverviewController* overview_controller = OverviewController::Get();
overview_controller->StartOverview(OverviewStartAction::kOverviewButton);
auto* overview_item0 = GetOverviewItemForWindow(w0.get());
event_generator->set_current_screen_location(
gfx::ToRoundedPoint(overview_item0->target_bounds().CenterPoint()));
event_generator->PressLeftButton();
event_generator->MoveMouseTo(gfx::Point(0, 0));
event_generator->ReleaseLeftButton();
EXPECT_EQ(WindowState::Get(w0.get())->GetStateType(),
chromeos::WindowStateType::kPrimarySnapped);
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w0.get(), w1.get()));
EXPECT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w0.get(), w2.get()));
}
TEST_F(SnapGroupSnapToReplaceTest, UseShortcutToGroupPerformSnapToReplace) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
TRACE_CALL(SnapWindowsSideBySide(kGrouped, w1.get(), w2.get()));
std::unique_ptr<aura::Window> w3(CreateAppWindow());
SnapOneTestWindow(w3.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kAutoSnapInSplitView);
TRACE_CALL(ExpectWindowsSnappedSideBySide(kGrouped, w1.get(), w2.get()));
auto* event_generator = GetEventGenerator();
event_generator->PressAndReleaseKey(ui::VKEY_G,
ui::EF_SHIFT_DOWN | ui::EF_COMMAND_DOWN);
TRACE_CALL(ExpectWindowsSnappedSideBySide(kGrouped, w3.get(), w2.get()));
std::unique_ptr<aura::Window> w4(CreateAppWindow());
SnapOneTestWindow(w4.get(), WindowStateType::kSecondarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kAutoSnapInSplitView);
TRACE_CALL(ExpectWindowsSnappedSideBySide(kGrouped, w3.get(), w2.get()));
event_generator->PressAndReleaseKey(ui::VKEY_G,
ui::EF_SHIFT_DOWN | ui::EF_COMMAND_DOWN);
TRACE_CALL(ExpectWindowsSnappedSideBySide(kGrouped, w3.get(), w4.get()));
}
TEST_F(SnapGroupSnapToReplaceTest, SnapToReplaceWithFloatedWindow) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
ASSERT_FALSE(GetSplitViewController()->InSplitViewMode());
SnapGroupController* snap_group_controller = SnapGroupController::Get();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
std::unique_ptr<aura::Window> floated_window(CreateAppWindow());
PressAndReleaseKey(ui::VKEY_F, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN);
ASSERT_TRUE(WindowState::Get(floated_window.get())->IsFloated());
SnapOneTestWindow(floated_window.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
ASSERT_FALSE(WindowState::Get(floated_window.get())->IsFloated());
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(floated_window.get(),
w2.get()));
EXPECT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
}
TEST_F(SnapGroupSnapToReplaceTest, DisallowSnapToReplaceWithAlwaysOnTopWindow) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
ASSERT_FALSE(GetSplitViewController()->InSplitViewMode());
SnapGroupController* snap_group_controller = SnapGroupController::Get();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
std::unique_ptr<aura::Window> always_on_top_window(CreateAlwaysOnTopWindow());
EXPECT_NE(w1->parent(), always_on_top_window->parent());
SnapOneTestWindow(always_on_top_window.get(),
WindowStateType::kSecondarySnapped,
chromeos::kDefaultSnapRatio);
EXPECT_FALSE(snap_group_controller->AreWindowsInSnapGroup(
w1.get(), always_on_top_window.get()));
EXPECT_FALSE(snap_group_controller->AreWindowsInSnapGroup(
w2.get(), always_on_top_window.get()));
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
}
using SnapGroupAutoSnapGroupTest = SnapGroupTest;
TEST_F(SnapGroupAutoSnapGroupTest, ReSnapWindowWithDifferentSnapRatio) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
SnapGroupController* snap_group_controller = SnapGroupController::Get();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
ASSERT_TRUE(GetTopmostSnapGroupDivider());
SnapOneTestWindow(w2.get(), WindowStateType::kSecondarySnapped,
chromeos::kOneThirdSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
const int work_area_width(GetWorkAreaBounds().width());
EXPECT_EQ(std::round(work_area_width * chromeos::kTwoThirdSnapRatio),
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint().x());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
}
TEST_F(SnapGroupAutoSnapGroupTest, DragToSnap) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kTwoThirdSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
auto* event_generator = GetEventGenerator();
ClickOverviewItem(event_generator, w2.get());
auto* snap_group_controller = Shell::Get()->snap_group_controller();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
const int work_area_width(GetWorkAreaBounds().width());
EXPECT_EQ(std::round(work_area_width * chromeos::kTwoThirdSnapRatio),
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint().x());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
event_generator->MoveMouseTo(GetDragPoint(w1.get()));
event_generator->DragMouseTo(GetWorkAreaBounds().CenterPoint());
EXPECT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
event_generator->DragMouseTo(GetWorkAreaBounds().left_center());
ASSERT_EQ(WindowStateType::kPrimarySnapped,
WindowState::Get(w1.get())->GetStateType());
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_EQ(std::round(work_area_width * chromeos::kTwoThirdSnapRatio),
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint().x());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
event_generator->MoveMouseTo(GetDragPoint(w2.get()));
event_generator->DragMouseTo(GetWorkAreaBounds().CenterPoint());
EXPECT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
event_generator->DragMouseTo(GetWorkAreaBounds().right_center());
ASSERT_EQ(WindowStateType::kSecondarySnapped,
WindowState::Get(w2.get())->GetStateType());
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_EQ(std::round(work_area_width * chromeos::kTwoThirdSnapRatio),
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint().x());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
}
TEST_F(SnapGroupAutoSnapGroupTest, DragToSnapWithoutReleasingMouse) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
auto* snap_group_controller = Shell::Get()->snap_group_controller();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
const int work_area_width(GetWorkAreaBounds().width());
EXPECT_EQ(std::round(work_area_width * chromeos::kDefaultSnapRatio),
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint().x());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
event_generator->MoveMouseTo(GetDragPoint(w1.get()));
event_generator->PressLeftButton();
event_generator->MoveMouseTo(GetWorkAreaBounds().CenterPoint());
ASSERT_EQ(gfx::Rect(250, 252, 300, 300), w1->GetTargetBounds());
EXPECT_EQ(WindowStateType::kPrimarySnapped,
WindowState::Get(w1.get())->GetStateType());
event_generator->MoveMouseTo(GetWorkAreaBounds().left_center());
event_generator->ReleaseLeftButton();
ASSERT_EQ(WindowStateType::kPrimarySnapped,
WindowState::Get(w1.get())->GetStateType());
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_EQ(GetWorkAreaBounds().CenterPoint(),
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
}
TEST_F(SnapGroupAutoSnapGroupTest, ResizeThenDragToSnap) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
auto* snap_group_controller = Shell::Get()->snap_group_controller();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
for (const int resize_delta : {-30, 0, 15}) {
SCOPED_TRACE(base::StringPrintf("Resize delta: %d", resize_delta));
const gfx::Point divider_center(
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint());
event_generator->MoveMouseTo(divider_center);
event_generator->PressLeftButton();
const gfx::Point resize_point(divider_center +
gfx::Vector2d(resize_delta, 0));
event_generator->MoveMouseTo(resize_point, 2);
EXPECT_EQ(resize_point,
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
event_generator->ReleaseLeftButton();
event_generator->MoveMouseTo(GetDragPoint(w1.get()));
event_generator->DragMouseTo(GetWorkAreaBounds().CenterPoint());
ASSERT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
event_generator->DragMouseTo(GetWorkAreaBounds().left_center());
ASSERT_TRUE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_NEAR(resize_point.x(),
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint().x(),
1);
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
}
}
TEST_F(SnapGroupAutoSnapGroupTest, WindowLayoutMenu) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kTwoThirdSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
auto* event_generator = GetEventGenerator();
ClickOverviewItem(event_generator, w2.get());
auto* snap_group_controller = Shell::Get()->snap_group_controller();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
const int work_area_width(GetWorkAreaBounds().width());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_EQ(work_area_width * chromeos::kDefaultSnapRatio,
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint().x());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kTwoThirdSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_EQ(std::round(work_area_width * chromeos::kTwoThirdSnapRatio),
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint().x());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
SnapOneTestWindow(w2.get(), WindowStateType::kSecondarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_EQ(std::round(work_area_width * chromeos::kDefaultSnapRatio),
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint().x());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
SnapOneTestWindow(w2.get(), WindowStateType::kSecondarySnapped,
chromeos::kOneThirdSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_EQ(std::round(work_area_width * chromeos::kTwoThirdSnapRatio),
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint().x());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
}
TEST_F(SnapGroupAutoSnapGroupTest, SkipPartialAndFormSnapGroup) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kTwoThirdSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
VerifySplitViewOverviewSession(w1.get());
PressAndReleaseKey(ui::VKEY_ESCAPE, ui::EF_NONE);
VerifyNotSplitViewOrOverviewSession(w1.get());
wm::ActivateWindow(w2.get());
auto* event_generator = GetEventGenerator();
event_generator->MoveMouseTo(GetDragPoint(w2.get()));
event_generator->DragMouseTo(GetWorkAreaBounds().right_center());
EXPECT_EQ(WindowStateType::kSecondarySnapped,
WindowState::Get(w2.get())->GetStateType());
auto* snap_group_controller = Shell::Get()->snap_group_controller();
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
event_generator->set_current_screen_location(GetDragPoint(w1.get()));
event_generator->DragMouseTo(GetWorkAreaBounds().CenterPoint());
EXPECT_EQ(WindowStateType::kNormal,
WindowState::Get(w1.get())->GetStateType());
PressAndReleaseKey(ui::VKEY_OEM_4, ui::EF_ALT_DOWN);
EXPECT_EQ(WindowStateType::kPrimarySnapped,
WindowState::Get(w1.get())->GetStateType());
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
}
TEST_F(SnapGroupAutoSnapGroupTest, SnapRatioGapThreshold) {
UpdateDisplay("1000x800");
std::unique_ptr<aura::Window> w1(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
auto* event_generator = GetEventGenerator();
const gfx::Point resize_point1(w1->GetBoundsInScreen().right_center());
event_generator->MoveMouseTo(resize_point1);
event_generator->DragMouseTo(250, resize_point1.y());
ASSERT_LT(*WindowState::Get(w1.get())->snap_ratio(),
chromeos::kOneThirdSnapRatio);
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w2.get(), WindowStateType::kSecondarySnapped,
chromeos::kOneThirdSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
EXPECT_LT(*WindowState::Get(w1.get())->snap_ratio(),
chromeos::kOneThirdSnapRatio);
ASSERT_GT(GetSnapRatioGap(w1.get(), w2.get()),
kSnapToReplaceRatioDiffThreshold);
EXPECT_FALSE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w1.get(), w2.get()));
}
TEST_F(SnapGroupAutoSnapGroupTest, SnapRatioOverlapThreshold) {
UpdateDisplay("1000x800");
std::unique_ptr<aura::Window> w1(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kSecondarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
const gfx::Point resize_point(w1->GetBoundsInScreen().left_center());
auto* event_generator = GetEventGenerator();
event_generator->MoveMouseTo(resize_point);
event_generator->DragMouseTo(100, resize_point.y());
ASSERT_GT(*WindowState::Get(w1.get())->snap_ratio(),
chromeos::kTwoThirdSnapRatio);
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w2.get(), WindowStateType::kPrimarySnapped,
chromeos::kTwoThirdSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
ASSERT_GT(GetSnapRatioGap(w1.get(), w2.get()),
kSnapToReplaceRatioDiffThreshold);
EXPECT_FALSE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w1.get(), w2.get()));
}
TEST_F(SnapGroupAutoSnapGroupTest, ShelfRoundedCornersInAutoGroupEntryPoint) {
ShelfLayoutManager* shelf_layout_manager =
AshTestBase::GetPrimaryShelf()->shelf_layout_manager();
ASSERT_EQ(ShelfBackgroundType::kDefaultBg,
shelf_layout_manager->shelf_background_type());
std::unique_ptr<aura::Window> w1(CreateAppWindow());
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kTwoThirdSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapOneTestWindow(w2.get(), WindowStateType::kSecondarySnapped,
chromeos::kOneThirdSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
SnapGroupController* snap_group_controller = SnapGroupController::Get();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_EQ(ShelfBackgroundType::kMaximized,
shelf_layout_manager->shelf_background_type());
auto* event_generator = GetEventGenerator();
event_generator->MoveMouseTo(w1->GetBoundsInScreen().top_center());
aura::test::TestWindowDelegate().set_window_component(HTCAPTION);
event_generator->PressLeftButton();
event_generator->MoveMouseBy(50, 200);
EXPECT_TRUE(WindowState::Get(w1.get())->is_dragged());
event_generator->ReleaseLeftButton();
ASSERT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_EQ(ShelfBackgroundType::kDefaultBg,
shelf_layout_manager->shelf_background_type());
}
using SnapGroupDisplayMetricsTest = SnapGroupTest;
TEST_F(SnapGroupDisplayMetricsTest, DisplayScaleChange) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
const float w1_snap_ratio = *WindowState::Get(w1.get())->snap_ratio();
const float w2_snap_ratio = *WindowState::Get(w2.get())->snap_ratio();
SplitViewDivider* divider = GetTopmostSnapGroupDivider();
ASSERT_TRUE(divider->divider_widget());
display::DisplayManager* display_manager = Shell::Get()->display_manager();
const auto display_id = WindowTreeHostManager::GetPrimaryDisplayId();
for (const bool zoom_in : {true, true, true, true, false, false, false}) {
display_manager->ZoomDisplay(display_id, zoom_in);
EXPECT_NEAR(w1_snap_ratio, *WindowState::Get(w1.get())->snap_ratio(), 0.01);
EXPECT_NEAR(w2_snap_ratio, *WindowState::Get(w2.get())->snap_ratio(), 0.01);
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(), divider);
}
}
TEST_F(SnapGroupDisplayMetricsTest, DisplayRotation) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
SplitViewDivider* divider = GetTopmostSnapGroupDivider();
ASSERT_TRUE(divider->divider_widget());
auto* display_manager = Shell::Get()->display_manager();
for (auto rotation :
{display::Display::ROTATE_270, display::Display::ROTATE_0}) {
SCOPED_TRACE(
base::StringPrintf("Screen rotation = %d", static_cast<int>(rotation)));
display_manager->SetDisplayRotation(
WindowTreeHostManager::GetPrimaryDisplayId(), rotation,
display::Display::RotationSource::USER);
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(), divider);
}
}
TEST_F(SnapGroupDisplayMetricsTest, ScaleUpWorkArea) {
UpdateDisplay("800x600");
const gfx::Size min_size(370, 0);
std::unique_ptr<aura::Window> w1(CreateAppWindowWithMinSize(min_size));
std::unique_ptr<aura::Window> w2(CreateAppWindowWithMinSize(min_size));
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
auto* snap_group_controller = SnapGroupController::Get();
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
PressAndReleaseKey(ui::VKEY_OEM_PLUS,
ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN);
const int64_t primary_id = display::Screen::Get()->GetPrimaryDisplay().id();
const float zoom_factor_1 = 1.05f;
ASSERT_EQ(zoom_factor_1,
display_manager()->GetDisplayInfo(primary_id).zoom_factor());
ASSERT_FALSE(w1->GetBoundsInScreen().Intersects(w2->GetBoundsInScreen()));
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
PressAndReleaseKey(ui::VKEY_OEM_PLUS,
ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN);
const float zoom_factor_2 = 1.1f;
ASSERT_EQ(zoom_factor_2,
display_manager()->GetDisplayInfo(primary_id).zoom_factor());
ASSERT_TRUE(w1->GetBoundsInScreen().Intersects(w2->GetBoundsInScreen()));
EXPECT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
}
TEST_F(SnapGroupDisplayMetricsTest, ScaleUpWorkAreaInOverview) {
UpdateDisplay("800x600");
const gfx::Size min_size(395, 0);
std::unique_ptr<aura::Window> w1(CreateAppWindowWithMinSize(min_size));
std::unique_ptr<aura::Window> w2(CreateAppWindowWithMinSize(min_size));
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
auto* snap_group_controller = SnapGroupController::Get();
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
ToggleOverview();
ASSERT_TRUE(IsInOverviewSession());
const auto* overview_grid =
GetOverviewGridForRoot(Shell::GetPrimaryRootWindow());
ASSERT_TRUE(overview_grid);
PressAndReleaseKey(ui::VKEY_OEM_PLUS,
ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN);
const int64_t primary_id = display::Screen::Get()->GetPrimaryDisplay().id();
ASSERT_EQ(1.05f, display_manager()->GetDisplayInfo(primary_id).zoom_factor());
ASSERT_TRUE(GetUnionScreenBoundsForWindow(w1.get()).Intersects(
GetUnionScreenBoundsForWindow(w2.get())));
ToggleOverview();
ASSERT_FALSE(IsInOverviewSession());
EXPECT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_FALSE(GetTopmostSnapGroupDivider());
}
TEST_F(SnapGroupDisplayMetricsTest, DockedMagnifier) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
auto* docked_mangnifier_controller =
Shell::Get()->docked_magnifier_controller();
docked_mangnifier_controller->SetEnabled(true);
}
TEST_F(SnapGroupDisplayMetricsTest, VirtualKeyboard) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
SetVirtualKeyboardEnabled(true);
auto* keyboard_controller = keyboard::KeyboardUIController::Get();
keyboard_controller->ShowKeyboard(true);
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
keyboard_controller->HideKeyboardByUser();
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
}
TEST_F(SnapGroupDisplayMetricsTest, ChromeVox) {
const gfx::Rect work_area_without_cvox(GetWorkAreaBounds());
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
const int kAccessibilityPanelHeight = 45;
std::unique_ptr<views::Widget> widget =
CreateTestWidget(views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET,
nullptr, kShellWindowId_AccessibilityPanelContainer);
SetAccessibilityPanelHeight(kAccessibilityPanelHeight);
auto* a11y_controller = Shell::Get()->accessibility_controller();
a11y_controller->spoken_feedback().SetEnabled(true);
const gfx::Rect work_area_with_cvox(GetWorkAreaBounds());
ASSERT_NE(work_area_without_cvox, work_area_with_cvox);
EXPECT_TRUE(a11y_controller->spoken_feedback().enabled());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
SetAccessibilityPanelHeight(0);
a11y_controller->spoken_feedback().SetEnabled(false);
ASSERT_EQ(work_area_without_cvox, GetWorkAreaBounds());
EXPECT_FALSE(a11y_controller->spoken_feedback().enabled());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
}
using SnapGroupMultiDisplayTest = SnapGroupTest;
TEST_F(SnapGroupMultiDisplayTest, SnapGroupCreationOnExternalDisplay) {
UpdateDisplay("800x700,801+0-800x700");
display::DisplayManager* display_manager = Shell::Get()->display_manager();
const auto& displays = display_manager->active_display_list();
ASSERT_EQ(2U, displays.size());
std::unique_ptr<aura::Window> w1(
CreateAppWindow(gfx::Rect(900, 0, 200, 100)));
std::unique_ptr<aura::Window> w2(
CreateAppWindow(gfx::Rect(1000, 50, 100, 200)));
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
auto* snap_group =
SnapGroupController::Get()->GetSnapGroupForGivenWindow(w1.get());
ASSERT_TRUE(snap_group);
VerifySnapGroupOnDisplay(snap_group, displays[1].id());
auto* snap_group_divider = snap_group->snap_group_divider();
const gfx::Point divider_point(
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint());
event_generator->set_current_screen_location(divider_point);
event_generator->PressLeftButton();
const gfx::Point resize_point1 = gfx::Point(950, divider_point.y());
const bool horizontal = IsLayoutHorizontal(displays[1]);
const int min_length = GetMinimumWindowLength(w1.get(), horizontal);
ASSERT_EQ(min_length, GetMinimumWindowLength(w2.get(), horizontal));
ASSERT_EQ(104, min_length);
event_generator->MoveMouseTo(resize_point1, 2);
EXPECT_EQ(resize_point1.x(),
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint().x());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(), snap_group_divider);
const gfx::Point resize_point2 = gfx::Point(810, divider_point.y());
event_generator->MoveMouseTo(resize_point2, 2);
EXPECT_EQ(min_length, w1->GetBoundsInScreen().width());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(), snap_group_divider);
const gfx::Point resize_point3 = gfx::Point(1500, divider_point.y());
event_generator->MoveMouseTo(resize_point3, 2);
EXPECT_EQ(min_length, w2->GetBoundsInScreen().width());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(), snap_group_divider);
}
TEST_F(SnapGroupMultiDisplayTest, NoGapAfterSnapGroupCreation) {
gfx::ScopedAnimationDurationScaleMode animation_scale(
gfx::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
UpdateDisplay("1366x768,1367+0-1366x768");
const gfx::Size window_minimum_size = gfx::Size(500, 0);
for (const int window_x_origin : {0, 1367}) {
SCOPED_TRACE(base::StringPrintf("window origin = %d", window_x_origin));
aura::test::TestWindowDelegate delegate1;
std::unique_ptr<aura::Window> w1(CreateTestWindowInShell(
{.delegate = &delegate1, .bounds = {window_x_origin, 0, 800, 600}}));
w1->SetProperty(chromeos::kAppTypeKey, chromeos::AppType::CHROME_APP);
delegate1.set_minimum_size(window_minimum_size);
aura::test::TestWindowDelegate delegate2;
std::unique_ptr<aura::Window> w2(CreateTestWindowInShell(
{.delegate = &delegate2,
.bounds = {window_x_origin + 500, 0, 800, 600}}));
w2->SetProperty(chromeos::kAppTypeKey, chromeos::AppType::CHROME_APP);
delegate2.set_minimum_size(window_minimum_size);
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kTwoThirdSnapRatio);
WaitForOverviewEntered();
ClickOverviewItem(GetEventGenerator(), w2.get());
EXPECT_EQ(chromeos::WindowStateType::kSecondarySnapped,
WindowState::Get(w2.get())->GetStateType());
WaitForOverviewExitAnimation();
EXPECT_TRUE(GetTopmostSnapGroupDivider());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
}
}
TEST_F(SnapGroupMultiDisplayTest, RemoveDisplayInSplitViewSetupSession) {
UpdateDisplay("800x600,801+0-800x600");
display::test::DisplayManagerTestApi display_manager_test(display_manager());
std::unique_ptr<aura::Window> window1(
CreateTestWindowInShell({.bounds = {900, 0, 100, 100}}));
std::unique_ptr<aura::Window> window2(
CreateTestWindowInShell({.bounds = {1000, 0, 100, 100}}));
WindowState* window_state = WindowState::Get(window1.get());
const WindowSnapWMEvent snap_type(
WM_EVENT_SNAP_PRIMARY,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
window_state->OnWMEvent(&snap_type);
ASSERT_EQ(
display_manager_test.GetSecondaryDisplay().id(),
display::Screen::Get()->GetDisplayNearestWindow(window1.get()).id());
EXPECT_EQ(WindowStateType::kPrimarySnapped, window_state->GetStateType());
EXPECT_TRUE(OverviewController::Get()->InOverviewSession());
EXPECT_TRUE(RootWindowController::ForWindow(window1.get())
->split_view_overview_session());
UpdateDisplay("800x600");
base::RunLoop().RunUntilIdle();
}
TEST_F(SnapGroupMultiDisplayTest, NoCrashOnDisplayMetricsChange) {
UpdateDisplay("800x700,801+0-900x600");
display::DisplayManager* display_manager = Shell::Get()->display_manager();
const auto& displays = display_manager->active_display_list();
ASSERT_EQ(2U, displays.size());
display::DisplayIdList display_ids =
display_manager->GetConnectedDisplayIdList();
Shelf* primary_shelf = RootWindowController::ForWindow(
Shell::GetRootWindowForDisplayId(display_ids[0]))
->shelf();
primary_shelf->SetAutoHideBehavior(ShelfAutoHideBehavior::kAlways);
Shelf* secondary_shelf = RootWindowController::ForWindow(
Shell::GetRootWindowForDisplayId(display_ids[1]))
->shelf();
secondary_shelf->SetAutoHideBehavior(ShelfAutoHideBehavior::kAlways);
std::unique_ptr<aura::Window> w1(
CreateAppWindow(gfx::Rect(900, 0, 200, 100)));
std::unique_ptr<aura::Window> w2(
CreateAppWindow(gfx::Rect(1000, 50, 100, 200)));
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
wm::ActivateWindow(w1.get());
auto* snap_group =
SnapGroupController::Get()->GetSnapGroupForGivenWindow(w1.get());
ASSERT_TRUE(snap_group);
VerifySnapGroupOnDisplay(snap_group, display_ids[1]);
display_manager->UpdateWorkAreaOfDisplay(display_ids[0], gfx::Insets(5));
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(display_manager->GetDisplayInfo(display_ids[0]));
display_manager->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(1u, display_manager->GetNumDisplays());
EXPECT_FALSE(
SnapGroupController::Get()->GetSnapGroupForGivenWindow(w1.get()));
EXPECT_FALSE(
SnapGroupController::Get()->GetSnapGroupForGivenWindow(w2.get()));
EXPECT_EQ(display_ids[0],
display::Screen::Get()->GetDisplayNearestWindow(w1.get()).id());
EXPECT_EQ(display_ids[0],
display::Screen::Get()->GetDisplayNearestWindow(w2.get()).id());
}
TEST_F(SnapGroupMultiDisplayTest, DragWindowOutOfSnapGroupToAnotherDisplay) {
UpdateDisplay("800x700,801+0-800x700,1602+0-800x700");
display::DisplayManager* display_manager = Shell::Get()->display_manager();
const auto& displays = display_manager->active_display_list();
ASSERT_EQ(3U, displays.size());
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
const gfx::Point point_in_display2(802, 0);
ASSERT_FALSE(displays[0].bounds().Contains(point_in_display2));
ASSERT_TRUE(displays[1].bounds().Contains(point_in_display2));
event_generator->set_current_screen_location(GetDragPoint(w2.get()));
event_generator->DragMouseTo(point_in_display2);
ASSERT_FALSE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w1.get(), w2.get()));
display::Screen* screen = display::Screen::Get();
EXPECT_EQ(displays[1].id(), screen->GetDisplayNearestWindow(w2.get()).id());
EXPECT_EQ(chromeos::WindowStateType::kPrimarySnapped,
WindowState::Get(w2.get())->GetStateType());
gfx::Rect display1_left_half, display1_right_half;
displays[1].work_area().SplitVertically(display1_left_half,
display1_right_half);
EXPECT_EQ(display1_left_half, w2->GetBoundsInScreen());
}
TEST_F(SnapGroupMultiDisplayTest, MoveSnapGroupBetweenDisplays) {
UpdateDisplay("800x600,1000x600");
std::unique_ptr<aura::Window> w1(
CreateTestWindowInShell({.bounds = {100, 100}}));
w1->SetProperty(chromeos::kAppTypeKey, chromeos::AppType::CHROME_APP);
std::unique_ptr<aura::Window> w2(
CreateTestWindowInShell({.bounds = {100, 100}}));
w2->SetProperty(chromeos::kAppTypeKey, chromeos::AppType::CHROME_APP);
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
EXPECT_TRUE(SnapGroupController::Get()
->GetSnapGroupForGivenWindow(w1.get())
->snap_group_divider());
const int64_t primary_id = GetPrimaryDisplay().id();
display::Screen* screen = display::Screen::Get();
ASSERT_EQ(primary_id, screen->GetDisplayNearestWindow(w1.get()).id());
ASSERT_EQ(primary_id, screen->GetDisplayNearestWindow(w2.get()).id());
wm::ActivateWindow(w1.get());
MruWindowTracker* mru_window_tracker = Shell::Get()->mru_window_tracker();
aura::Window* mru_window = window_util::GetTopMostWindow(
mru_window_tracker->BuildMruWindowList(DesksMruType::kActiveDesk));
EXPECT_EQ(mru_window, w1.get());
PressAndReleaseKey(ui::VKEY_M, ui::EF_ALT_DOWN | ui::EF_COMMAND_DOWN);
const int64_t secondary_id = GetSecondaryDisplay().id();
ASSERT_EQ(secondary_id, screen->GetDisplayNearestWindow(w1.get()).id());
EXPECT_EQ(secondary_id, screen->GetDisplayNearestWindow(w2.get()).id());
EXPECT_FALSE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w1.get(), w2.get()));
auto list = Shell::Get()->mru_window_tracker()->BuildMruWindowList(
DesksMruType::kActiveDesk);
ASSERT_EQ(2u, list.size());
EXPECT_EQ(w2.get(), window_util::GetTopMostWindow(list));
}
TEST_F(SnapGroupMultiDisplayTest,
MirrorSnapGroupWhenMovingAcrossDisplaysInOverview) {
UpdateDisplay("800x700,801+0-800x700");
display::DisplayManager* display_manager = Shell::Get()->display_manager();
const auto& displays = display_manager->active_display_list();
ASSERT_EQ(2U, displays.size());
const gfx::Point point_in_display1(400, 100);
const gfx::Point point_in_display2(1000, 100);
EXPECT_FALSE(displays[0].bounds().Contains(point_in_display2));
EXPECT_TRUE(displays[1].bounds().Contains(point_in_display2));
std::unique_ptr<aura::Window> w1(CreateAppWindow(gfx::Rect(0, 0, 200, 100)));
std::unique_ptr<aura::Window> w2(
CreateAppWindow(gfx::Rect(50, 50, 100, 200)));
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
auto* snap_group_controller = SnapGroupController::Get();
VerifySnapGroupOnDisplay(
snap_group_controller->GetSnapGroupForGivenWindow(w1.get()),
displays[0].id());
ToggleOverview();
ASSERT_TRUE(IsInOverviewSession());
OverviewGroupItem* group_item =
static_cast<OverviewGroupItem*>(GetOverviewItemForWindow(w1.get()));
DragGroupItemToPoint(group_item, point_in_display2, event_generator,
false, false);
for (const auto& item : group_item->overview_items_for_testing()) {
EXPECT_TRUE(item->item_mirror_for_dragging_for_testing());
EXPECT_TRUE(item->window_mirror_for_dragging_for_testing());
}
event_generator->MoveMouseTo(point_in_display1);
event_generator->ReleaseLeftButton();
EXPECT_TRUE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w1.get(), w2.get()));
display::Screen* screen = display::Screen::Get();
EXPECT_EQ(displays[0].id(), screen->GetDisplayNearestWindow(w1.get()).id());
EXPECT_EQ(displays[0].id(), screen->GetDisplayNearestWindow(w2.get()).id());
}
TEST_F(SnapGroupMultiDisplayTest,
MoveSnapGroupToAnotherDisplayWithSnapGroupInOverview) {
UpdateDisplay("800x700,800x700");
display::DisplayManager* display_manager = Shell::Get()->display_manager();
const auto& displays = display_manager->active_display_list();
ASSERT_EQ(2U, displays.size());
const gfx::Point point_in_display1(100, 10);
EXPECT_TRUE(displays[0].bounds().Contains(point_in_display1));
EXPECT_FALSE(displays[1].bounds().Contains(point_in_display1));
const gfx::Point point_in_display2(1000, 100);
EXPECT_FALSE(displays[0].bounds().Contains(point_in_display2));
EXPECT_TRUE(displays[1].bounds().Contains(point_in_display2));
std::unique_ptr<aura::Window> w1(CreateAppWindow(gfx::Rect(0, 0, 200, 100)));
std::unique_ptr<aura::Window> w2(
CreateAppWindow(gfx::Rect(50, 50, 100, 200)));
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
auto* snap_group_controller = SnapGroupController::Get();
VerifySnapGroupOnDisplay(
snap_group_controller->GetSnapGroupForGivenWindow(w1.get()),
displays[0].id());
std::unique_ptr<aura::Window> w3(
CreateAppWindow(gfx::Rect(900, 0, 200, 100)));
std::unique_ptr<aura::Window> w4(
CreateAppWindow(gfx::Rect(1000, 50, 100, 200)));
SnapTwoTestWindows(w3.get(), w4.get(), true, event_generator);
VerifySnapGroupOnDisplay(
snap_group_controller->GetSnapGroupForGivenWindow(w3.get()),
displays[1].id());
OverviewController* overview_controller = OverviewController::Get();
overview_controller->StartOverview(OverviewStartAction::kOverviewButton);
ASSERT_TRUE(overview_controller->InOverviewSession());
DragGroupItemToPoint(GetOverviewItemForWindow(w3.get()), point_in_display1,
event_generator,
false, true);
DragGroupItemToPoint(GetOverviewItemForWindow(w1.get()), point_in_display2,
event_generator,
false, true);
display::Screen* screen = display::Screen::Get();
EXPECT_FALSE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_EQ(displays[1].id(), screen->GetDisplayNearestWindow(w1.get()).id());
EXPECT_EQ(displays[1].id(), screen->GetDisplayNearestWindow(w2.get()).id());
EXPECT_FALSE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w3.get(), w4.get()));
EXPECT_EQ(displays[0].id(), screen->GetDisplayNearestWindow(w3.get()).id());
EXPECT_EQ(displays[0].id(), screen->GetDisplayNearestWindow(w4.get()).id());
}
TEST_F(SnapGroupMultiDisplayTest,
MoveSnapGroupToADifferentDeskInAnotherDisplayInOverview) {
UpdateDisplay("800x700,801+0-800x700");
display::DisplayManager* display_manager = Shell::Get()->display_manager();
const auto& displays = display_manager->active_display_list();
ASSERT_EQ(2U, displays.size());
aura::Window::Windows root_windows = Shell::GetAllRootWindows();
ASSERT_EQ(2U, root_windows.size());
auto* root2 = root_windows[1].get();
auto* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
ASSERT_EQ(2u, desks_controller->desks().size());
std::unique_ptr<aura::Window> w1(CreateAppWindow(gfx::Rect(0, 0, 200, 100)));
std::unique_ptr<aura::Window> w2(
CreateAppWindow(gfx::Rect(50, 50, 100, 200)));
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
auto* snap_group_controller = SnapGroupController::Get();
VerifySnapGroupOnDisplay(
snap_group_controller->GetSnapGroupForGivenWindow(w1.get()),
displays[0].id());
ToggleOverview();
ASSERT_TRUE(IsInOverviewSession());
auto* overview_grid = GetOverviewGridForRoot(root2);
ASSERT_TRUE(overview_grid);
const auto* desks_bar_view = overview_grid->desks_bar_view();
ASSERT_TRUE(desks_bar_view);
const auto& desks_mini_views = desks_bar_view->mini_views();
ASSERT_EQ(desks_mini_views.size(), 2u);
const auto drop_point =
desks_mini_views[1]->GetBoundsInScreen().CenterPoint();
DragGroupItemToPoint(GetOverviewItemForWindow(w1.get()), drop_point,
event_generator,
false, true);
event_generator->MoveMouseTo(drop_point);
DeskSwitchAnimationWaiter waiter;
event_generator->ClickLeftButton();
waiter.Wait();
ASSERT_FALSE(IsInOverviewSession());
EXPECT_FALSE(GetTopmostSnapGroupDivider());
display::Screen* screen = display::Screen::Get();
EXPECT_EQ(displays[1].id(), screen->GetDisplayNearestWindow(w1.get()).id());
EXPECT_EQ(displays[1].id(), screen->GetDisplayNearestWindow(w2.get()).id());
EXPECT_TRUE(desks_util::IsActiveDeskContainer(w1->parent()));
EXPECT_TRUE(desks_util::IsActiveDeskContainer(w2->parent()));
EXPECT_TRUE(w1->IsVisible());
EXPECT_TRUE(w2->IsVisible());
}
TEST_F(SnapGroupMultiDisplayTest, DeskChangeWithMultiDisplay) {
UpdateDisplay("800x700,801+0-800x700");
display::DisplayManager* display_manager = Shell::Get()->display_manager();
const auto& displays = display_manager->active_display_list();
ASSERT_EQ(2U, displays.size());
std::unique_ptr<aura::Window> w1(
CreateAppWindow(gfx::Rect(900, 0, 200, 100)));
std::unique_ptr<aura::Window> w2(
CreateAppWindow(gfx::Rect(1000, 50, 100, 200)));
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
SnapGroupController* snap_group_controller = SnapGroupController::Get();
VerifySnapGroupOnDisplay(
snap_group_controller->GetSnapGroupForGivenWindow(w1.get()),
displays[1].id());
display::Screen* screen = display::Screen::Get();
ASSERT_EQ(displays[1].id(), screen->GetDisplayNearestWindow(w1.get()).id());
ASSERT_EQ(displays[1].id(), screen->GetDisplayNearestWindow(w2.get()).id());
auto* desks_controller = DesksController::Get();
desks_controller->NewDesk(DesksCreationRemovalSource::kButton);
ASSERT_EQ(2u, desks_controller->desks().size());
const Desk* desk0 = desks_controller->GetDeskAtIndex(0);
const Desk* desk1 = desks_controller->GetDeskAtIndex(1);
ASSERT_TRUE(desk0->is_active());
PressAndReleaseKey(ui::VKEY_OEM_6, ui::EF_COMMAND_DOWN);
DeskSwitchAnimationWaiter().Wait();
ASSERT_TRUE(desk1->is_active());
PressAndReleaseKey(ui::VKEY_OEM_4, ui::EF_COMMAND_DOWN);
DeskSwitchAnimationWaiter().Wait();
ASSERT_TRUE(desk0->is_active());
EXPECT_EQ(displays[1].id(), screen->GetDisplayNearestWindow(w1.get()).id());
EXPECT_EQ(displays[1].id(), screen->GetDisplayNearestWindow(w2.get()).id());
VerifySnapGroupOnDisplay(
snap_group_controller->GetSnapGroupForGivenWindow(w1.get()),
displays[1].id());
}
TEST_F(SnapGroupMultiDisplayTest, MirroredMode) {
UpdateDisplay("800x700,801+0-800x700");
display::DisplayManager* display_manager = Shell::Get()->display_manager();
const auto& displays = display_manager->active_display_list();
ASSERT_EQ(2U, displays.size());
const int64_t primary_id = displays[0].id();
const int64_t secondary_id = displays[1].id();
std::unique_ptr<aura::Window> w1(CreateAppWindow(gfx::Rect(0, 0, 200, 100)));
std::unique_ptr<aura::Window> w2(
CreateAppWindow(gfx::Rect(50, 50, 100, 200)));
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
auto* snap_group_controller = SnapGroupController::Get();
auto* group1 = snap_group_controller->GetSnapGroupForGivenWindow(w1.get());
VerifySnapGroupOnDisplay(group1, primary_id);
std::unique_ptr<aura::Window> w3(
CreateAppWindow(gfx::Rect(900, 0, 200, 100)));
std::unique_ptr<aura::Window> w4(
CreateAppWindow(gfx::Rect(1000, 50, 100, 200)));
SnapTwoTestWindows(w3.get(), w4.get(), true, event_generator);
auto* group2 = snap_group_controller->GetSnapGroupForGivenWindow(w3.get());
VerifySnapGroupOnDisplay(group2, secondary_id);
display_manager->SetMirrorMode(display::MirrorMode::kNormal, std::nullopt);
ASSERT_EQ(1U, displays.size());
VerifySnapGroupOnDisplay(group1, primary_id);
EXPECT_FALSE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w3.get(), w4.get()));
display_manager->SetMirrorMode(display::MirrorMode::kOff, std::nullopt);
ASSERT_EQ(2U, displays.size());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
group1->snap_group_divider());
EXPECT_FALSE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w3.get(), w4.get()));
}
TEST_F(SnapGroupMultiDisplayTest, ToggleMirrorMode) {
UpdateDisplay("800x700,801+0-800x700");
display::DisplayManager* display_manager = Shell::Get()->display_manager();
const auto& displays = display_manager->active_display_list();
ASSERT_EQ(2U, displays.size());
const gfx::Point point_in_display2(1000, 100);
EXPECT_FALSE(displays[0].bounds().Contains(point_in_display2));
EXPECT_TRUE(displays[1].bounds().Contains(point_in_display2));
std::unique_ptr<aura::Window> w1(
CreateAppWindow(gfx::Rect(1000, 0, 200, 100)));
std::unique_ptr<aura::Window> w2(
CreateAppWindow(gfx::Rect(1050, 50, 100, 200)));
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
auto* snap_group_controller = SnapGroupController::Get();
SnapGroup* snap_group =
snap_group_controller->GetSnapGroupForGivenWindow(w1.get());
VerifySnapGroupOnDisplay(snap_group, displays[1].id());
display_manager->SetMirrorMode(display::MirrorMode::kNormal, std::nullopt);
ASSERT_EQ(1U, displays.size());
EXPECT_FALSE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w1.get(), w2.get()));
base::RunLoop().RunUntilIdle();
display_manager->SetMirrorMode(display::MirrorMode::kOff, std::nullopt);
ASSERT_EQ(2U, displays.size());
EXPECT_FALSE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w1.get(), w2.get()));
base::RunLoop().RunUntilIdle();
}
TEST_F(SnapGroupMultiDisplayTest, LandscapeAndPortrait) {
UpdateDisplay("800x600,600x800");
std::unique_ptr<aura::Window> w1(
CreateAppWindow(gfx::Rect(0, 400, 200, 200)));
std::unique_ptr<aura::Window> w2(
CreateAppWindow(gfx::Rect(800, 0, 200, 200)));
wm::ActivateWindow(w1.get());
auto* event_generator = GetEventGenerator();
event_generator->set_current_screen_location(GetDragPoint(w1.get()));
const display::Display display2 =
display::test::DisplayManagerTestApi(display_manager())
.GetSecondaryDisplay();
ASSERT_FALSE(IsLayoutHorizontal(display2));
const gfx::Rect work_area2 = display2.work_area();
event_generator->DragMouseTo(work_area2.top_center());
gfx::Rect top_half, bottom_half;
work_area2.SplitHorizontally(top_half, bottom_half);
EXPECT_EQ(chromeos::WindowStateType::kPrimarySnapped,
WindowState::Get(w1.get())->GetStateType());
EXPECT_EQ(top_half, w1->GetBoundsInScreen());
ClickOverviewItem(event_generator, w2.get());
EXPECT_EQ(chromeos::WindowStateType::kSecondarySnapped,
WindowState::Get(w2.get())->GetStateType());
EXPECT_TRUE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w1.get(), w2.get()));
const gfx::Rect divider_bounds(
work_area2.x(),
work_area2.CenterPoint().y() - kSplitviewDividerShortSideLength / 2,
work_area2.width(), kSplitviewDividerShortSideLength);
EXPECT_EQ(divider_bounds, GetTopmostSnapGroupDividerBoundsInScreen());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
}
TEST_F(SnapGroupMultiDisplayTest, AddRemovePrimaryDisplay) {
UpdateDisplay("800x700,801+0-800x700");
const int64_t primary_id = WindowTreeHostManager::GetPrimaryDisplayId();
const int64_t secondary_id =
display::test::DisplayManagerTestApi(display_manager())
.GetSecondaryDisplay()
.id();
std::unique_ptr<aura::Window> w1(CreateAppWindow(gfx::Rect(0, 0, 200, 100)));
std::unique_ptr<aura::Window> w2(
CreateAppWindow(gfx::Rect(50, 50, 100, 200)));
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
auto* snap_group_controller = SnapGroupController::Get();
auto* group1 = snap_group_controller->GetSnapGroupForGivenWindow(w1.get());
VerifySnapGroupOnDisplay(group1, primary_id);
std::unique_ptr<aura::Window> w3(
CreateAppWindow(gfx::Rect(801, 0, 200, 100)));
std::unique_ptr<aura::Window> w4(
CreateAppWindow(gfx::Rect(810, 50, 100, 200)));
SnapTwoTestWindows(w3.get(), w4.get(), true, event_generator);
auto* group2 = snap_group_controller->GetSnapGroupForGivenWindow(w3.get());
VerifySnapGroupOnDisplay(group2, secondary_id);
display::ManagedDisplayInfo primary_info =
display_manager()->GetDisplayInfo(primary_id);
display::ManagedDisplayInfo secondary_info =
display_manager()->GetDisplayInfo(secondary_id);
std::vector<display::ManagedDisplayInfo> display_info_list;
display_info_list.push_back(secondary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
const auto& displays = display_manager()->active_display_list();
ASSERT_EQ(1U, displays.size());
ASSERT_EQ(WindowTreeHostManager::GetPrimaryDisplayId(), secondary_id);
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
group1->snap_group_divider());
EXPECT_FALSE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w3.get(), w4.get()));
display_info_list.push_back(primary_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
ASSERT_EQ(2U, displays.size());
ASSERT_EQ(WindowTreeHostManager::GetPrimaryDisplayId(), primary_id);
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
group1->snap_group_divider());
EXPECT_FALSE(
SnapGroupController::Get()->AreWindowsInSnapGroup(w3.get(), w4.get()));
}
TEST_F(SnapGroupMultiDisplayTest, AddRemovePrimaryDisplayAfterResize) {
UpdateDisplay("1200x900,0+901-1200x900/u");
ASSERT_EQ(2U, display_manager()->active_display_list().size());
std::unique_ptr<aura::Window> w1(CreateAppWindow(gfx::Rect(0, 0, 200, 100)));
std::unique_ptr<aura::Window> w2(
CreateAppWindow(gfx::Rect(50, 50, 100, 200)));
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
auto* snap_group_controller = SnapGroupController::Get();
auto* snap_group =
snap_group_controller->GetSnapGroupForGivenWindow(w1.get());
ASSERT_TRUE(snap_group);
auto* snap_group_divider = snap_group->snap_group_divider();
const gfx::Point divider_point(
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint());
event_generator->set_current_screen_location(divider_point);
event_generator->PressLeftButton();
const gfx::Point resize_point(350, divider_point.y());
event_generator->MoveMouseTo(resize_point, 22);
event_generator->ReleaseLeftButton();
EXPECT_EQ(resize_point.x(),
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint().x());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(), snap_group_divider);
UpdateDisplay("1200x900/u");
ASSERT_EQ(1U, display_manager()->active_display_list().size());
UnionBoundsEqualToWorkAreaBounds(w2.get(), w1.get(), snap_group_divider);
UpdateDisplay("1200x900,0+901-1200x900/u");
ASSERT_EQ(2U, display_manager()->active_display_list().size());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(), snap_group_divider);
}
TEST_F(SnapGroupMultiDisplayTest, ResizeCursorBetweenDisplays) {
UpdateDisplay("800x700,801+0-800x700");
const int min_width = 300;
std::unique_ptr<aura::Window> w1(
CreateAppWindowWithMinSize(gfx::Size(min_width, 0)));
std::unique_ptr<aura::Window> w2(
CreateAppWindowWithMinSize(gfx::Size(min_width, 0)));
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
auto* snap_group =
SnapGroupController::Get()->GetSnapGroupForGivenWindow(w1.get());
auto* snap_group_divider = snap_group->snap_group_divider();
const gfx::Point divider_point(
snap_group_divider->GetDividerBoundsInScreen(false)
.CenterPoint());
event_generator->set_current_screen_location(divider_point);
event_generator->PressLeftButton();
event_generator->MoveMouseTo(gfx::Point(350, divider_point.y()), 2);
ASSERT_TRUE(snap_group_divider->is_resizing_with_divider());
EXPECT_EQ(350, GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint().x());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(), snap_group_divider);
event_generator->MoveMouseTo(gfx::Point(810, divider_point.y()), 2);
EXPECT_EQ(min_width, w2->GetBoundsInScreen().width());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(), snap_group_divider);
event_generator->MoveMouseTo(gfx::Point(799, divider_point.y()),
2);
EXPECT_EQ(min_width, w2->GetBoundsInScreen().width());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(), snap_group_divider);
event_generator->MoveMouseTo(gfx::Point(350, divider_point.y()), 2);
EXPECT_EQ(350, GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint().x());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(), snap_group_divider);
event_generator->ReleaseLeftButton();
}
TEST_F(SnapGroupMultiDisplayTest, GroupItemCrossDisplayDragInteractivity) {
UpdateDisplay("800x700,801+0-800x700");
display::DisplayManager* display_manager = Shell::Get()->display_manager();
const auto& displays = display_manager->active_display_list();
ASSERT_EQ(2U, displays.size());
aura::Window::Windows root_windows = Shell::GetAllRootWindows();
ASSERT_EQ(2U, root_windows.size());
const gfx::Point point_in_display1(100, 10);
EXPECT_TRUE(displays[0].bounds().Contains(point_in_display1));
EXPECT_FALSE(displays[1].bounds().Contains(point_in_display1));
const gfx::Point point_in_display2(1000, 100);
EXPECT_FALSE(displays[0].bounds().Contains(point_in_display2));
EXPECT_TRUE(displays[1].bounds().Contains(point_in_display2));
std::unique_ptr<aura::Window> w1(CreateAppWindow(gfx::Rect(0, 0, 200, 100)));
std::unique_ptr<aura::Window> w2(
CreateAppWindow(gfx::Rect(50, 50, 100, 200)));
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
auto* snap_group_controller = SnapGroupController::Get();
VerifySnapGroupOnDisplay(
snap_group_controller->GetSnapGroupForGivenWindow(w1.get()),
displays[0].id());
ToggleOverview();
ASSERT_TRUE(IsInOverviewSession());
OverviewGroupItem* overview_group_item =
static_cast<OverviewGroupItem*>(GetOverviewItemForWindow(w1.get()));
ASSERT_TRUE(overview_group_item);
const auto& overview_items =
overview_group_item->overview_items_for_testing();
ASSERT_EQ(2u, overview_items.size());
auto* group_item_widget = overview_group_item->item_widget();
ASSERT_TRUE(group_item_widget);
auto* group_item_widget_window = group_item_widget->GetNativeWindow();
for (const auto& overview_item : overview_items) {
EXPECT_TRUE(window_util::IsStackedBelow(
group_item_widget_window,
overview_item->item_widget()->GetNativeWindow()));
}
DragGroupItemToPoint(overview_group_item, point_in_display2, event_generator,
false, false);
DragGroupItemToPoint(overview_group_item, point_in_display1, event_generator,
false, true);
for (const auto& overview_item : overview_items) {
EXPECT_TRUE(window_util::IsStackedBelow(
group_item_widget_window,
overview_item->item_widget()->GetNativeWindow()));
}
event_generator->MoveMouseTo(
gfx::ToRoundedPoint(overview_group_item->target_bounds().CenterPoint()) +
gfx::Vector2d(10, 0));
event_generator->ClickLeftButton();
VerifyNotSplitViewOrOverviewSession(w1.get());
display::Screen* screen = display::Screen::Get();
EXPECT_EQ(displays[0].id(), screen->GetDisplayNearestWindow(w1.get()).id());
EXPECT_EQ(displays[0].id(), screen->GetDisplayNearestWindow(w2.get()).id());
VerifySnapGroupOnDisplay(
snap_group_controller->GetSnapGroupForGivenWindow(w1.get()),
displays[0].id());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(),
GetTopmostSnapGroupDivider());
}
TEST_F(SnapGroupMultiDisplayTest, NewDeskButtonStateUpdateOnMultiDisplay) {
auto skip_scale_up_new_desk_button_duration = OverviewWindowDragController::
SkipNewDeskButtonScaleUpDurationForTesting();
UpdateDisplay("800x700,801+0-800x700");
display::DisplayManager* display_manager = Shell::Get()->display_manager();
const auto& displays = display_manager->active_display_list();
ASSERT_EQ(2U, displays.size());
const gfx::Point point_in_display1(502, 300);
ASSERT_TRUE(displays[0].bounds().Contains(point_in_display1));
ASSERT_FALSE(displays[1].bounds().Contains(point_in_display1));
std::unique_ptr<aura::Window> w1(CreateAppWindow(gfx::Rect(0, 0, 200, 100)));
std::unique_ptr<aura::Window> w2(
CreateAppWindow(gfx::Rect(50, 50, 100, 200)));
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
auto* snap_group_controller = SnapGroupController::Get();
VerifySnapGroupOnDisplay(
snap_group_controller->GetSnapGroupForGivenWindow(w1.get()),
displays[0].id());
ToggleOverview();
ASSERT_TRUE(IsInOverviewSession());
ASSERT_TRUE(IsWindowInItsCorrespondingOverviewGrid(w1.get()));
const auto& grids = GetOverviewSession()->grid_list();
ASSERT_EQ(2u, grids.size());
auto* grid0 = grids[0].get();
ASSERT_TRUE(grid0);
auto* desks_bar_view0 = grid0->desks_bar_view();
const DeskIconButton* new_desk_button0 = desks_bar_view0->new_desk_button();
ASSERT_TRUE(new_desk_button0);
ASSERT_TRUE(new_desk_button0->GetVisible());
ASSERT_EQ(DeskIconButton::State::kZero, new_desk_button0->state());
auto* grid1 = grids[1].get();
ASSERT_TRUE(grid1);
auto* desks_bar_view1 = grid1->desks_bar_view();
const DeskIconButton* new_desk_button1 = desks_bar_view1->new_desk_button();
ASSERT_TRUE(new_desk_button1);
ASSERT_TRUE(new_desk_button1->GetVisible());
ASSERT_EQ(DeskIconButton::State::kZero, new_desk_button1->state());
OverviewItemBase* overview_group_item = GetOverviewItemForWindow(w1.get());
ASSERT_TRUE(overview_group_item);
DragGroupItemToPoint(
overview_group_item, new_desk_button1->GetBoundsInScreen().CenterPoint(),
event_generator, false, false);
EXPECT_EQ(DeskIconButton::State::kExpanded, new_desk_button0->state());
EXPECT_EQ(DeskIconButton::State::kActive, new_desk_button1->state());
DragItemToPoint(overview_group_item, point_in_display1, event_generator,
false, true);
EXPECT_EQ(DeskIconButton::State::kExpanded, new_desk_button0->state());
EXPECT_EQ(DeskIconButton::State::kExpanded, new_desk_button1->state());
}
using SnapGroupA11yTest = SnapGroupTest;
TEST_F(SnapGroupA11yTest, DividerPaneFocus) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
auto* snap_group =
SnapGroupController::Get()->GetSnapGroupForGivenWindow(w1.get());
ASSERT_TRUE(snap_group);
auto* snap_group_divider = snap_group->snap_group_divider();
auto* divider_widget = snap_group_divider->divider_widget();
EXPECT_FALSE(divider_widget->IsActive());
auto* focus_ring =
views::FocusRing::Get(snap_group_divider->divider_view_for_testing());
ASSERT_TRUE(focus_ring);
EXPECT_FALSE(focus_ring->GetVisible());
event_generator->PressKey(ui::VKEY_BROWSER_BACK, ui::EF_CONTROL_DOWN);
EXPECT_TRUE(divider_widget->IsActive());
EXPECT_TRUE(focus_ring->GetVisible());
constexpr int kFocusRingPaddingDp = 8;
EXPECT_TRUE(focus_ring->GetBoundsInScreen().ApproximatelyEqual(
GetTopmostSnapGroupDividerBoundsInScreen(),
kFocusRingPaddingDp));
event_generator->PressKey(ui::VKEY_BROWSER_BACK, ui::EF_CONTROL_DOWN);
EXPECT_FALSE(divider_widget->IsActive());
EXPECT_FALSE(focus_ring->GetVisible());
event_generator->PressKey(ui::VKEY_BROWSER_FORWARD, ui::EF_CONTROL_DOWN);
EXPECT_TRUE(divider_widget->IsActive());
EXPECT_TRUE(focus_ring->GetVisible());
EXPECT_TRUE(focus_ring->GetBoundsInScreen().ApproximatelyEqual(
GetTopmostSnapGroupDividerBoundsInScreen(),
kFocusRingPaddingDp));
}
TEST_F(SnapGroupA11yTest, DividerResize) {
TestAccessibilityControllerClient client;
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
auto* snap_group =
SnapGroupController::Get()->GetSnapGroupForGivenWindow(w1.get());
ASSERT_TRUE(snap_group);
auto* snap_group_divider = snap_group->snap_group_divider();
auto* divider_widget = snap_group_divider->divider_widget();
PressAndReleaseKey(ui::VKEY_BROWSER_BACK, ui::EF_CONTROL_DOWN);
ASSERT_TRUE(divider_widget->IsActive());
gfx::Point divider_center =
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint();
PressAndReleaseKey(ui::VKEY_LEFT);
EXPECT_EQ(divider_center + gfx::Vector2d(-kSplitViewDividerResizeDistance, 0),
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(), snap_group_divider);
EXPECT_EQ(AccessibilityAlert::SNAP_GROUP_RESIZE_LEFT,
client.last_a11y_alert());
PressAndReleaseKey(ui::VKEY_RIGHT);
EXPECT_EQ(divider_center,
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(), snap_group_divider);
EXPECT_EQ(AccessibilityAlert::SNAP_GROUP_RESIZE_RIGHT,
client.last_a11y_alert());
UpdateDisplay("800x600/u");
divider_center = GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint();
PressAndReleaseKey(ui::VKEY_LEFT);
EXPECT_EQ(divider_center + gfx::Vector2d(-kSplitViewDividerResizeDistance, 0),
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint());
UnionBoundsEqualToWorkAreaBounds(w2.get(), w1.get(), snap_group_divider);
EXPECT_EQ(AccessibilityAlert::SNAP_GROUP_RESIZE_LEFT,
client.last_a11y_alert());
PressAndReleaseKey(ui::VKEY_RIGHT);
EXPECT_EQ(divider_center,
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint());
UnionBoundsEqualToWorkAreaBounds(w2.get(), w1.get(), snap_group_divider);
EXPECT_EQ(AccessibilityAlert::SNAP_GROUP_RESIZE_RIGHT,
client.last_a11y_alert());
UpdateDisplay("600x800");
divider_center = GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint();
PressAndReleaseKey(ui::VKEY_UP);
EXPECT_EQ(divider_center + gfx::Vector2d(0, -kSplitViewDividerResizeDistance),
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(), snap_group_divider);
EXPECT_EQ(AccessibilityAlert::SNAP_GROUP_RESIZE_UP, client.last_a11y_alert());
PressAndReleaseKey(ui::VKEY_DOWN);
EXPECT_EQ(divider_center,
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(), snap_group_divider);
EXPECT_EQ(AccessibilityAlert::SNAP_GROUP_RESIZE_DOWN,
client.last_a11y_alert());
UpdateDisplay("600x800/u");
divider_center = GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint();
PressAndReleaseKey(ui::VKEY_UP);
EXPECT_EQ(divider_center + gfx::Vector2d(0, -kSplitViewDividerResizeDistance),
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint());
UnionBoundsEqualToWorkAreaBounds(w2.get(), w1.get(), snap_group_divider);
EXPECT_EQ(AccessibilityAlert::SNAP_GROUP_RESIZE_UP, client.last_a11y_alert());
PressAndReleaseKey(ui::VKEY_DOWN);
EXPECT_EQ(divider_center,
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint());
UnionBoundsEqualToWorkAreaBounds(w2.get(), w1.get(), snap_group_divider);
EXPECT_EQ(AccessibilityAlert::SNAP_GROUP_RESIZE_DOWN,
client.last_a11y_alert());
}
TEST_F(SnapGroupA11yTest, ResizeVertical) {
UpdateDisplay("600x800");
const gfx::Rect work_area_without_cvox(GetWorkAreaBounds());
const int kAccessibilityPanelHeight = 45;
std::unique_ptr<views::Widget> widget =
CreateTestWidget(views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET,
nullptr, kShellWindowId_AccessibilityPanelContainer);
SetAccessibilityPanelHeight(kAccessibilityPanelHeight);
Shell::Get()->accessibility_controller()->spoken_feedback().SetEnabled(true);
const gfx::Rect work_area_with_cvox(GetWorkAreaBounds());
ASSERT_NE(work_area_without_cvox, work_area_with_cvox);
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), false,
GetEventGenerator());
auto* snap_group =
SnapGroupController::Get()->GetSnapGroupForGivenWindow(w1.get());
ASSERT_TRUE(snap_group);
auto* snap_group_divider = snap_group->snap_group_divider();
const gfx::Point divider_center =
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint();
ASSERT_EQ(work_area_with_cvox.CenterPoint(), divider_center);
ASSERT_EQ(
GetTopmostSnapGroupDividerBoundsInScreen().y() - work_area_with_cvox.y(),
snap_group_divider->divider_position());
PressAndReleaseKey(ui::VKEY_BROWSER_BACK, ui::EF_CONTROL_DOWN);
ASSERT_TRUE(snap_group_divider->divider_widget()->IsActive());
ASSERT_EQ(GetWorkAreaBounds().CenterPoint(), divider_center);
PressAndReleaseKey(ui::VKEY_UP);
EXPECT_EQ(divider_center + gfx::Vector2d(0, -kSplitViewDividerResizeDistance),
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint());
UnionBoundsEqualToWorkAreaBounds(w1.get(), w2.get(), snap_group_divider);
}
class SnapGroupMetricsTest : public SnapGroupTest {
public:
SnapGroupMetricsTest()
: SnapGroupTest(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
~SnapGroupMetricsTest() override = default;
void AdvanceClock(base::TimeDelta delta) {
task_environment()->AdvanceClock(delta);
task_environment()->RunUntilIdle();
}
protected:
base::HistogramTester histogram_tester_;
base::UserActionTester user_action_tester_;
};
TEST_F(SnapGroupMetricsTest, SnapActionSourcePipeline) {
UpdateDisplay("800x600");
std::unique_ptr<aura::Window> window1(CreateAppWindow(gfx::Rect(100, 100)));
std::unique_ptr<aura::Window> window2(CreateAppWindow(gfx::Rect(200, 100)));
std::unique_ptr<WindowResizer> resizer(CreateWindowResizer(
window1.get(), gfx::PointF(), HTCAPTION, wm::WINDOW_MOVE_SOURCE_MOUSE));
resizer->Drag(gfx::PointF(0, 400), 0);
resizer->CompleteDrag();
resizer.reset();
VerifySplitViewOverviewSession(window1.get());
EXPECT_EQ(GetSplitViewOverviewSession(window1.get())
->snap_action_source_for_testing(),
WindowSnapActionSource::kDragWindowToEdgeToSnap);
MaximizeToClearTheSession(window1.get());
chromeos::SnapController::Get()->CommitSnap(
window1.get(), chromeos::SnapDirection::kSecondary,
chromeos::kDefaultSnapRatio,
chromeos::SnapController::SnapRequestSource::kWindowLayoutMenu);
VerifySplitViewOverviewSession(window1.get());
EXPECT_EQ(GetSplitViewOverviewSession(window1.get())
->snap_action_source_for_testing(),
WindowSnapActionSource::kSnapByWindowLayoutMenu);
MaximizeToClearTheSession(window1.get());
chromeos::SnapController::Get()->CommitSnap(
window1.get(), chromeos::SnapDirection::kPrimary,
chromeos::kDefaultSnapRatio,
chromeos::SnapController::SnapRequestSource::kSnapButton);
VerifySplitViewOverviewSession(window1.get());
EXPECT_EQ(GetSplitViewOverviewSession(window1.get())
->snap_action_source_for_testing(),
WindowSnapActionSource::kLongPressCaptionButtonToSnap);
MaximizeToClearTheSession(window1.get());
}
TEST_F(SnapGroupMetricsTest, SnapGroupDuration) {
const std::string persistence_duration_histogram_name =
BuildHistogramName(kSnapGroupPersistenceDurationRootWord);
histogram_tester_.ExpectTotalCount(persistence_duration_histogram_name, 0);
const std::string actual_duration_histogram_name =
BuildHistogramName(kSnapGroupActualDurationRootWord);
histogram_tester_.ExpectTotalCount(actual_duration_histogram_name, 0);
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
SnapGroupController* snap_group_controller = SnapGroupController::Get();
SnapGroup* snap_group =
snap_group_controller->GetSnapGroupForGivenWindow(w1.get());
ASSERT_TRUE(snap_group);
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
AdvanceClock(base::Seconds(10));
std::unique_ptr<aura::Window> w3(CreateAppWindow());
SnapOneTestWindow(w3.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w3.get(), w2.get()));
EXPECT_FALSE(
snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
histogram_tester_.ExpectTotalCount(persistence_duration_histogram_name, 0);
histogram_tester_.ExpectBucketCount(actual_duration_histogram_name,
10, 1);
AdvanceClock(base::Seconds(10));
snap_group_controller->RemoveSnapGroup(
snap_group_controller->GetSnapGroupForGivenWindow(w2.get()),
SnapGroupExitPoint::kDragWindowOut);
histogram_tester_.ExpectBucketCount(persistence_duration_histogram_name,
20, 1);
histogram_tester_.ExpectBucketCount(actual_duration_histogram_name,
10, 2);
}
TEST_F(SnapGroupMetricsTest, SnapGroupExitPoint) {
const std::string snap_group_exit_point =
BuildHistogramName(kSnapGroupExitPointRootWord);
histogram_tester_.ExpectTotalCount(snap_group_exit_point, 0);
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
SnapGroupController* snap_group_controller = SnapGroupController::Get();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
SCOPED_TRACE("Test case 1: drag window out to exit");
event_generator->MoveMouseTo(w1->GetBoundsInScreen().top_center());
event_generator->MoveMouseBy(0, 5);
event_generator->PressLeftButton();
event_generator->MoveMouseBy(50, 200);
EXPECT_TRUE(WindowState::Get(w1.get())->is_dragged());
EXPECT_EQ(WindowState::Get(w1.get())->drag_details()->bounds_change,
WindowResizer::kBoundsChange_Repositions);
event_generator->ReleaseLeftButton();
EXPECT_FALSE(snap_group_controller->GetSnapGroupForGivenWindow(w1.get()));
EXPECT_FALSE(snap_group_controller->GetSnapGroupForGivenWindow(w2.get()));
histogram_tester_.ExpectBucketCount(snap_group_exit_point,
SnapGroupExitPoint::kDragWindowOut, 1);
MaximizeToClearTheSession(w2.get());
SCOPED_TRACE("Test case 2: maximize window to exit");
std::unique_ptr<aura::Window> w3(CreateAppWindow());
SnapTwoTestWindows(w2.get(), w3.get(), true, event_generator);
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w2.get(), w3.get()));
WindowState* w2_state = WindowState::Get(w2.get());
w2_state->Maximize();
EXPECT_FALSE(snap_group_controller->GetSnapGroupForGivenWindow(w2.get()));
EXPECT_FALSE(snap_group_controller->GetSnapGroupForGivenWindow(w3.get()));
histogram_tester_.ExpectBucketCount(
snap_group_exit_point, SnapGroupExitPoint::kWindowStateChangedMaximized,
1);
MaximizeToClearTheSession(w2.get());
SCOPED_TRACE("Test case 3: minimize window to exit");
std::unique_ptr<aura::Window> w4(CreateAppWindow());
SnapTwoTestWindows(w3.get(), w4.get(), true, event_generator);
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w3.get(), w4.get()));
WindowState* w3_state = WindowState::Get(w3.get());
w3_state->Minimize();
EXPECT_FALSE(snap_group_controller->GetSnapGroupForGivenWindow(w3.get()));
EXPECT_FALSE(snap_group_controller->GetSnapGroupForGivenWindow(w4.get()));
histogram_tester_.ExpectBucketCount(
snap_group_exit_point, SnapGroupExitPoint::kWindowStateChangedMinimized,
1);
MaximizeToClearTheSession(w4.get());
SCOPED_TRACE("Test case 4: float window to exit");
std::unique_ptr<aura::Window> w5(CreateAppWindow());
SnapTwoTestWindows(w4.get(), w5.get(), true, event_generator);
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w4.get(), w5.get()));
WindowState* w4_state = WindowState::Get(w4.get());
const WindowFloatWMEvent float_event(
chromeos::FloatStartLocation::kBottomRight);
w4_state->OnWMEvent(&float_event);
EXPECT_FALSE(snap_group_controller->GetSnapGroupForGivenWindow(w4.get()));
EXPECT_FALSE(snap_group_controller->GetSnapGroupForGivenWindow(w5.get()));
histogram_tester_.ExpectBucketCount(
snap_group_exit_point, SnapGroupExitPoint::kWindowStateChangedFloated, 1);
MaximizeToClearTheSession(w5.get());
SCOPED_TRACE("Test case 5: window destruction to exit");
std::unique_ptr<aura::Window> w6(CreateAppWindow());
SnapTwoTestWindows(w5.get(), w6.get(), true, event_generator);
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w5.get(), w6.get()));
w5.reset();
EXPECT_FALSE(snap_group_controller->GetSnapGroupForGivenWindow(w6.get()));
histogram_tester_.ExpectBucketCount(
snap_group_exit_point, SnapGroupExitPoint::kWindowDestruction, 1);
SCOPED_TRACE("Test case 6: switch to tablet mode to exit");
std::unique_ptr<aura::Window> w7(CreateAppWindow());
SnapTwoTestWindows(w6.get(), w7.get(), true, event_generator);
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w6.get(), w7.get()));
SwitchToTabletMode();
EXPECT_FALSE(snap_group_controller->GetSnapGroupForGivenWindow(w6.get()));
EXPECT_FALSE(snap_group_controller->GetSnapGroupForGivenWindow(w7.get()));
histogram_tester_.ExpectBucketCount(snap_group_exit_point,
SnapGroupExitPoint::kTabletTransition, 1);
}
TEST_F(SnapGroupMetricsTest, SnapGroupsCount) {
UpdateDisplay("800x600");
const std::string snap_groups_count_histogram =
BuildHistogramName(kSnapGroupsCountRootWord);
histogram_tester_.ExpectTotalCount(snap_groups_count_histogram, 0);
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
auto* snap_group_controller = SnapGroupController::Get();
ASSERT_EQ(1u, snap_group_controller->snap_groups_for_testing().size());
histogram_tester_.ExpectBucketCount(snap_groups_count_histogram,
1,
1);
std::unique_ptr<aura::Window> w0(CreateAppWindow(gfx::Rect(0, 0, 800, 600)));
std::unique_ptr<aura::Window> w3(CreateAppWindow());
std::unique_ptr<aura::Window> w4(CreateAppWindow());
SnapTwoTestWindows(w3.get(), w4.get(), true, event_generator);
ASSERT_EQ(2u, snap_group_controller->snap_groups_for_testing().size());
histogram_tester_.ExpectBucketCount(snap_groups_count_histogram,
2,
1);
w3.reset();
ASSERT_EQ(1u, snap_group_controller->snap_groups_for_testing().size());
histogram_tester_.ExpectBucketCount(snap_groups_count_histogram,
1,
2);
wm::ActivateWindow(w1.get());
ASSERT_TRUE(snap_group_controller->GetTopmostVisibleSnapGroup(
w1->GetRootWindow(), true));
std::unique_ptr<aura::Window> w5(CreateAppWindow());
ASSERT_EQ(1u, snap_group_controller->snap_groups_for_testing().size());
SnapOneTestWindow(w5.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kDragWindowToEdgeToSnap);
ASSERT_EQ(1u, snap_group_controller->snap_groups_for_testing().size());
histogram_tester_.ExpectBucketCount(snap_groups_count_histogram,
1,
2);
histogram_tester_.ExpectTotalCount(snap_groups_count_histogram, 3);
}
TEST_F(SnapGroupMetricsTest, KeyboardshortcutToToggleSnapGroupHistogram) {
auto* event_generator = GetEventGenerator();
const std::string histogram_name = "Ash.Accelerators.Actions.ToggleSnapGroup";
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
TRACE_CALL(SnapWindowsSideBySide(kGrouped, w1.get(), w2.get()));
histogram_tester_.ExpectTotalCount(histogram_name, 0);
event_generator->PressAndReleaseKey(ui::VKEY_G,
ui::EF_SHIFT_DOWN | ui::EF_COMMAND_DOWN);
TRACE_CALL(ExpectWindowsSnappedSideBySide(kUngrouped, w1.get(), w2.get()));
histogram_tester_.ExpectTotalCount(histogram_name, 1);
event_generator->PressAndReleaseKey(ui::VKEY_G,
ui::EF_SHIFT_DOWN | ui::EF_COMMAND_DOWN);
TRACE_CALL(ExpectWindowsSnappedSideBySide(kGrouped, w1.get(), w2.get()));
histogram_tester_.ExpectTotalCount(histogram_name, 2);
std::unique_ptr<aura::Window> w3(CreateAppWindow());
SnapOneTestWindow(w3.get(), WindowStateType::kSecondarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kAutoSnapInSplitView);
TRACE_CALL(ExpectWindowsSnappedSideBySide(kGrouped, w1.get(), w2.get()));
event_generator->PressAndReleaseKey(ui::VKEY_G,
ui::EF_SHIFT_DOWN | ui::EF_COMMAND_DOWN);
TRACE_CALL(ExpectWindowsSnappedSideBySide(kGrouped, w1.get(), w3.get()));
histogram_tester_.ExpectTotalCount(histogram_name, 3);
}
TEST_F(SnapGroupMetricsTest, SnapGroupUserActions) {
UpdateDisplay("800x600");
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
auto* snap_group_controller = SnapGroupController::Get();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_EQ(
user_action_tester_.GetActionCount("SnapGroups_StartPartialOverview"), 1);
EXPECT_EQ(user_action_tester_.GetActionCount("SnapGroups_AddSnapGroup"), 1);
std::unique_ptr<aura::Window> w3(CreateAppWindow());
SnapOneTestWindow(w3.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kDragWindowToEdgeToSnap);
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w2.get(), w3.get()));
EXPECT_EQ(user_action_tester_.GetActionCount("SnapGroups_SnapToReplace"), 1);
const gfx::Point resize_point(w3->GetBoundsInScreen().right_center());
event_generator->MoveMouseTo(resize_point);
event_generator->DragMouseTo(150, resize_point.y());
std::unique_ptr<aura::Window> w4(CreateAppWindow());
SnapOneTestWindow(w4.get(), WindowStateType::kPrimarySnapped,
chromeos::kTwoThirdSnapRatio,
WindowSnapActionSource::kSnapByWindowLayoutMenu);
ASSERT_GT(GetSnapRatioGap(w4.get(), w2.get()),
kSnapToReplaceRatioDiffThreshold);
ASSERT_FALSE(snap_group_controller->GetSnapGroupForGivenWindow(w4.get()));
EXPECT_EQ(user_action_tester_.GetActionCount("SnapGroups_SnapDirect"), 1);
w3.reset();
ASSERT_FALSE(snap_group_controller->GetSnapGroupForGivenWindow(w3.get()));
EXPECT_EQ(user_action_tester_.GetActionCount("SnapGroups_RemoveSnapGroup"),
1);
}
TEST_F(SnapGroupMetricsTest, RecallSnapGroupUserAction) {
UpdateDisplay("800x600");
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
auto* snap_group_controller = SnapGroupController::Get();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
ASSERT_TRUE(wm::IsActiveWindow(w2.get()));
std::unique_ptr<aura::Window> w3(CreateAppWindow(GetWorkAreaBounds()));
wm::ActivateWindow(w1.get());
EXPECT_EQ(user_action_tester_.GetActionCount("SnapGroups_RecallSnapGroup"),
1);
ToggleOverview();
ASSERT_TRUE(
wm::IsActiveWindow(GetOverviewSession()->GetOverviewFocusWindow()));
OverviewGroupItem* overview_group_item =
static_cast<OverviewGroupItem*>(GetOverviewItemForWindow(w1.get()));
ASSERT_TRUE(overview_group_item);
GetOverviewSession()->SelectWindow(overview_group_item);
ASSERT_TRUE(wm::IsActiveWindow(w1.get()));
EXPECT_EQ(user_action_tester_.GetActionCount("SnapGroups_RecallSnapGroup"),
2);
wm::ActivateWindow(w3.get());
CycleWindow(WindowCyclingDirection::kForward, 1);
CompleteWindowCycling();
ASSERT_TRUE(wm::IsActiveWindow(w1.get()));
EXPECT_EQ(user_action_tester_.GetActionCount("SnapGroups_RecallSnapGroup"),
3);
CycleWindow(WindowCyclingDirection::kForward, 1);
CompleteWindowCycling();
ASSERT_TRUE(wm::IsActiveWindow(w2.get()));
EXPECT_EQ(user_action_tester_.GetActionCount("SnapGroups_RecallSnapGroup"),
3);
}
TEST_F(SnapGroupMetricsTest, SkipFormSnapGroupAfterSnapping) {
UpdateDisplay("800x600");
std::unique_ptr<aura::Window> w2(CreateAppWindow());
std::unique_ptr<aura::Window> w1(CreateAppWindow());
EXPECT_EQ(user_action_tester_.GetActionCount(
"SnapGroups_SkipFormSnapGroupAfterSnapping"),
0);
PressAndReleaseKey(ui::VKEY_OEM_4, ui::EF_ALT_DOWN);
VerifyNotSplitViewOrOverviewSession(w1.get());
EXPECT_EQ(WindowStateType::kPrimarySnapped,
WindowState::Get(w1.get())->GetStateType());
EXPECT_EQ(user_action_tester_.GetActionCount(
"SnapGroups_SkipFormSnapGroupAfterSnapping"),
1);
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kSnapByWindowStateRestore);
VerifyNotSplitViewOrOverviewSession(w1.get());
EXPECT_EQ(user_action_tester_.GetActionCount(
"SnapGroups_SkipFormSnapGroupAfterSnapping"),
1);
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
VerifySplitViewOverviewSession(w1.get());
PressAndReleaseKey(ui::VKEY_ESCAPE, ui::EF_NONE);
VerifyNotSplitViewOrOverviewSession(w1.get());
EXPECT_EQ(user_action_tester_.GetActionCount(
"SnapGroups_SkipFormSnapGroupAfterSnapping"),
1);
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio);
VerifySplitViewOverviewSession(w1.get());
ClickOverviewItem(GetEventGenerator(), w2.get());
EXPECT_EQ(user_action_tester_.GetActionCount(
"SnapGroups_SkipFormSnapGroupAfterSnapping"),
1);
auto* snap_group_controller = SnapGroupController::Get();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_EQ(user_action_tester_.GetActionCount("SnapGroups_AddSnapGroup"), 1);
auto* event_generator = GetEventGenerator();
event_generator->set_current_screen_location(GetDragPoint(w1.get()));
event_generator->DragMouseTo(GetWorkAreaBounds().CenterPoint());
ASSERT_FALSE(snap_group_controller->GetSnapGroupForGivenWindow(w1.get()));
SnapOneTestWindow(w1.get(), WindowStateType::kPrimarySnapped,
chromeos::kDefaultSnapRatio,
WindowSnapActionSource::kDragWindowToEdgeToSnap);
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_EQ(user_action_tester_.GetActionCount("SnapGroups_AddSnapGroup"), 2);
}
TEST_F(SnapGroupMetricsTest, DoubleTapDividerUserAction) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
auto* event_generator = GetEventGenerator();
SnapTwoTestWindows(w1.get(), w2.get(), true, event_generator);
SnapGroupController* snap_group_controller = SnapGroupController::Get();
ASSERT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
SplitViewDivider* divider = GetTopmostSnapGroupDivider();
auto* divider_widget = divider->divider_widget();
ASSERT_TRUE(divider_widget);
auto* divider_view = divider->divider_view_for_testing();
ASSERT_TRUE(divider_view);
auto* handler_view = divider_view->handler_view_for_testing();
ASSERT_TRUE(handler_view);
const auto divider_center_point =
GetTopmostSnapGroupDividerBoundsInScreen().CenterPoint();
event_generator->set_current_screen_location(divider_center_point);
event_generator->DoubleClickLeftButton();
EXPECT_TRUE(snap_group_controller->AreWindowsInSnapGroup(w1.get(), w2.get()));
EXPECT_EQ(user_action_tester_.GetActionCount(
"SnapGroups_DoubleTapWindowSwapAttempts"),
1);
EXPECT_EQ(user_action_tester_.GetActionCount(
"SnapGroups_DoubleTapWindowSwapSuccess"),
1);
event_generator->GestureTapAt(divider_center_point);
event_generator->GestureTapAt(divider_center_point);
EXPECT_EQ(user_action_tester_.GetActionCount(
"SnapGroups_DoubleTapWindowSwapAttempts"),
2);
EXPECT_EQ(user_action_tester_.GetActionCount(
"SnapGroups_DoubleTapWindowSwapSuccess"),
2);
}
TEST_F(SnapGroupMetricsTest, GroupContainerCycleViewAccessibleProperties) {
std::unique_ptr<aura::Window> w1(CreateAppWindow());
std::unique_ptr<aura::Window> w2(CreateAppWindow());
SnapTwoTestWindows(w1.get(), w2.get(), true,
GetEventGenerator());
auto* snap_group_controller = SnapGroupController::Get();
auto* snap_group =
snap_group_controller->GetSnapGroupForGivenWindow(w1.get());
ASSERT_TRUE(snap_group);
std::unique_ptr<GroupContainerCycleView> cycle_view =
std::make_unique<GroupContainerCycleView>(snap_group);
ui::AXNodeData data;
cycle_view->GetViewAccessibility().GetAccessibleNodeData(&data);
EXPECT_EQ(ax::mojom::Role::kGroup, data.role);
EXPECT_EQ(
l10n_util::GetStringUTF16(IDS_ASH_SNAP_GROUP_WINDOW_CYCLE_DESCRIPTION),
data.GetString16Attribute(ax::mojom::StringAttribute::kDescription));
EXPECT_TRUE(
data.GetString16Attribute(ax::mojom::StringAttribute::kName).empty());
EXPECT_FALSE(data.HasState(ax::mojom::State::kIgnored));
cycle_view->SetSelectedWindowForFocus(w1.get());
data = ui::AXNodeData();
cycle_view->GetViewAccessibility().GetAccessibleNodeData(&data);
EXPECT_EQ(u"Window -1",
data.GetString16Attribute(ax::mojom::StringAttribute::kName));
EXPECT_FALSE(data.HasState(ax::mojom::State::kIgnored));
cycle_view->mini_views()[0]->source_window()->SetTitle(u"Title 1");
cycle_view->mini_views()[1]->source_window()->SetTitle(u"Title 2");
data = ui::AXNodeData();
cycle_view->GetViewAccessibility().GetAccessibleNodeData(&data);
EXPECT_EQ(u"Title 1",
data.GetString16Attribute(ax::mojom::StringAttribute::kName));
EXPECT_FALSE(data.HasState(ax::mojom::State::kIgnored));
cycle_view->SetSelectedWindowForFocus(w2.get());
data = ui::AXNodeData();
cycle_view->GetViewAccessibility().GetAccessibleNodeData(&data);
EXPECT_EQ(u"Title 1",
data.GetString16Attribute(ax::mojom::StringAttribute::kName));
EXPECT_FALSE(data.HasState(ax::mojom::State::kIgnored));
cycle_view->ClearFocusSelection();
data = ui::AXNodeData();
cycle_view->GetViewAccessibility().GetAccessibleNodeData(&data);
EXPECT_TRUE(
data.GetString16Attribute(ax::mojom::StringAttribute::kName).empty());
EXPECT_FALSE(data.HasState(ax::mojom::State::kIgnored));
cycle_view->SetSelectedWindowForFocus(w2.get());
data = ui::AXNodeData();
cycle_view->GetViewAccessibility().GetAccessibleNodeData(&data);
EXPECT_EQ(u"Title 2",
data.GetString16Attribute(ax::mojom::StringAttribute::kName));
EXPECT_FALSE(data.HasState(ax::mojom::State::kIgnored));
EXPECT_EQ(cycle_view->TryRemovingChildItem(w2.get()), 1);
data = ui::AXNodeData();
cycle_view->GetViewAccessibility().GetAccessibleNodeData(&data);
EXPECT_TRUE(
data.GetString16Attribute(ax::mojom::StringAttribute::kName).empty());
EXPECT_FALSE(data.HasState(ax::mojom::State::kIgnored));
cycle_view->SetSelectedWindowForFocus(w1.get());
data = ui::AXNodeData();
cycle_view->GetViewAccessibility().GetAccessibleNodeData(&data);
EXPECT_EQ(u"Title 1",
data.GetString16Attribute(ax::mojom::StringAttribute::kName));
EXPECT_FALSE(data.HasState(ax::mojom::State::kIgnored));
cycle_view->mini_views()[0]->source_window()->SetTitle(std::u16string());
data = ui::AXNodeData();
cycle_view->GetViewAccessibility().GetAccessibleNodeData(&data);
EXPECT_EQ(l10n_util::GetStringUTF8(IDS_WM_WINDOW_CYCLER_UNTITLED_WINDOW),
data.GetStringAttribute(ax::mojom::StringAttribute::kName));
EXPECT_FALSE(data.HasState(ax::mojom::State::kIgnored));
cycle_view->mini_views()[0]->OnWindowDestroying(w1.get());
data = ui::AXNodeData();
cycle_view->GetViewAccessibility().GetAccessibleNodeData(&data);
EXPECT_EQ(l10n_util::GetStringUTF8(IDS_WM_WINDOW_CYCLER_UNTITLED_WINDOW),
data.GetStringAttribute(ax::mojom::StringAttribute::kName));
EXPECT_TRUE(data.HasState(ax::mojom::State::kIgnored));
}
}