blob: b57c0c2bcdc440f63c74156be2917052f8670ff5 [file] [log] [blame]
// Copyright (c) 2012 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/wm/panels/panel_layout_manager.h"
#include "ash/public/cpp/config.h"
#include "ash/public/cpp/shelf_model.h"
#include "ash/public/cpp/shelf_types.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/public/cpp/window_properties.h"
#include "ash/root_window_controller.h"
#include "ash/shelf/shelf.h"
#include "ash/shelf/shelf_button.h"
#include "ash/shelf/shelf_layout_manager.h"
#include "ash/shelf/shelf_view.h"
#include "ash/shelf/shelf_view_test_api.h"
#include "ash/shelf/shelf_widget.h"
#include "ash/shell.h"
#include "ash/system/web_notification/web_notification_tray.h"
#include "ash/test/ash_test_base.h"
#include "ash/wm/mru_window_tracker.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_util.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/i18n/rtl.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/test/test_windows.h"
#include "ui/aura/window.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/manager/managed_display_info.h"
#include "ui/display/screen.h"
#include "ui/display/test/display_manager_test_api.h"
#include "ui/events/event_utils.h"
#include "ui/events/test/event_generator.h"
#include "ui/views/widget/widget.h"
namespace ash {
namespace {
std::string ToDisplayName(int64_t id) {
return "x-" + base::Int64ToString(id);
}
display::ManagedDisplayInfo CreateDisplayInfo(int64_t id,
const gfx::Rect& bounds) {
display::ManagedDisplayInfo info(id, ToDisplayName(id), false);
info.SetBounds(bounds);
return info;
}
} // namespace
using aura::test::WindowIsAbove;
class PanelLayoutManagerTest : public AshTestBase {
public:
PanelLayoutManagerTest() = default;
~PanelLayoutManagerTest() override = default;
void SetUp() override {
AshTestBase::SetUp();
shelf_view_test_.reset(
new ShelfViewTestAPI(GetPrimaryShelf()->GetShelfViewForTesting()));
shelf_view_test_->SetAnimationDuration(1);
WebNotificationTray::DisableAnimationsForTest(true);
}
void TearDown() override {
AshTestBase::TearDown();
WebNotificationTray::DisableAnimationsForTest(false); // Reenable animation
}
aura::Window* CreateNormalWindow(const gfx::Rect& bounds) {
return CreateTestWindowInShellWithBounds(bounds);
}
aura::Window* CreatePanelWindowWithDelegate(aura::WindowDelegate* delegate,
const gfx::Rect& bounds) {
aura::Window* window = CreateTestWindowInShellWithDelegateAndType(
delegate, aura::client::WINDOW_TYPE_PANEL, 0, bounds);
static int id = 0;
std::string shelf_id(ShelfID(base::IntToString(id++)).Serialize());
window->SetProperty(kShelfIDKey, new std::string(shelf_id));
window->SetProperty<int>(kShelfItemTypeKey, TYPE_APP_PANEL);
shelf_view_test()->RunMessageLoopUntilAnimationsDone();
return window;
}
aura::Window* CreatePanelWindow(const gfx::Rect& bounds) {
return CreatePanelWindowWithDelegate(nullptr, bounds);
}
aura::Window* GetPanelContainer(aura::Window* panel) {
return Shell::GetContainer(panel->GetRootWindow(),
kShellWindowId_PanelContainer);
}
views::Widget* GetCalloutWidgetForPanel(aura::Window* panel) {
PanelLayoutManager* manager = PanelLayoutManager::Get(panel);
DCHECK(manager);
PanelLayoutManager::PanelList::iterator found = std::find(
manager->panel_windows_.begin(), manager->panel_windows_.end(), panel);
DCHECK(found != manager->panel_windows_.end());
DCHECK(found->callout_widget);
return found->CalloutWidget();
}
void PanelInScreen(aura::Window* panel) {
gfx::Rect panel_bounds = panel->GetBoundsInRootWindow();
gfx::Point root_point = panel_bounds.origin();
display::Display display =
display::Screen::GetScreen()->GetDisplayNearestPoint(root_point);
gfx::Rect panel_bounds_in_screen = panel->GetBoundsInScreen();
gfx::Point screen_bottom_right = gfx::Point(
panel_bounds_in_screen.right(), panel_bounds_in_screen.bottom());
gfx::Rect display_bounds = display.bounds();
EXPECT_TRUE(screen_bottom_right.x() < display_bounds.width() &&
screen_bottom_right.y() < display_bounds.height());
}
void PanelsNotOverlapping(aura::Window* panel1, aura::Window* panel2) {
// Waits until all shelf view animations are done.
shelf_view_test()->RunMessageLoopUntilAnimationsDone();
gfx::Rect window1_bounds = panel1->GetBoundsInRootWindow();
gfx::Rect window2_bounds = panel2->GetBoundsInRootWindow();
EXPECT_FALSE(window1_bounds.Intersects(window2_bounds));
}
void IsPanelAboveLauncherIcon(aura::Window* panel) {
// Waits until all shelf view animations are done.
shelf_view_test()->RunMessageLoopUntilAnimationsDone();
Shelf* shelf = GetShelfForWindow(panel);
gfx::Rect icon_bounds = shelf->GetScreenBoundsOfItemIconForWindow(panel);
ASSERT_FALSE(icon_bounds.width() == 0 && icon_bounds.height() == 0);
gfx::Rect window_bounds = panel->GetBoundsInScreen();
ASSERT_LT(icon_bounds.width(), window_bounds.width());
ASSERT_LT(icon_bounds.height(), window_bounds.height());
gfx::Rect shelf_bounds = shelf->shelf_widget()->GetWindowBoundsInScreen();
const ShelfAlignment alignment = shelf->alignment();
if (IsHorizontal(alignment)) {
// The horizontal bounds of the panel window should contain the bounds of
// the shelf icon.
EXPECT_LE(window_bounds.x(), icon_bounds.x());
EXPECT_GE(window_bounds.right(), icon_bounds.right());
} else {
// The vertical bounds of the panel window should contain the bounds of
// the shelf icon.
EXPECT_LE(window_bounds.y(), icon_bounds.y());
EXPECT_GE(window_bounds.bottom(), icon_bounds.bottom());
}
if (alignment == SHELF_ALIGNMENT_LEFT)
EXPECT_EQ(shelf_bounds.right(), window_bounds.x());
else if (alignment == SHELF_ALIGNMENT_RIGHT)
EXPECT_EQ(shelf_bounds.x(), window_bounds.right());
else
EXPECT_EQ(shelf_bounds.y(), window_bounds.bottom());
}
void IsCalloutAboveLauncherIcon(aura::Window* panel) {
// Flush the message loop, since callout updates use a delayed task.
base::RunLoop().RunUntilIdle();
views::Widget* widget = GetCalloutWidgetForPanel(panel);
Shelf* shelf = GetShelfForWindow(panel);
gfx::Rect icon_bounds = shelf->GetScreenBoundsOfItemIconForWindow(panel);
ASSERT_FALSE(icon_bounds.IsEmpty());
gfx::Rect panel_bounds = panel->GetBoundsInScreen();
gfx::Rect callout_bounds = widget->GetWindowBoundsInScreen();
ASSERT_FALSE(icon_bounds.IsEmpty());
EXPECT_TRUE(widget->IsVisible());
const ShelfAlignment alignment = shelf->alignment();
if (alignment == SHELF_ALIGNMENT_LEFT)
EXPECT_EQ(panel_bounds.x(), callout_bounds.right());
else if (alignment == SHELF_ALIGNMENT_RIGHT)
EXPECT_EQ(panel_bounds.right(), callout_bounds.x());
else
EXPECT_EQ(panel_bounds.bottom(), callout_bounds.y());
if (IsHorizontal(alignment)) {
EXPECT_NEAR(icon_bounds.CenterPoint().x(),
widget->GetWindowBoundsInScreen().CenterPoint().x(), 1);
} else {
EXPECT_NEAR(icon_bounds.CenterPoint().y(),
widget->GetWindowBoundsInScreen().CenterPoint().y(), 1);
}
}
bool IsPanelCalloutVisible(aura::Window* panel) {
views::Widget* widget = GetCalloutWidgetForPanel(panel);
return widget->IsVisible();
}
ShelfViewTestAPI* shelf_view_test() { return shelf_view_test_.get(); }
// Clicks the shelf item on |shelf_view| associated with the given |window|.
void ClickShelfItemForWindow(ShelfView* shelf_view, aura::Window* window) {
ShelfViewTestAPI test_api(shelf_view);
test_api.SetAnimationDuration(1);
test_api.RunMessageLoopUntilAnimationsDone();
ShelfID shelf_id = ShelfID::Deserialize(window->GetProperty(kShelfIDKey));
DCHECK(!shelf_id.IsNull());
int index = Shell::Get()->shelf_model()->ItemIndexByID(shelf_id);
DCHECK_GE(index, 0);
gfx::Rect bounds = test_api.GetButton(index)->GetBoundsInScreen();
ui::test::EventGenerator& event_generator = GetEventGenerator();
event_generator.MoveMouseTo(bounds.CenterPoint());
event_generator.ClickLeftButton();
test_api.RunMessageLoopUntilAnimationsDone();
}
Shelf* GetShelfForWindow(aura::Window* window) {
return RootWindowController::ForWindow(window)->shelf();
}
void SetAlignment(aura::Window* window, ShelfAlignment alignment) {
GetShelfForWindow(window)->SetAlignment(alignment);
}
void SetShelfAutoHideBehavior(aura::Window* window,
ShelfAutoHideBehavior behavior) {
Shelf* shelf = GetShelfForWindow(window);
shelf->SetAutoHideBehavior(behavior);
ShelfViewTestAPI test_api(shelf->GetShelfViewForTesting());
test_api.RunMessageLoopUntilAnimationsDone();
}
void SetShelfVisibilityState(aura::Window* window,
ShelfVisibilityState visibility_state) {
Shelf* shelf = GetShelfForWindow(window);
shelf->shelf_layout_manager()->SetState(visibility_state);
}
private:
std::unique_ptr<ShelfViewTestAPI> shelf_view_test_;
bool IsHorizontal(ShelfAlignment alignment) {
return alignment == SHELF_ALIGNMENT_BOTTOM;
}
DISALLOW_COPY_AND_ASSIGN(PanelLayoutManagerTest);
};
class PanelLayoutManagerTextDirectionTest
: public PanelLayoutManagerTest,
public testing::WithParamInterface<bool> {
public:
PanelLayoutManagerTextDirectionTest() : is_rtl_(GetParam()) {}
virtual ~PanelLayoutManagerTextDirectionTest() = default;
void SetUp() override {
original_locale_ = base::i18n::GetConfiguredLocale();
if (is_rtl_)
base::i18n::SetICUDefaultLocale("he");
PanelLayoutManagerTest::SetUp();
ASSERT_EQ(is_rtl_, base::i18n::IsRTL());
}
void TearDown() override {
if (is_rtl_)
base::i18n::SetICUDefaultLocale(original_locale_);
PanelLayoutManagerTest::TearDown();
}
private:
bool is_rtl_;
std::string original_locale_;
DISALLOW_COPY_AND_ASSIGN(PanelLayoutManagerTextDirectionTest);
};
// Tests that a created panel window is above the shelf icon in LTR and RTL.
TEST_P(PanelLayoutManagerTextDirectionTest, AddOnePanel) {
gfx::Rect bounds(0, 0, 201, 201);
std::unique_ptr<aura::Window> window(CreatePanelWindow(bounds));
EXPECT_EQ(GetPanelContainer(window.get()), window->parent());
EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(window.get()));
EXPECT_NO_FATAL_FAILURE(IsCalloutAboveLauncherIcon(window.get()));
}
// Tests for crashes during undocking.
// See https://crbug.com/632755
TEST_F(PanelLayoutManagerTest, UndockTest) {
std::vector<display::ManagedDisplayInfo> info_list;
const int64_t internal_display_id =
display::test::DisplayManagerTestApi(Shell::Get()->display_manager())
.SetFirstDisplayAsInternalDisplay();
// Create the primary display info.
display::ManagedDisplayInfo internal_display =
CreateDisplayInfo(internal_display_id, gfx::Rect(0, 0, 1280, 720));
// Create the secondary external display info. This will be docked display.
display::ManagedDisplayInfo external_display_info =
CreateDisplayInfo(2, gfx::Rect(0, 0, 1920, 1080));
info_list.push_back(external_display_info);
// Docked state.
display_manager()->OnNativeDisplaysChanged(info_list);
// Create a panel in the docked state
std::unique_ptr<aura::Window> p1_d2(
CreatePanelWindow(gfx::Rect(1555, 800, 50, 50)));
info_list.clear();
info_list.push_back(internal_display);
// Undock and bring back the native device display as primary display.
display_manager()->OnNativeDisplaysChanged(info_list);
}
// Tests for any crash during docking and then undocking.
// See https://crbug.com/632755
TEST_F(PanelLayoutManagerTest, DockUndockTest) {
std::vector<display::ManagedDisplayInfo> info_list;
const int64_t internal_display_id =
display::test::DisplayManagerTestApi(Shell::Get()->display_manager())
.SetFirstDisplayAsInternalDisplay();
// Create the primary display info.
display::ManagedDisplayInfo internal_display =
CreateDisplayInfo(internal_display_id, gfx::Rect(0, 0, 1280, 720));
info_list.push_back(internal_display);
display_manager()->OnNativeDisplaysChanged(info_list);
// Create a panel in the undocked state.
std::unique_ptr<aura::Window> p1_d2(
CreatePanelWindow(gfx::Rect(600, 200, 50, 50)));
// Create the secondary external display info. This will be docked display.
display::ManagedDisplayInfo external_display_info =
CreateDisplayInfo(2, gfx::Rect(0, 0, 1920, 1080));
info_list.push_back(external_display_info);
// Adding external Display
display_manager()->OnNativeDisplaysChanged(info_list);
info_list.clear();
info_list.push_back(external_display_info);
// Docked state.
display_manager()->OnNativeDisplaysChanged(info_list);
info_list.clear();
info_list.push_back(internal_display);
// Undock and bring back the native device display as primary display.
display_manager()->OnNativeDisplaysChanged(info_list);
}
// Tests that a created panel window is successfully aligned over a hidden
// shelf icon.
TEST_F(PanelLayoutManagerTest, PanelAlignsToHiddenLauncherIcon) {
gfx::Rect bounds(0, 0, 201, 201);
SetShelfAutoHideBehavior(Shell::GetPrimaryRootWindow(),
SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
std::unique_ptr<aura::Window> normal_window(CreateNormalWindow(bounds));
std::unique_ptr<aura::Window> window(CreatePanelWindow(bounds));
EXPECT_EQ(GetPanelContainer(window.get()), window->parent());
EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(window.get()));
}
TEST_F(PanelLayoutManagerTest, PanelAlignsToHiddenLauncherIconSecondDisplay) {
// Keep the displays wide so that shelves have enough space for shelves
// buttons.
UpdateDisplay("400x400,600x400");
aura::Window::Windows root_windows = Shell::GetAllRootWindows();
std::unique_ptr<aura::Window> normal_window(
CreateNormalWindow(gfx::Rect(450, 0, 100, 100)));
std::unique_ptr<aura::Window> panel(
CreatePanelWindow(gfx::Rect(400, 0, 50, 50)));
EXPECT_EQ(root_windows[1], panel->GetRootWindow());
EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(panel.get()));
gfx::Rect shelf_visible_position = panel->GetBoundsInScreen();
SetShelfAutoHideBehavior(root_windows[1], SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
// Expect the panel X position to remain the same after the shelf is hidden
// but the Y to move down.
EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(panel.get()));
EXPECT_EQ(shelf_visible_position.x(), panel->GetBoundsInScreen().x());
EXPECT_GT(panel->GetBoundsInScreen().y(), shelf_visible_position.y());
}
// Tests interactions between multiple panels
TEST_F(PanelLayoutManagerTest, MultiplePanelsAreAboveIcons) {
gfx::Rect odd_bounds(0, 0, 201, 201);
gfx::Rect even_bounds(0, 0, 200, 200);
std::unique_ptr<aura::Window> w1(CreatePanelWindow(odd_bounds));
EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w1.get()));
std::unique_ptr<aura::Window> w2(CreatePanelWindow(even_bounds));
EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w1.get()));
EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w2.get()));
std::unique_ptr<aura::Window> w3(CreatePanelWindow(odd_bounds));
EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w1.get()));
EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w2.get()));
EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w3.get()));
}
TEST_F(PanelLayoutManagerTest, MultiplePanelStacking) {
gfx::Rect bounds(0, 0, 201, 201);
std::unique_ptr<aura::Window> w1(CreatePanelWindow(bounds));
std::unique_ptr<aura::Window> w2(CreatePanelWindow(bounds));
std::unique_ptr<aura::Window> w3(CreatePanelWindow(bounds));
// Default stacking order.
EXPECT_TRUE(WindowIsAbove(w3.get(), w2.get()));
EXPECT_TRUE(WindowIsAbove(w2.get(), w1.get()));
// Changing the active window should update the stacking order.
wm::ActivateWindow(w1.get());
shelf_view_test()->RunMessageLoopUntilAnimationsDone();
EXPECT_TRUE(WindowIsAbove(w1.get(), w2.get()));
EXPECT_TRUE(WindowIsAbove(w2.get(), w3.get()));
wm::ActivateWindow(w2.get());
shelf_view_test()->RunMessageLoopUntilAnimationsDone();
EXPECT_TRUE(WindowIsAbove(w1.get(), w3.get()));
EXPECT_TRUE(WindowIsAbove(w2.get(), w3.get()));
EXPECT_TRUE(WindowIsAbove(w2.get(), w1.get()));
wm::ActivateWindow(w3.get());
EXPECT_TRUE(WindowIsAbove(w3.get(), w2.get()));
EXPECT_TRUE(WindowIsAbove(w2.get(), w1.get()));
}
TEST_F(PanelLayoutManagerTest, MultiplePanelStackingVertical) {
// Set shelf to be aligned on the right.
SetAlignment(Shell::GetPrimaryRootWindow(), SHELF_ALIGNMENT_RIGHT);
// Size panels in such a way that ordering them by X coordinate would cause
// stacking order to be incorrect. Test that stacking order is based on Y.
std::unique_ptr<aura::Window> w1(
CreatePanelWindow(gfx::Rect(0, 0, 210, 201)));
std::unique_ptr<aura::Window> w2(
CreatePanelWindow(gfx::Rect(0, 0, 220, 201)));
std::unique_ptr<aura::Window> w3(
CreatePanelWindow(gfx::Rect(0, 0, 200, 201)));
// Default stacking order.
EXPECT_TRUE(WindowIsAbove(w3.get(), w2.get()));
EXPECT_TRUE(WindowIsAbove(w2.get(), w1.get()));
// Changing the active window should update the stacking order.
wm::ActivateWindow(w1.get());
shelf_view_test()->RunMessageLoopUntilAnimationsDone();
EXPECT_TRUE(WindowIsAbove(w1.get(), w2.get()));
// TODO(crbug.com/698887): investigate failure in Mash.
if (Shell::GetAshConfig() != Config::MASH)
EXPECT_TRUE(WindowIsAbove(w2.get(), w3.get()));
wm::ActivateWindow(w2.get());
shelf_view_test()->RunMessageLoopUntilAnimationsDone();
// TODO(crbug.com/698887): investigate failure in Mash.
if (Shell::GetAshConfig() != Config::MASH)
EXPECT_TRUE(WindowIsAbove(w1.get(), w3.get()));
EXPECT_TRUE(WindowIsAbove(w2.get(), w3.get()));
EXPECT_TRUE(WindowIsAbove(w2.get(), w1.get()));
wm::ActivateWindow(w3.get());
EXPECT_TRUE(WindowIsAbove(w3.get(), w2.get()));
EXPECT_TRUE(WindowIsAbove(w2.get(), w1.get()));
}
TEST_F(PanelLayoutManagerTest, MultiplePanelCallout) {
gfx::Rect bounds(0, 0, 200, 200);
std::unique_ptr<aura::Window> w1(CreatePanelWindow(bounds));
std::unique_ptr<aura::Window> w2(CreatePanelWindow(bounds));
std::unique_ptr<aura::Window> w3(CreatePanelWindow(bounds));
std::unique_ptr<aura::Window> w4(CreateNormalWindow(gfx::Rect()));
shelf_view_test()->RunMessageLoopUntilAnimationsDone();
EXPECT_TRUE(IsPanelCalloutVisible(w1.get()));
EXPECT_TRUE(IsPanelCalloutVisible(w2.get()));
EXPECT_TRUE(IsPanelCalloutVisible(w3.get()));
// TODO(crbug.com/698887): investigate failure in Mash.
if (Shell::GetAshConfig() == Config::MASH)
return;
wm::ActivateWindow(w1.get());
EXPECT_NO_FATAL_FAILURE(IsCalloutAboveLauncherIcon(w1.get()));
wm::ActivateWindow(w2.get());
EXPECT_NO_FATAL_FAILURE(IsCalloutAboveLauncherIcon(w2.get()));
wm::ActivateWindow(w3.get());
EXPECT_NO_FATAL_FAILURE(IsCalloutAboveLauncherIcon(w3.get()));
wm::ActivateWindow(w4.get());
wm::ActivateWindow(w3.get());
EXPECT_NO_FATAL_FAILURE(IsCalloutAboveLauncherIcon(w3.get()));
w3.reset();
EXPECT_NO_FATAL_FAILURE(IsCalloutAboveLauncherIcon(w2.get()));
}
// Tests removing panels.
TEST_F(PanelLayoutManagerTest, RemoveLeftPanel) {
gfx::Rect bounds(0, 0, 201, 201);
std::unique_ptr<aura::Window> w1(CreatePanelWindow(bounds));
std::unique_ptr<aura::Window> w2(CreatePanelWindow(bounds));
std::unique_ptr<aura::Window> w3(CreatePanelWindow(bounds));
// At this point, windows should be stacked with 1 < 2 < 3
wm::ActivateWindow(w1.get());
shelf_view_test()->RunMessageLoopUntilAnimationsDone();
// Now, windows should be stacked 1 > 2 > 3
w1.reset();
EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w2.get()));
EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w3.get()));
EXPECT_TRUE(WindowIsAbove(w2.get(), w3.get()));
}
TEST_F(PanelLayoutManagerTest, RemoveMiddlePanel) {
gfx::Rect bounds(0, 0, 201, 201);
std::unique_ptr<aura::Window> w1(CreatePanelWindow(bounds));
std::unique_ptr<aura::Window> w2(CreatePanelWindow(bounds));
std::unique_ptr<aura::Window> w3(CreatePanelWindow(bounds));
// At this point, windows should be stacked with 1 < 2 < 3
wm::ActivateWindow(w2.get());
// Windows should be stacked 1 < 2 > 3
w2.reset();
EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w1.get()));
EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w3.get()));
EXPECT_TRUE(WindowIsAbove(w3.get(), w1.get()));
}
TEST_F(PanelLayoutManagerTest, RemoveRightPanel) {
gfx::Rect bounds(0, 0, 201, 201);
std::unique_ptr<aura::Window> w1(CreatePanelWindow(bounds));
std::unique_ptr<aura::Window> w2(CreatePanelWindow(bounds));
std::unique_ptr<aura::Window> w3(CreatePanelWindow(bounds));
// At this point, windows should be stacked with 1 < 2 < 3
wm::ActivateWindow(w3.get());
// Order shouldn't change.
w3.reset();
EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w1.get()));
EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w2.get()));
EXPECT_TRUE(WindowIsAbove(w2.get(), w1.get()));
}
TEST_F(PanelLayoutManagerTest, RemoveNonActivePanel) {
gfx::Rect bounds(0, 0, 201, 201);
std::unique_ptr<aura::Window> w1(CreatePanelWindow(bounds));
std::unique_ptr<aura::Window> w2(CreatePanelWindow(bounds));
std::unique_ptr<aura::Window> w3(CreatePanelWindow(bounds));
// At this point, windows should be stacked with 1 < 2 < 3
wm::ActivateWindow(w2.get());
// Windows should be stacked 1 < 2 > 3
w1.reset();
EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w2.get()));
EXPECT_NO_FATAL_FAILURE(IsPanelAboveLauncherIcon(w3.get()));
EXPECT_TRUE(WindowIsAbove(w2.get(), w3.get()));
}
TEST_F(PanelLayoutManagerTest, SplitView) {
gfx::Rect bounds(0, 0, 90, 201);
std::unique_ptr<aura::Window> w1(CreatePanelWindow(bounds));
std::unique_ptr<aura::Window> w2(CreatePanelWindow(bounds));
EXPECT_NO_FATAL_FAILURE(PanelsNotOverlapping(w1.get(), w2.get()));
}
TEST_F(PanelLayoutManagerTest, SplitViewOverlapWhenLarge) {
gfx::Rect bounds(0, 0, 600, 201);
std::unique_ptr<aura::Window> w1(CreatePanelWindow(bounds));
std::unique_ptr<aura::Window> w2(CreatePanelWindow(bounds));
EXPECT_NO_FATAL_FAILURE(PanelInScreen(w1.get()));
EXPECT_NO_FATAL_FAILURE(PanelInScreen(w2.get()));
}
TEST_F(PanelLayoutManagerTest, FanWindows) {
gfx::Rect bounds(0, 0, 201, 201);
std::unique_ptr<aura::Window> w1(CreatePanelWindow(bounds));
std::unique_ptr<aura::Window> w2(CreatePanelWindow(bounds));
std::unique_ptr<aura::Window> w3(CreatePanelWindow(bounds));
shelf_view_test()->RunMessageLoopUntilAnimationsDone();
int window_x1 = w1->GetBoundsInRootWindow().CenterPoint().x();
int window_x2 = w2->GetBoundsInRootWindow().CenterPoint().x();
int window_x3 = w3->GetBoundsInRootWindow().CenterPoint().x();
Shelf* shelf = GetPrimaryShelf();
int icon_x1 = shelf->GetScreenBoundsOfItemIconForWindow(w1.get()).x();
int icon_x2 = shelf->GetScreenBoundsOfItemIconForWindow(w2.get()).x();
// TODO(crbug.com/698887): investigate failure in Mash.
if (Shell::GetAshConfig() != Config::MASH)
EXPECT_EQ(window_x2 - window_x1, window_x3 - window_x2);
// New shelf items for panels are inserted before existing panel items.
EXPECT_LT(window_x2, window_x1);
EXPECT_LT(window_x3, window_x2);
int spacing = window_x2 - window_x1;
EXPECT_GT(std::abs(spacing), std::abs(icon_x2 - icon_x1));
}
TEST_F(PanelLayoutManagerTest, FanLargeWindow) {
gfx::Rect small_bounds(0, 0, 201, 201);
gfx::Rect large_bounds(0, 0, 501, 201);
std::unique_ptr<aura::Window> w1(CreatePanelWindow(small_bounds));
std::unique_ptr<aura::Window> w2(CreatePanelWindow(large_bounds));
std::unique_ptr<aura::Window> w3(CreatePanelWindow(small_bounds));
shelf_view_test()->RunMessageLoopUntilAnimationsDone();
int window_x1 = w1->GetBoundsInRootWindow().CenterPoint().x();
int window_x2 = w2->GetBoundsInRootWindow().CenterPoint().x();
int window_x3 = w3->GetBoundsInRootWindow().CenterPoint().x();
// The distances between windows may not be equidistant with a large panel,
// but the windows should be placed relative to the order they were added.
// New shelf items for panels are inserted before existing panel items.
EXPECT_LT(window_x2, window_x1);
EXPECT_LT(window_x3, window_x2);
}
TEST_F(PanelLayoutManagerTest, MinimizeRestorePanel) {
gfx::Rect bounds(0, 0, 201, 201);
std::unique_ptr<aura::Window> window(CreatePanelWindow(bounds));
// Activate the window, ensure callout is visible.
wm::ActivateWindow(window.get());
RunAllPendingInMessageLoop();
EXPECT_TRUE(IsPanelCalloutVisible(window.get()));
// Minimize the panel, callout should be hidden.
wm::GetWindowState(window.get())->Minimize();
RunAllPendingInMessageLoop();
EXPECT_FALSE(IsPanelCalloutVisible(window.get()));
// Restore the panel; panel should not be activated by default but callout
// should be visible.
wm::GetWindowState(window.get())->Unminimize();
RunAllPendingInMessageLoop();
EXPECT_TRUE(IsPanelCalloutVisible(window.get()));
// Activate the window, ensure callout is visible.
wm::ActivateWindow(window.get());
RunAllPendingInMessageLoop();
EXPECT_TRUE(IsPanelCalloutVisible(window.get()));
}
TEST_F(PanelLayoutManagerTest, PanelMoveBetweenMultipleDisplays) {
// Keep the displays wide so that shelves have enough space for launcher
// buttons.
UpdateDisplay("600x400,600x400");
aura::Window::Windows root_windows = Shell::GetAllRootWindows();
std::unique_ptr<aura::Window> p1_d1(
CreatePanelWindow(gfx::Rect(0, 0, 50, 50)));
std::unique_ptr<aura::Window> p2_d1(
CreatePanelWindow(gfx::Rect(0, 0, 50, 50)));
std::unique_ptr<aura::Window> p1_d2(
CreatePanelWindow(gfx::Rect(600, 0, 50, 50)));
std::unique_ptr<aura::Window> p2_d2(
CreatePanelWindow(gfx::Rect(600, 0, 50, 50)));
ShelfView* shelf_view_1st = GetPrimaryShelf()->GetShelfViewForTesting();
ShelfView* shelf_view_2nd =
GetShelfForWindow(root_windows[1])->GetShelfViewForTesting();
EXPECT_EQ(root_windows[0], p1_d1->GetRootWindow());
EXPECT_EQ(root_windows[0], p2_d1->GetRootWindow());
EXPECT_EQ(root_windows[1], p1_d2->GetRootWindow());
EXPECT_EQ(root_windows[1], p2_d2->GetRootWindow());
EXPECT_EQ(kShellWindowId_PanelContainer, p1_d1->parent()->id());
EXPECT_EQ(kShellWindowId_PanelContainer, p2_d1->parent()->id());
EXPECT_EQ(kShellWindowId_PanelContainer, p1_d2->parent()->id());
EXPECT_EQ(kShellWindowId_PanelContainer, p2_d2->parent()->id());
// Test a panel on 1st display.
// Clicking on the same display has no effect.
ClickShelfItemForWindow(shelf_view_1st, p1_d1.get());
EXPECT_EQ(root_windows[0], p1_d1->GetRootWindow());
EXPECT_EQ(root_windows[0], p2_d1->GetRootWindow());
EXPECT_EQ(root_windows[1], p1_d2->GetRootWindow());
EXPECT_EQ(root_windows[1], p1_d2->GetRootWindow());
EXPECT_FALSE(root_windows[1]->GetBoundsInScreen().Contains(
p1_d1->GetBoundsInScreen()));
// Test if clicking on another display moves the panel to
// that display.
ClickShelfItemForWindow(shelf_view_2nd, p1_d1.get());
EXPECT_EQ(root_windows[1], p1_d1->GetRootWindow());
EXPECT_EQ(root_windows[0], p2_d1->GetRootWindow());
EXPECT_EQ(root_windows[1], p1_d2->GetRootWindow());
EXPECT_EQ(root_windows[1], p2_d2->GetRootWindow());
EXPECT_TRUE(root_windows[1]->GetBoundsInScreen().Contains(
p1_d1->GetBoundsInScreen()));
// Test a panel on 2nd display.
// Clicking on the same display has no effect.
ClickShelfItemForWindow(shelf_view_2nd, p1_d2.get());
EXPECT_EQ(root_windows[1], p1_d1->GetRootWindow());
EXPECT_EQ(root_windows[0], p2_d1->GetRootWindow());
EXPECT_EQ(root_windows[1], p1_d2->GetRootWindow());
EXPECT_EQ(root_windows[1], p2_d2->GetRootWindow());
EXPECT_TRUE(root_windows[1]->GetBoundsInScreen().Contains(
p1_d2->GetBoundsInScreen()));
// Test if clicking on another display moves the panel to
// that display.
ClickShelfItemForWindow(shelf_view_1st, p1_d2.get());
EXPECT_EQ(root_windows[1], p1_d1->GetRootWindow());
EXPECT_EQ(root_windows[0], p2_d1->GetRootWindow());
EXPECT_EQ(root_windows[0], p1_d2->GetRootWindow());
EXPECT_EQ(root_windows[1], p2_d2->GetRootWindow());
EXPECT_TRUE(root_windows[0]->GetBoundsInScreen().Contains(
p1_d2->GetBoundsInScreen()));
// Test if clicking on a previously moved window moves the
// panel back to the original display.
ClickShelfItemForWindow(shelf_view_1st, p1_d1.get());
EXPECT_EQ(root_windows[0], p1_d1->GetRootWindow());
EXPECT_EQ(root_windows[0], p2_d1->GetRootWindow());
EXPECT_EQ(root_windows[0], p1_d2->GetRootWindow());
EXPECT_EQ(root_windows[1], p2_d2->GetRootWindow());
EXPECT_TRUE(root_windows[0]->GetBoundsInScreen().Contains(
p1_d1->GetBoundsInScreen()));
}
TEST_F(PanelLayoutManagerTest, PanelAttachPositionMultipleDisplays) {
// Keep the displays wide so that shelves have enough space for shelf buttons.
// Use differently sized displays so the shelf is in a different
// position on second display.
UpdateDisplay("600x400,600x600");
aura::Window::Windows root_windows = Shell::GetAllRootWindows();
std::unique_ptr<aura::Window> p1_d1(
CreatePanelWindow(gfx::Rect(0, 0, 50, 50)));
std::unique_ptr<aura::Window> p1_d2(
CreatePanelWindow(gfx::Rect(600, 0, 50, 50)));
EXPECT_EQ(root_windows[0], p1_d1->GetRootWindow());
EXPECT_EQ(root_windows[1], p1_d2->GetRootWindow());
IsPanelAboveLauncherIcon(p1_d1.get());
IsCalloutAboveLauncherIcon(p1_d1.get());
IsPanelAboveLauncherIcon(p1_d2.get());
IsCalloutAboveLauncherIcon(p1_d2.get());
}
TEST_F(PanelLayoutManagerTest, PanelAlignmentSecondDisplay) {
UpdateDisplay("600x400,600x400");
aura::Window::Windows root_windows = Shell::GetAllRootWindows();
std::unique_ptr<aura::Window> p1_d2(
CreatePanelWindow(gfx::Rect(600, 0, 50, 50)));
EXPECT_EQ(root_windows[1], p1_d2->GetRootWindow());
IsPanelAboveLauncherIcon(p1_d2.get());
IsCalloutAboveLauncherIcon(p1_d2.get());
SetAlignment(root_windows[1], SHELF_ALIGNMENT_RIGHT);
IsPanelAboveLauncherIcon(p1_d2.get());
IsCalloutAboveLauncherIcon(p1_d2.get());
SetAlignment(root_windows[1], SHELF_ALIGNMENT_LEFT);
IsPanelAboveLauncherIcon(p1_d2.get());
IsCalloutAboveLauncherIcon(p1_d2.get());
}
TEST_F(PanelLayoutManagerTest, AlignmentLeft) {
gfx::Rect bounds(0, 0, 201, 201);
std::unique_ptr<aura::Window> w(CreatePanelWindow(bounds));
SetAlignment(Shell::GetPrimaryRootWindow(), SHELF_ALIGNMENT_LEFT);
IsPanelAboveLauncherIcon(w.get());
IsCalloutAboveLauncherIcon(w.get());
}
TEST_F(PanelLayoutManagerTest, AlignmentRight) {
gfx::Rect bounds(0, 0, 201, 201);
std::unique_ptr<aura::Window> w(CreatePanelWindow(bounds));
SetAlignment(Shell::GetPrimaryRootWindow(), SHELF_ALIGNMENT_RIGHT);
IsPanelAboveLauncherIcon(w.get());
IsCalloutAboveLauncherIcon(w.get());
}
// Tests that panels will hide and restore their state with the shelf visibility
// state. This ensures that entering full-screen mode will hide your panels
// until you leave it.
TEST_F(PanelLayoutManagerTest, PanelsHideAndRestoreWithShelf) {
gfx::Rect bounds(0, 0, 201, 201);
std::unique_ptr<aura::Window> w1(CreatePanelWindow(bounds));
std::unique_ptr<aura::Window> w2(CreatePanelWindow(bounds));
std::unique_ptr<aura::Window> w3;
// Minimize w2.
wm::GetWindowState(w2.get())->Minimize();
RunAllPendingInMessageLoop();
EXPECT_TRUE(w1->IsVisible());
EXPECT_FALSE(w2->IsVisible());
SetShelfVisibilityState(Shell::GetPrimaryRootWindow(), SHELF_HIDDEN);
RunAllPendingInMessageLoop();
// w3 is created while in full-screen mode, should only become visible when
// we exit fullscreen mode.
w3.reset(CreatePanelWindow(bounds));
EXPECT_FALSE(w1->IsVisible());
EXPECT_FALSE(w2->IsVisible());
EXPECT_FALSE(w3->IsVisible());
// While in full-screen mode, the panel windows should still be in the
// switchable window list - http://crbug.com/313919.
aura::Window::Windows switchable_window_list =
Shell::Get()->mru_window_tracker()->BuildMruWindowList();
EXPECT_EQ(3u, switchable_window_list.size());
EXPECT_NE(switchable_window_list.end(),
std::find(switchable_window_list.begin(),
switchable_window_list.end(), w1.get()));
EXPECT_NE(switchable_window_list.end(),
std::find(switchable_window_list.begin(),
switchable_window_list.end(), w2.get()));
EXPECT_NE(switchable_window_list.end(),
std::find(switchable_window_list.begin(),
switchable_window_list.end(), w3.get()));
SetShelfVisibilityState(Shell::GetPrimaryRootWindow(), SHELF_VISIBLE);
RunAllPendingInMessageLoop();
// Windows should be restored to their prior state.
EXPECT_TRUE(w1->IsVisible());
EXPECT_FALSE(w2->IsVisible());
EXPECT_TRUE(w3->IsVisible());
}
// Verifies that touches along the attached edge of a panel do not
// target the panel itself.
TEST_F(PanelLayoutManagerTest, TouchHitTestPanel) {
aura::test::TestWindowDelegate delegate;
std::unique_ptr<aura::Window> w(
CreatePanelWindowWithDelegate(&delegate, gfx::Rect(0, 0, 200, 200)));
aura::Window* root = w->GetRootWindow();
ui::EventTargeter* targeter =
root->GetHost()->dispatcher()->GetDefaultEventTargeter();
// Note that the constants used in the touch locations below are
// arbitrarily-selected small numbers which will ensure the point is
// within the default extended region surrounding the panel. This value
// is calculated as
// kResizeOutsideBoundsSize * kResizeOutsideBoundsScaleForTouch
// in src/ash/root_window_controller.cc.
// Hit test outside the right edge with a bottom-aligned shelf.
SetAlignment(Shell::GetPrimaryRootWindow(), SHELF_ALIGNMENT_BOTTOM);
gfx::Rect bounds(w->bounds());
ui::TouchEvent touch(
ui::ET_TOUCH_PRESSED, gfx::Point(bounds.right() + 3, bounds.y() + 2),
ui::EventTimeForNow(),
ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 0));
ui::EventTarget* target = targeter->FindTargetForEvent(root, &touch);
EXPECT_EQ(w.get(), target);
// Hit test outside the bottom edge with a bottom-aligned shelf.
touch.set_location(gfx::Point(bounds.x() + 6, bounds.bottom() + 5));
target = targeter->FindTargetForEvent(root, &touch);
EXPECT_NE(w.get(), target);
// Hit test outside the bottom edge with a right-aligned shelf.
SetAlignment(Shell::GetPrimaryRootWindow(), SHELF_ALIGNMENT_RIGHT);
bounds = w->bounds();
touch.set_location(gfx::Point(bounds.x() + 6, bounds.bottom() + 5));
target = targeter->FindTargetForEvent(root, &touch);
EXPECT_EQ(w.get(), target);
// Hit test outside the right edge with a right-aligned shelf.
touch.set_location(gfx::Point(bounds.right() + 3, bounds.y() + 2));
target = targeter->FindTargetForEvent(root, &touch);
EXPECT_NE(w.get(), target);
// Hit test outside the top edge with a left-aligned shelf.
SetAlignment(Shell::GetPrimaryRootWindow(), SHELF_ALIGNMENT_LEFT);
bounds = w->bounds();
touch.set_location(gfx::Point(bounds.x() + 4, bounds.y() - 6));
target = targeter->FindTargetForEvent(root, &touch);
EXPECT_EQ(w.get(), target);
// Hit test outside the left edge with a left-aligned shelf.
touch.set_location(gfx::Point(bounds.x() - 1, bounds.y() + 5));
target = targeter->FindTargetForEvent(root, &touch);
EXPECT_NE(w.get(), target);
}
INSTANTIATE_TEST_CASE_P(LtrRtl,
PanelLayoutManagerTextDirectionTest,
testing::Bool());
} // namespace ash