blob: ed54ccb97dd019a89b4b61cf33e05ccf35a12faf [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/display/display_alignment_controller.h"
#include "ash/display/display_alignment_indicator.h"
#include "ash/public/cpp/ash_features.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "base/test/scoped_feature_list.h"
#include "base/timer/mock_timer.h"
#include "ui/display/display_layout_builder.h"
#include "ui/display/manager/display_layout_store.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/test/display_manager_test_api.h"
#include "ui/views/widget/widget.h"
namespace ash {
namespace {
enum class EdgeType { kTop, kRight, kBottom, kLeft };
DisplayAlignmentController* display_alignment_controller() {
return Shell::Get()->display_alignment_controller();
}
void TriggerIndicator(const display::Display& display, EdgeType edge) {
WindowTreeHostManager* window_tree_host_manager =
Shell::Get()->window_tree_host_manager();
aura::Window* primary_root =
window_tree_host_manager->GetRootWindowForDisplayId(display.id());
ui::test::EventGenerator primary_generator(primary_root);
const gfx::Rect display_bounds = primary_root->GetBoundsInRootWindow();
gfx::Point point_on_edge;
if (edge == EdgeType::kTop)
point_on_edge = display_bounds.top_center();
else if (edge == EdgeType::kRight)
point_on_edge = display_bounds.right_center();
else if (edge == EdgeType::kBottom)
point_on_edge = display_bounds.bottom_center();
else
point_on_edge = display_bounds.left_center();
primary_generator.MoveMouseToInHost(point_on_edge);
primary_generator.MoveMouseToInHost(display_bounds.CenterPoint());
primary_generator.MoveMouseToInHost(point_on_edge);
}
} // namespace
class DisplayAlignmentControllerTest : public AshTestBase {
public:
DisplayAlignmentControllerTest() = default;
~DisplayAlignmentControllerTest() override = default;
protected:
void LockScreen() { GetSessionControllerClient()->LockScreen(); }
void UnlockScreen() { GetSessionControllerClient()->UnlockScreen(); }
// AshTestBase:
void SetUp() override {
scoped_feature_list_.InitAndEnableFeature(features::kDisplayAlignAssist);
AshTestBase::SetUp();
std::unique_ptr<base::MockOneShotTimer> mock_timer =
std::make_unique<base::MockOneShotTimer>();
mock_timer_ptr_ = mock_timer.get();
display_alignment_controller()->SetTimerForTesting(std::move(mock_timer));
}
bool NoIndicatorsExist() {
return display_alignment_controller()
->GetActiveIndicatorsForTesting()
.empty();
}
void CheckIndicatorShown(size_t num_indicators,
const display::Display& src_display) {
const auto& active_indicators =
display_alignment_controller()->GetActiveIndicatorsForTesting();
WindowTreeHostManager* window_tree_host_manager =
Shell::Get()->window_tree_host_manager();
aura::Window* primary_root =
window_tree_host_manager->GetRootWindowForDisplayId(src_display.id());
EXPECT_EQ(num_indicators, active_indicators.size());
for (const auto& indicator : active_indicators) {
ASSERT_TRUE(indicator);
EXPECT_TRUE(indicator->indicator_widget_.IsVisible());
aura::Window* current_root =
indicator->indicator_widget_.GetNativeWindow()->GetRootWindow();
if (current_root == primary_root) {
ASSERT_TRUE(indicator->pill_widget_);
EXPECT_TRUE(indicator->pill_widget_->IsVisible());
} else {
EXPECT_FALSE(indicator->pill_widget_);
}
}
}
base::MockOneShotTimer* mock_timer_ptr_ = nullptr;
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_F(DisplayAlignmentControllerTest, SingleDisplayNoIndicators) {
UpdateDisplay("1920x1080");
EXPECT_TRUE(NoIndicatorsExist());
}
TEST_F(DisplayAlignmentControllerTest, TriggerIndicatorPrimary) {
UpdateDisplay("1920x1080,1366x768");
const auto& display = display_manager()->GetDisplayAt(0);
aura::Window* root_window =
Shell::Get()->window_tree_host_manager()->GetRootWindowForDisplayId(
display.id());
ui::test::EventGenerator generator(root_window);
// Move mouse on to an edge.
generator.MoveMouseToInHost(gfx::Point(0, 0));
EXPECT_TRUE(NoIndicatorsExist());
// Move mouse off the edge.
generator.MoveMouseToInHost(gfx::Point(20, 50));
EXPECT_TRUE(NoIndicatorsExist());
// Move mouse on to an edge for the second time.
generator.MoveMouseToInHost(gfx::Point(0, 0));
CheckIndicatorShown(2, display);
// Finish displaying indicators.
mock_timer_ptr_->Fire();
EXPECT_TRUE(NoIndicatorsExist());
}
TEST_F(DisplayAlignmentControllerTest, RetriggerIndicatorPrimary) {
UpdateDisplay("1920x1080,1366x768");
const auto& display = display_manager()->GetDisplayAt(0);
EXPECT_TRUE(NoIndicatorsExist());
TriggerIndicator(display, EdgeType::kTop);
CheckIndicatorShown(2, display);
// Finish displaying indicators.
mock_timer_ptr_->Fire();
EXPECT_TRUE(NoIndicatorsExist());
// Re-trigger indicators.
TriggerIndicator(display, EdgeType::kTop);
CheckIndicatorShown(2, display);
}
TEST_F(DisplayAlignmentControllerTest, OnlyTriggerNeighboringIndicators) {
UpdateDisplay("1920x1080,1366x768,800x600");
const auto& display = display_manager()->GetDisplayAt(0);
TriggerIndicator(display, EdgeType::kTop);
CheckIndicatorShown(2, display);
}
TEST_F(DisplayAlignmentControllerTest, TriggerSecondaryDisplay) {
UpdateDisplay("1920x1080,1366x768,800x600");
const auto& display = display_manager()->GetDisplayAt(1);
TriggerIndicator(display, EdgeType::kTop);
CheckIndicatorShown(4, display);
}
TEST_F(DisplayAlignmentControllerTest, TriggerTwoDisplayOnSameEdge) {
//
// +---------+---------+
// | 1 | 2 |
// | | |
// | | |
// | | |
// +-+-------+---------+-+
// | P |
// | |
// | |
// | |
// +-------------------+
//
int64_t primary_id = display::Screen::GetScreen()->GetPrimaryDisplay().id();
display::DisplayIdList list =
display::test::CreateDisplayIdListN(primary_id, 3);
display::DisplayLayoutBuilder builder(primary_id);
builder.AddDisplayPlacement(list[1], primary_id,
display::DisplayPlacement::TOP, -110);
builder.AddDisplayPlacement(list[2], primary_id,
display::DisplayPlacement::TOP, 490);
display_manager()->layout_store()->RegisterLayoutForDisplayIdList(
list, builder.Build());
UpdateDisplay("1200x500,600x500,600x500");
const auto& display = display_manager()->GetDisplayAt(1);
TriggerIndicator(display, EdgeType::kTop);
CheckIndicatorShown(4, display);
}
TEST_F(DisplayAlignmentControllerTest, DontTriggerIndicator) {
UpdateDisplay("1920x1080,1366x768");
const auto& display = display_manager()->GetDisplayAt(0);
aura::Window* root_window =
Shell::Get()->window_tree_host_manager()->GetRootWindowForDisplayId(
display.id());
ui::test::EventGenerator generator(root_window);
// Move the mouse on to the edge once.
generator.MoveMouseToInHost(gfx::Point(0, 0));
generator.MoveMouseToInHost(gfx::Point(20, 50));
EXPECT_TRUE(NoIndicatorsExist());
mock_timer_ptr_->Fire();
EXPECT_TRUE(NoIndicatorsExist());
}
TEST_F(DisplayAlignmentControllerTest, DontTriggerIndicatorDifferentDisplays) {
UpdateDisplay("1920x1080,1366x768");
const auto& primary_display = display_manager()->GetDisplayAt(0);
const auto& secondary_display = display_manager()->GetDisplayAt(1);
WindowTreeHostManager* window_tree_host_manager =
Shell::Get()->window_tree_host_manager();
aura::Window* primary_root =
window_tree_host_manager->GetRootWindowForDisplayId(primary_display.id());
aura::Window* secondary_root =
window_tree_host_manager->GetRootWindowForDisplayId(
secondary_display.id());
ui::test::EventGenerator primary_generator(primary_root);
ui::test::EventGenerator secondary_generator(secondary_root);
// Simulate hitting the edge of the first display.
primary_generator.MoveMouseToInHost(gfx::Point(0, 0));
primary_generator.MoveMouseToInHost(gfx::Point(20, 20));
// Hitting the edge of the second display should not trigger alignment
// indicators.
secondary_generator.MoveMouseToInHost(gfx::Point(1365, 0));
EXPECT_TRUE(NoIndicatorsExist());
}
TEST_F(DisplayAlignmentControllerTest, RemoveDisplay) {
UpdateDisplay("1920x1080,1366x768");
const auto& primary_display = display_manager()->GetDisplayAt(0);
TriggerIndicator(primary_display, EdgeType::kLeft);
CheckIndicatorShown(2, primary_display);
UpdateDisplay("1920x1080");
EXPECT_TRUE(NoIndicatorsExist());
TriggerIndicator(primary_display, EdgeType::kTop);
EXPECT_TRUE(NoIndicatorsExist());
}
TEST_F(DisplayAlignmentControllerTest, LockScreen) {
UpdateDisplay("1920x1080,1366x768");
const auto& primary_display = display_manager()->GetDisplayAt(0);
TriggerIndicator(primary_display, EdgeType::kBottom);
CheckIndicatorShown(2, primary_display);
LockScreen();
EXPECT_TRUE(NoIndicatorsExist());
TriggerIndicator(primary_display, EdgeType::kTop);
EXPECT_TRUE(NoIndicatorsExist());
UnlockScreen();
TriggerIndicator(primary_display, EdgeType::kTop);
CheckIndicatorShown(2, primary_display);
}
TEST_F(DisplayAlignmentControllerTest, ChangeResolution) {
UpdateDisplay("1920x1080,1366x768");
const auto& primary_display = display_manager()->GetDisplayAt(0);
TriggerIndicator(primary_display, EdgeType::kTop);
CheckIndicatorShown(2, primary_display);
UpdateDisplay("2560x1440,1366x768");
// Resolution change immediately hides the indicator.
EXPECT_TRUE(NoIndicatorsExist());
TriggerIndicator(primary_display, EdgeType::kRight);
CheckIndicatorShown(2, primary_display);
}
TEST_F(DisplayAlignmentControllerTest, AllowOffByOnes) {
UpdateDisplay("1920x1080,1366x768");
const auto& primary_display = display_manager()->GetDisplayAt(0);
WindowTreeHostManager* window_tree_host_manager =
Shell::Get()->window_tree_host_manager();
aura::Window* primary_root =
window_tree_host_manager->GetRootWindowForDisplayId(primary_display.id());
ui::test::EventGenerator primary_generator(primary_root);
// (1, 1) is one off of both top and left edge.
primary_generator.MoveMouseToInHost(gfx::Point(1, 1));
primary_generator.MoveMouseToInHost(gfx::Point(20, 20));
primary_generator.MoveMouseToInHost(gfx::Point(1, 1));
CheckIndicatorShown(2, primary_display);
}
} // namespace ash