blob: d9584b435e05432fffb3998b37bd05e4a3cebf86 [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/drag_window_resizer.h"
#include "ash/aura/wm_window_aura.h"
#include "ash/common/material_design/material_design_controller.h"
#include "ash/common/shelf/shelf_layout_manager.h"
#include "ash/common/wm/window_positioning_utils.h"
#include "ash/display/mouse_cursor_event_filter.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/root_window_controller.h"
#include "ash/shell.h"
#include "ash/test/ash_md_test_base.h"
#include "ash/test/cursor_manager_test_api.h"
#include "ash/wm/drag_window_controller.h"
#include "ash/wm/window_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/env.h"
#include "ui/aura/test/test_window_delegate.h"
#include "ui/base/hit_test.h"
#include "ui/base/ui_base_types.h"
#include "ui/compositor/layer_delegate.h"
#include "ui/compositor/layer_tree_owner.h"
#include "ui/display/manager/display_layout.h"
#include "ui/display/manager/display_layout_builder.h"
#include "ui/display/manager/display_manager.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/views/widget/widget.h"
#include "ui/wm/core/window_util.h"
namespace ash {
namespace {
const int kRootHeight = 600;
// Used to test if the OnPaintLayer is called by DragWindowLayerDelegate.
class TestLayerDelegate : public ui::LayerDelegate {
public:
TestLayerDelegate() = default;
~TestLayerDelegate() override = default;
int paint_count() const { return paint_count_; }
private:
// Paint content for the layer to the specified context.
void OnPaintLayer(const ui::PaintContext& context) override {
paint_count_++;
}
void OnDelegatedFrameDamage(const gfx::Rect& damage_rect_in_dip) override {}
void OnDeviceScaleFactorChanged(float device_scale_factor) override {}
int paint_count_ = 0;
DISALLOW_COPY_AND_ASSIGN(TestLayerDelegate);
};
} // namespace
class DragWindowResizerTest : public test::AshMDTestBase {
public:
DragWindowResizerTest() : transient_child_(nullptr) {}
~DragWindowResizerTest() override {}
void SetUp() override {
AshMDTestBase::SetUp();
UpdateDisplay(base::StringPrintf("800x%d", kRootHeight));
aura::Window* root = Shell::GetPrimaryRootWindow();
gfx::Rect root_bounds(root->bounds());
EXPECT_EQ(kRootHeight, root_bounds.height());
EXPECT_EQ(800, root_bounds.width());
window_.reset(new aura::Window(&delegate_));
window_->SetType(ui::wm::WINDOW_TYPE_NORMAL);
window_->Init(ui::LAYER_NOT_DRAWN);
ParentWindowInPrimaryRootWindow(window_.get());
window_->set_id(1);
always_on_top_window_.reset(new aura::Window(&delegate2_));
always_on_top_window_->SetType(ui::wm::WINDOW_TYPE_NORMAL);
always_on_top_window_->SetProperty(aura::client::kAlwaysOnTopKey, true);
always_on_top_window_->Init(ui::LAYER_NOT_DRAWN);
ParentWindowInPrimaryRootWindow(always_on_top_window_.get());
always_on_top_window_->set_id(2);
system_modal_window_.reset(new aura::Window(&delegate3_));
system_modal_window_->SetType(ui::wm::WINDOW_TYPE_NORMAL);
system_modal_window_->SetProperty(aura::client::kModalKey,
ui::MODAL_TYPE_SYSTEM);
system_modal_window_->Init(ui::LAYER_NOT_DRAWN);
ParentWindowInPrimaryRootWindow(system_modal_window_.get());
system_modal_window_->set_id(3);
transient_child_ = new aura::Window(&delegate4_);
transient_child_->SetType(ui::wm::WINDOW_TYPE_NORMAL);
transient_child_->Init(ui::LAYER_NOT_DRAWN);
ParentWindowInPrimaryRootWindow(transient_child_);
transient_child_->set_id(4);
transient_parent_.reset(new aura::Window(&delegate5_));
transient_parent_->SetType(ui::wm::WINDOW_TYPE_NORMAL);
transient_parent_->Init(ui::LAYER_NOT_DRAWN);
ParentWindowInPrimaryRootWindow(transient_parent_.get());
::wm::AddTransientChild(transient_parent_.get(), transient_child_);
transient_parent_->set_id(5);
panel_window_.reset(new aura::Window(&delegate6_));
panel_window_->SetType(ui::wm::WINDOW_TYPE_PANEL);
panel_window_->Init(ui::LAYER_NOT_DRAWN);
ParentWindowInPrimaryRootWindow(panel_window_.get());
}
void TearDown() override {
window_.reset();
always_on_top_window_.reset();
system_modal_window_.reset();
transient_parent_.reset();
panel_window_.reset();
AshMDTestBase::TearDown();
}
protected:
gfx::Point CalculateDragPoint(const WindowResizer& resizer,
int delta_x,
int delta_y) const {
gfx::Point location = resizer.GetInitialLocation();
location.set_x(location.x() + delta_x);
location.set_y(location.y() + delta_y);
return location;
}
ShelfLayoutManager* shelf_layout_manager() {
return Shell::GetPrimaryRootWindowController()->GetShelfLayoutManager();
}
static WindowResizer* CreateDragWindowResizer(
aura::Window* window,
const gfx::Point& point_in_parent,
int window_component) {
return CreateWindowResizer(WmWindowAura::Get(window), point_in_parent,
window_component,
aura::client::WINDOW_MOVE_SOURCE_MOUSE)
.release();
}
bool TestIfMouseWarpsAt(const gfx::Point& point_in_screen) {
return test::AshTestBase::TestIfMouseWarpsAt(GetEventGenerator(),
point_in_screen);
}
aura::test::TestWindowDelegate delegate_;
aura::test::TestWindowDelegate delegate2_;
aura::test::TestWindowDelegate delegate3_;
aura::test::TestWindowDelegate delegate4_;
aura::test::TestWindowDelegate delegate5_;
aura::test::TestWindowDelegate delegate6_;
std::unique_ptr<aura::Window> window_;
std::unique_ptr<aura::Window> always_on_top_window_;
std::unique_ptr<aura::Window> system_modal_window_;
std::unique_ptr<aura::Window> panel_window_;
aura::Window* transient_child_;
std::unique_ptr<aura::Window> transient_parent_;
private:
DISALLOW_COPY_AND_ASSIGN(DragWindowResizerTest);
};
INSTANTIATE_TEST_CASE_P(
/* prefix intentionally left blank due to only one parameterization */,
DragWindowResizerTest,
testing::Values(MaterialDesignController::NON_MATERIAL,
MaterialDesignController::MATERIAL_NORMAL,
MaterialDesignController::MATERIAL_EXPERIMENTAL));
// Verifies a window can be moved from the primary display to another.
TEST_P(DragWindowResizerTest, WindowDragWithMultiDisplays) {
if (!SupportsMultipleDisplays())
return;
const int height_offset = GetMdMaximizedWindowHeightOffset();
// The secondary display is logically on the right, but on the system (e.g. X)
// layer, it's below the primary one. See UpdateDisplay() in ash_test_base.cc.
UpdateDisplay("800x600,400x300");
aura::Window::Windows root_windows = Shell::GetAllRootWindows();
ASSERT_EQ(2U, root_windows.size());
window_->SetBoundsInScreen(gfx::Rect(0, 0, 50, 60),
display::Screen::GetScreen()->GetPrimaryDisplay());
EXPECT_EQ(root_windows[0], window_->GetRootWindow());
{
// Grab (0, 0) of the window.
std::unique_ptr<WindowResizer> resizer(
CreateDragWindowResizer(window_.get(), gfx::Point(), HTCAPTION));
ASSERT_TRUE(resizer.get());
// Drag the pointer to the right. Once it reaches the right edge of the
// primary display, it warps to the secondary.
resizer->Drag(CalculateDragPoint(*resizer, 800, 10), 0);
resizer->CompleteDrag();
// The whole window is on the secondary display now. The parent should be
// changed.
EXPECT_EQ(root_windows[1], window_->GetRootWindow());
EXPECT_EQ("0,10 50x60", window_->bounds().ToString());
}
window_->SetBoundsInScreen(gfx::Rect(0, 0, 50, 60),
display::Screen::GetScreen()->GetPrimaryDisplay());
EXPECT_EQ(root_windows[0], window_->GetRootWindow());
{
// Grab (0, 0) of the window and move the pointer to (775, 10).
std::unique_ptr<WindowResizer> resizer(
CreateDragWindowResizer(window_.get(), gfx::Point(), HTCAPTION));
ASSERT_TRUE(resizer.get());
resizer->Drag(CalculateDragPoint(*resizer, 795, 10), 0);
// Window should be adjusted for minimum visibility (25px) during the drag.
EXPECT_EQ("775,10 50x60", window_->bounds().ToString());
resizer->CompleteDrag();
// Since the pointer is still on the primary root window, the parent should
// not be changed.
// Window origin should be adjusted for minimum visibility (25px).
EXPECT_EQ(root_windows[0], window_->GetRootWindow());
EXPECT_EQ("775,10 50x60", window_->bounds().ToString());
}
window_->SetBoundsInScreen(gfx::Rect(0, 0, 50, 60),
display::Screen::GetScreen()->GetPrimaryDisplay());
EXPECT_EQ(root_windows[0], window_->GetRootWindow());
{
// Grab the top-right edge of the window and move the pointer to (0, 10)
// in the secondary root window's coordinates.
std::unique_ptr<WindowResizer> resizer(
CreateDragWindowResizer(window_.get(), gfx::Point(49, 0), HTCAPTION));
ASSERT_TRUE(resizer.get());
resizer->Drag(CalculateDragPoint(*resizer, 751, 10), ui::EF_CONTROL_DOWN);
resizer->CompleteDrag();
// Since the pointer is on the secondary, the parent should be changed
// even though only small fraction of the window is within the secondary
// root window's bounds.
EXPECT_EQ(root_windows[1], window_->GetRootWindow());
// Window origin should be adjusted for minimum visibility (25px).
int expected_x = -50 + wm::kMinimumOnScreenArea;
EXPECT_EQ(base::IntToString(expected_x) + ",10 50x60",
window_->bounds().ToString());
}
// Dropping a window that is larger than the destination work area
// will shrink to fit to the work area.
window_->SetBoundsInScreen(gfx::Rect(0, 0, 700, 500),
display::Screen::GetScreen()->GetPrimaryDisplay());
EXPECT_EQ(root_windows[0], window_->GetRootWindow());
{
// Grab the top-right edge of the window and move the pointer to (0, 10)
// in the secondary root window's coordinates.
std::unique_ptr<WindowResizer> resizer(
CreateDragWindowResizer(window_.get(), gfx::Point(699, 0), HTCAPTION));
ASSERT_TRUE(resizer.get());
resizer->Drag(CalculateDragPoint(*resizer, 101, 10), ui::EF_CONTROL_DOWN);
resizer->CompleteDrag();
EXPECT_EQ(root_windows[1], window_->GetRootWindow());
// Window size should be adjusted to fit to the work area
EXPECT_EQ(gfx::Size(400, 253 + height_offset).ToString(),
window_->bounds().size().ToString());
gfx::Rect window_bounds_in_screen = window_->GetBoundsInScreen();
gfx::Rect intersect(window_->GetRootWindow()->GetBoundsInScreen());
intersect.Intersect(window_bounds_in_screen);
EXPECT_LE(10, intersect.width());
EXPECT_LE(10, intersect.height());
EXPECT_TRUE(window_bounds_in_screen.Contains(gfx::Point(800, 10)));
}
// Dropping a window that is larger than the destination work area
// will shrink to fit to the work area.
window_->SetBoundsInScreen(gfx::Rect(0, 0, 700, 500),
display::Screen::GetScreen()->GetPrimaryDisplay());
EXPECT_EQ(root_windows[0], window_->GetRootWindow());
{
// Grab the top-left edge of the window and move the pointer to (150, 10)
// in the secondary root window's coordinates. Make sure the window is
// shrink in such a way that it keeps the cursor within.
std::unique_ptr<WindowResizer> resizer(
CreateDragWindowResizer(window_.get(), gfx::Point(0, 0), HTCAPTION));
ASSERT_TRUE(resizer.get());
resizer->Drag(CalculateDragPoint(*resizer, 799, 10), ui::EF_CONTROL_DOWN);
resizer->Drag(CalculateDragPoint(*resizer, 850, 10), ui::EF_CONTROL_DOWN);
resizer->CompleteDrag();
EXPECT_EQ(root_windows[1], window_->GetRootWindow());
// Window size should be adjusted to fit to the work area
EXPECT_EQ(gfx::Size(400, 253 + height_offset).ToString(),
window_->bounds().size().ToString());
gfx::Rect window_bounds_in_screen = window_->GetBoundsInScreen();
gfx::Rect intersect(window_->GetRootWindow()->GetBoundsInScreen());
intersect.Intersect(window_bounds_in_screen);
EXPECT_LE(10, intersect.width());
EXPECT_LE(10, intersect.height());
EXPECT_TRUE(window_bounds_in_screen.Contains(gfx::Point(850, 10)));
}
}
// Verifies that dragging the active window to another display makes the new
// root window the active root window.
TEST_P(DragWindowResizerTest, WindowDragWithMultiDisplaysActiveRoot) {
if (!SupportsMultipleDisplays())
return;
// The secondary display is logically on the right, but on the system (e.g. X)
// layer, it's below the primary one. See UpdateDisplay() in ash_test_base.cc.
UpdateDisplay("800x600,800x600");
aura::Window::Windows root_windows = Shell::GetAllRootWindows();
ASSERT_EQ(2U, root_windows.size());
aura::test::TestWindowDelegate delegate;
std::unique_ptr<aura::Window> window(new aura::Window(&delegate));
window->SetType(ui::wm::WINDOW_TYPE_NORMAL);
window->Init(ui::LAYER_TEXTURED);
ParentWindowInPrimaryRootWindow(window.get());
window->SetBoundsInScreen(gfx::Rect(0, 0, 50, 60),
display::Screen::GetScreen()->GetPrimaryDisplay());
window->Show();
EXPECT_TRUE(ash::wm::CanActivateWindow(window.get()));
ash::wm::ActivateWindow(window.get());
EXPECT_EQ(root_windows[0], window->GetRootWindow());
EXPECT_EQ(root_windows[0], ash::Shell::GetTargetRootWindow());
{
// Grab (0, 0) of the window.
std::unique_ptr<WindowResizer> resizer(
CreateDragWindowResizer(window.get(), gfx::Point(), HTCAPTION));
ASSERT_TRUE(resizer.get());
// Drag the pointer to the right. Once it reaches the right edge of the
// primary display, it warps to the secondary.
resizer->Drag(CalculateDragPoint(*resizer, 800, 10), 0);
resizer->CompleteDrag();
// The whole window is on the secondary display now. The parent should be
// changed.
EXPECT_EQ(root_windows[1], window->GetRootWindow());
EXPECT_EQ(root_windows[1], ash::Shell::GetTargetRootWindow());
}
}
// Verifies a window can be moved from the secondary display to primary.
TEST_P(DragWindowResizerTest, WindowDragWithMultiDisplaysRightToLeft) {
if (!SupportsMultipleDisplays())
return;
UpdateDisplay("800x600,800x600");
aura::Window::Windows root_windows = Shell::GetAllRootWindows();
ASSERT_EQ(2U, root_windows.size());
window_->SetBoundsInScreen(
gfx::Rect(800, 00, 50, 60),
display::Screen::GetScreen()->GetDisplayNearestWindow(root_windows[1]));
EXPECT_EQ(root_windows[1], window_->GetRootWindow());
{
// Grab (0, 0) of the window.
std::unique_ptr<WindowResizer> resizer(
CreateDragWindowResizer(window_.get(), gfx::Point(), HTCAPTION));
ASSERT_TRUE(resizer.get());
// Move the mouse near the right edge, (798, 0), of the primary display.
resizer->Drag(CalculateDragPoint(*resizer, -2, 0), ui::EF_CONTROL_DOWN);
resizer->CompleteDrag();
EXPECT_EQ(root_windows[0], window_->GetRootWindow());
// Window origin should be adjusted for minimum visibility (25px).
EXPECT_EQ("775,0 50x60", window_->bounds().ToString());
}
}
// Verifies the drag window is shown correctly.
TEST_P(DragWindowResizerTest, DragWindowController) {
if (!SupportsMultipleDisplays())
return;
UpdateDisplay("800x600,800x600");
aura::Window::Windows root_windows = Shell::GetAllRootWindows();
ASSERT_EQ(2U, root_windows.size());
TestLayerDelegate delegate;
window_->layer()->set_delegate(&delegate);
window_->SetBoundsInScreen(gfx::Rect(0, 0, 50, 60),
display::Screen::GetScreen()->GetPrimaryDisplay());
EXPECT_EQ(root_windows[0], window_->GetRootWindow());
EXPECT_FLOAT_EQ(1.0f, window_->layer()->opacity());
{
// Hold the center of the window so that the window doesn't stick to the
// edge when dragging around the edge of the display.
std::unique_ptr<WindowResizer> resizer(
CreateDragWindowResizer(window_.get(), gfx::Point(25, 30), HTCAPTION));
ASSERT_TRUE(resizer.get());
DragWindowResizer* drag_resizer = DragWindowResizer::instance_;
ASSERT_TRUE(drag_resizer);
EXPECT_FALSE(drag_resizer->drag_window_controller_.get());
// The pointer is inside the primary root. The drag window controller
// should be NULL.
resizer->Drag(CalculateDragPoint(*resizer, 10, 10), 0);
DragWindowController* controller =
drag_resizer->drag_window_controller_.get();
EXPECT_EQ(0, controller->GetDragWindowsCountForTest());
// The window spans both root windows.
resizer->Drag(CalculateDragPoint(*resizer, 773, 10), 0);
EXPECT_EQ(1, controller->GetDragWindowsCountForTest());
const aura::Window* drag_window = controller->GetDragWindowForTest(0);
ASSERT_TRUE(drag_window);
const ui::Layer* drag_layer = drag_window->layer();
ASSERT_TRUE(drag_layer);
// Check if |resizer->layer_| is properly set to the drag widget.
const std::vector<ui::Layer*>& layers = drag_layer->children();
EXPECT_FALSE(layers.empty());
EXPECT_EQ(controller->GetDragLayerOwnerForTest(0)->root(), layers.back());
// The paint request on a drag window should reach the original delegate.
controller->RequestLayerPaintForTest();
EXPECT_EQ(1, delegate.paint_count());
// Invalidating the delegate on the original layer should prevent
// calling the OnPaintLayer on the original delegate from new delegate.
window_->layer()->set_delegate(nullptr);
controller->RequestLayerPaintForTest();
EXPECT_EQ(1, delegate.paint_count());
// |window_| should be opaque since the pointer is still on the primary
// root window. The drag window should be semi-transparent.
EXPECT_FLOAT_EQ(1.0f, window_->layer()->opacity());
EXPECT_GT(1.0f, drag_layer->opacity());
// Enter the pointer to the secondary display.
resizer->Drag(CalculateDragPoint(*resizer, 775, 10), 0);
EXPECT_EQ(1, controller->GetDragWindowsCountForTest());
// |window_| should be transparent, and the drag window should be opaque.
EXPECT_GT(1.0f, window_->layer()->opacity());
EXPECT_FLOAT_EQ(1.0f, drag_layer->opacity());
resizer->CompleteDrag();
EXPECT_EQ(root_windows[1], window_->GetRootWindow());
EXPECT_FLOAT_EQ(1.0f, window_->layer()->opacity());
}
// Do the same test with RevertDrag().
window_->SetBoundsInScreen(gfx::Rect(0, 0, 50, 60),
display::Screen::GetScreen()->GetPrimaryDisplay());
EXPECT_EQ(root_windows[0], window_->GetRootWindow());
EXPECT_FLOAT_EQ(1.0f, window_->layer()->opacity());
{
std::unique_ptr<WindowResizer> resizer(
CreateDragWindowResizer(window_.get(), gfx::Point(), HTCAPTION));
ASSERT_TRUE(resizer.get());
DragWindowResizer* drag_resizer = DragWindowResizer::instance_;
DragWindowController* controller =
drag_resizer->drag_window_controller_.get();
ASSERT_TRUE(drag_resizer);
EXPECT_FALSE(controller);
resizer->Drag(CalculateDragPoint(*resizer, 0, 610), 0);
resizer->RevertDrag();
EXPECT_EQ(root_windows[0], window_->GetRootWindow());
EXPECT_FLOAT_EQ(1.0f, window_->layer()->opacity());
}
}
TEST_P(DragWindowResizerTest, DragWindowControllerAcrossThreeDisplays) {
if (!SupportsMultipleDisplays())
return;
UpdateDisplay("400x600,400x600,800x600");
aura::Window::Windows root_windows = Shell::GetAllRootWindows();
// Layout so that all three displays touch each other.
display::DisplayIdList list = display_manager()->GetCurrentDisplayIdList();
ASSERT_EQ(3u, list.size());
ASSERT_EQ(display::Screen::GetScreen()->GetPrimaryDisplay().id(), list[0]);
display::DisplayLayoutBuilder builder(list[0]);
builder.AddDisplayPlacement(list[1], list[0],
display::DisplayPlacement::RIGHT, 0);
builder.AddDisplayPlacement(list[2], list[0],
display::DisplayPlacement::BOTTOM, 0);
display_manager()->SetLayoutForCurrentDisplays(builder.Build());
// Sanity check.
ASSERT_EQ(gfx::Rect(0, 000, 400, 600),
display_manager()->GetDisplayForId(list[0]).bounds());
ASSERT_EQ(gfx::Rect(400, 0, 400, 600),
display_manager()->GetDisplayForId(list[1]).bounds());
ASSERT_EQ(gfx::Rect(0, 600, 800, 600),
display_manager()->GetDisplayForId(list[2]).bounds());
// Create a window on 2nd display.
window_->SetBoundsInScreen(gfx::Rect(400, 0, 100, 100),
display_manager()->GetDisplayForId(list[1]));
ASSERT_EQ(root_windows[1], window_->GetRootWindow());
// Hold the center of the window so that the window doesn't stick to the edge
// when dragging around the edge of the display.
std::unique_ptr<WindowResizer> resizer(
CreateDragWindowResizer(window_.get(), gfx::Point(50, 50), HTCAPTION));
ASSERT_TRUE(resizer.get());
DragWindowResizer* drag_resizer = DragWindowResizer::instance_;
ASSERT_TRUE(drag_resizer);
EXPECT_FALSE(drag_resizer->drag_window_controller_.get());
resizer->Drag(CalculateDragPoint(*resizer, -50, 0), 0);
DragWindowController* controller =
drag_resizer->drag_window_controller_.get();
ASSERT_TRUE(controller);
ASSERT_EQ(1, controller->GetDragWindowsCountForTest());
const aura::Window* drag_window0 = controller->GetDragWindowForTest(0);
ASSERT_TRUE(drag_window0);
const ui::Layer* drag_layer0 = drag_window0->layer();
EXPECT_EQ(root_windows[0], drag_window0->GetRootWindow());
// |window_| should be opaque since the pointer is still on the primary
// root window. The drag window should be semi-transparent.
EXPECT_FLOAT_EQ(1.0f, window_->layer()->opacity());
EXPECT_GT(1.0f, drag_layer0->opacity());
// The window spans across 3 displays, dragging to 3rd display.
resizer->Drag(CalculateDragPoint(*resizer, -50, 549), 0);
ASSERT_EQ(2, controller->GetDragWindowsCountForTest());
drag_window0 = controller->GetDragWindowForTest(0);
const aura::Window* drag_window1 = controller->GetDragWindowForTest(1);
drag_layer0 = drag_window0->layer();
const ui::Layer* drag_layer1 = drag_window1->layer();
EXPECT_EQ(root_windows[0], drag_window0->GetRootWindow());
EXPECT_EQ(root_windows[2], drag_window1->GetRootWindow());
// |window_| should be opaque since the pointer is still on the 2nd
// root window. The drag window should be semi-transparent.
EXPECT_FLOAT_EQ(1.0f, window_->layer()->opacity());
EXPECT_GT(1.0f, drag_layer0->opacity());
EXPECT_GT(1.0f, drag_layer1->opacity());
resizer->Drag(CalculateDragPoint(*resizer, -51, 549), 0);
ASSERT_EQ(2, controller->GetDragWindowsCountForTest());
drag_window0 = controller->GetDragWindowForTest(0);
drag_window1 = controller->GetDragWindowForTest(1);
drag_layer0 = drag_window0->layer();
drag_layer1 = drag_window1->layer();
EXPECT_EQ(root_windows[0], drag_window0->GetRootWindow());
EXPECT_EQ(root_windows[2], drag_window1->GetRootWindow());
// |window_| should be transparent since the pointer is still on the primary
// root window. The drag window should be semi-transparent.
EXPECT_GT(1.0f, window_->layer()->opacity());
EXPECT_FLOAT_EQ(1.0f, drag_layer0->opacity());
EXPECT_GT(1.0f, drag_layer1->opacity());
// Enter the pointer to the 3rd. Since it's bottom, the window snaps and
// no drag windwos are created.
resizer->Drag(CalculateDragPoint(*resizer, -51, 551), 0);
ASSERT_EQ(1, controller->GetDragWindowsCountForTest());
drag_window0 = controller->GetDragWindowForTest(0);
drag_layer0 = drag_window0->layer();
EXPECT_EQ(root_windows[2], drag_window0->GetRootWindow());
// |window_| should be transparent, and the drag window should be opaque.
EXPECT_FLOAT_EQ(0.0f, window_->layer()->opacity());
EXPECT_FLOAT_EQ(1.0f, drag_layer0->opacity());
resizer->CompleteDrag();
EXPECT_EQ(root_windows[2], window_->GetRootWindow());
EXPECT_FLOAT_EQ(1.0f, window_->layer()->opacity());
}
// Verifies if the resizer sets and resets
// MouseCursorEventFilter::mouse_warp_mode_ as expected.
TEST_P(DragWindowResizerTest, WarpMousePointer) {
MouseCursorEventFilter* event_filter =
Shell::GetInstance()->mouse_cursor_filter();
ASSERT_TRUE(event_filter);
window_->SetBounds(gfx::Rect(0, 0, 50, 60));
EXPECT_TRUE(event_filter->mouse_warp_enabled_);
{
std::unique_ptr<WindowResizer> resizer(
CreateDragWindowResizer(window_.get(), gfx::Point(), HTCAPTION));
// While dragging a window, warp should be allowed.
EXPECT_TRUE(event_filter->mouse_warp_enabled_);
resizer->CompleteDrag();
}
EXPECT_TRUE(event_filter->mouse_warp_enabled_);
{
std::unique_ptr<WindowResizer> resizer(
CreateDragWindowResizer(window_.get(), gfx::Point(), HTCAPTION));
EXPECT_TRUE(event_filter->mouse_warp_enabled_);
resizer->RevertDrag();
}
EXPECT_TRUE(event_filter->mouse_warp_enabled_);
{
std::unique_ptr<WindowResizer> resizer(
CreateDragWindowResizer(window_.get(), gfx::Point(), HTRIGHT));
// While resizing a window, warp should NOT be allowed.
EXPECT_FALSE(event_filter->mouse_warp_enabled_);
resizer->CompleteDrag();
}
EXPECT_TRUE(event_filter->mouse_warp_enabled_);
{
std::unique_ptr<WindowResizer> resizer(
CreateDragWindowResizer(window_.get(), gfx::Point(), HTRIGHT));
EXPECT_FALSE(event_filter->mouse_warp_enabled_);
resizer->RevertDrag();
}
EXPECT_TRUE(event_filter->mouse_warp_enabled_);
}
// Verifies cursor's device scale factor is updated whe a window is moved across
// root windows with different device scale factors (http://crbug.com/154183).
TEST_P(DragWindowResizerTest, CursorDeviceScaleFactor) {
if (!SupportsMultipleDisplays())
return;
// The secondary display is logically on the right, but on the system (e.g. X)
// layer, it's below the primary one. See UpdateDisplay() in ash_test_base.cc.
UpdateDisplay("400x400,800x800*2");
aura::Window::Windows root_windows = Shell::GetAllRootWindows();
ASSERT_EQ(2U, root_windows.size());
test::CursorManagerTestApi cursor_test_api(
Shell::GetInstance()->cursor_manager());
// Move window from the root window with 1.0 device scale factor to the root
// window with 2.0 device scale factor.
{
window_->SetBoundsInScreen(
gfx::Rect(0, 0, 50, 60),
display::Screen::GetScreen()->GetPrimaryDisplay());
EXPECT_EQ(root_windows[0], window_->GetRootWindow());
// Grab (0, 0) of the window.
std::unique_ptr<WindowResizer> resizer(
CreateDragWindowResizer(window_.get(), gfx::Point(), HTCAPTION));
EXPECT_EQ(1.0f, cursor_test_api.GetCurrentCursor().device_scale_factor());
ASSERT_TRUE(resizer.get());
resizer->Drag(CalculateDragPoint(*resizer, 399, 200), 0);
TestIfMouseWarpsAt(gfx::Point(399, 200));
EXPECT_EQ(2.0f, cursor_test_api.GetCurrentCursor().device_scale_factor());
resizer->CompleteDrag();
EXPECT_EQ(2.0f, cursor_test_api.GetCurrentCursor().device_scale_factor());
}
// Move window from the root window with 2.0 device scale factor to the root
// window with 1.0 device scale factor.
{
// Make sure the window is on the default container first.
aura::Window* default_container =
GetRootWindowController(root_windows[1])
->GetContainer(kShellWindowId_DefaultContainer);
default_container->AddChild(window_.get());
window_->SetBoundsInScreen(
gfx::Rect(600, 0, 50, 60),
display::Screen::GetScreen()->GetDisplayNearestWindow(root_windows[1]));
EXPECT_EQ(root_windows[1], window_->GetRootWindow());
// Grab (0, 0) of the window.
std::unique_ptr<WindowResizer> resizer(
CreateDragWindowResizer(window_.get(), gfx::Point(), HTCAPTION));
EXPECT_EQ(2.0f, cursor_test_api.GetCurrentCursor().device_scale_factor());
ASSERT_TRUE(resizer.get());
resizer->Drag(CalculateDragPoint(*resizer, -200, 200), 0);
TestIfMouseWarpsAt(gfx::Point(400, 200));
EXPECT_EQ(1.0f, cursor_test_api.GetCurrentCursor().device_scale_factor());
resizer->CompleteDrag();
EXPECT_EQ(1.0f, cursor_test_api.GetCurrentCursor().device_scale_factor());
}
}
// Verifies several kinds of windows can be moved across displays.
TEST_P(DragWindowResizerTest, MoveWindowAcrossDisplays) {
if (!SupportsMultipleDisplays())
return;
// The secondary display is logically on the right, but on the system (e.g. X)
// layer, it's below the primary one. See UpdateDisplay() in ash_test_base.cc.
UpdateDisplay("400x400,400x400");
aura::Window::Windows root_windows = Shell::GetAllRootWindows();
ASSERT_EQ(2U, root_windows.size());
// Normal window can be moved across display.
{
aura::Window* window = window_.get();
window->SetBoundsInScreen(
gfx::Rect(0, 0, 50, 60),
display::Screen::GetScreen()->GetPrimaryDisplay());
// Grab (0, 0) of the window.
std::unique_ptr<WindowResizer> resizer(
CreateDragWindowResizer(window, gfx::Point(), HTCAPTION));
ASSERT_TRUE(resizer.get());
resizer->Drag(CalculateDragPoint(*resizer, 399, 200), 0);
EXPECT_TRUE(TestIfMouseWarpsAt(gfx::Point(399, 200)));
EXPECT_EQ("401,200",
aura::Env::GetInstance()->last_mouse_location().ToString());
resizer->CompleteDrag();
}
// Always on top window can be moved across display.
{
aura::Window* window = always_on_top_window_.get();
window->SetBoundsInScreen(
gfx::Rect(0, 0, 50, 60),
display::Screen::GetScreen()->GetPrimaryDisplay());
// Grab (0, 0) of the window.
std::unique_ptr<WindowResizer> resizer(
CreateDragWindowResizer(window, gfx::Point(), HTCAPTION));
ASSERT_TRUE(resizer.get());
resizer->Drag(CalculateDragPoint(*resizer, 399, 200), 0);
EXPECT_TRUE(TestIfMouseWarpsAt(gfx::Point(399, 200)));
EXPECT_EQ("401,200",
aura::Env::GetInstance()->last_mouse_location().ToString());
resizer->CompleteDrag();
}
// System modal window can be moved across display.
{
aura::Window* window = system_modal_window_.get();
window->SetBoundsInScreen(
gfx::Rect(0, 0, 50, 60),
display::Screen::GetScreen()->GetPrimaryDisplay());
aura::Env::GetInstance()->set_last_mouse_location(gfx::Point(0, 0));
// Grab (0, 0) of the window.
std::unique_ptr<WindowResizer> resizer(
CreateDragWindowResizer(window, gfx::Point(), HTCAPTION));
ASSERT_TRUE(resizer.get());
resizer->Drag(CalculateDragPoint(*resizer, 399, 200), 0);
EXPECT_TRUE(TestIfMouseWarpsAt(gfx::Point(399, 200)));
EXPECT_EQ("401,200",
aura::Env::GetInstance()->last_mouse_location().ToString());
resizer->CompleteDrag();
}
// Transient window cannot be moved across display.
{
aura::Window* window = transient_child_;
window->SetBoundsInScreen(
gfx::Rect(0, 0, 50, 60),
display::Screen::GetScreen()->GetPrimaryDisplay());
// Grab (0, 0) of the window.
std::unique_ptr<WindowResizer> resizer(
CreateDragWindowResizer(window, gfx::Point(), HTCAPTION));
ASSERT_TRUE(resizer.get());
resizer->Drag(CalculateDragPoint(*resizer, 399, 200), 0);
EXPECT_FALSE(TestIfMouseWarpsAt(gfx::Point(399, 200)));
EXPECT_EQ("399,200",
aura::Env::GetInstance()->last_mouse_location().ToString());
resizer->CompleteDrag();
}
// The parent of transient window can be moved across display.
{
aura::Window* window = transient_parent_.get();
window->SetBoundsInScreen(
gfx::Rect(0, 0, 50, 60),
display::Screen::GetScreen()->GetPrimaryDisplay());
// Grab (0, 0) of the window.
std::unique_ptr<WindowResizer> resizer(
CreateDragWindowResizer(window, gfx::Point(), HTCAPTION));
ASSERT_TRUE(resizer.get());
resizer->Drag(CalculateDragPoint(*resizer, 399, 200), 0);
EXPECT_TRUE(TestIfMouseWarpsAt(gfx::Point(399, 200)));
EXPECT_EQ("401,200",
aura::Env::GetInstance()->last_mouse_location().ToString());
resizer->CompleteDrag();
}
// Panel window can be moved across display.
{
aura::Window* window = panel_window_.get();
window->SetBoundsInScreen(
gfx::Rect(0, 0, 50, 60),
display::Screen::GetScreen()->GetPrimaryDisplay());
// Grab (0, 0) of the window.
std::unique_ptr<WindowResizer> resizer(
CreateDragWindowResizer(window, gfx::Point(), HTCAPTION));
ASSERT_TRUE(resizer.get());
resizer->Drag(CalculateDragPoint(*resizer, 399, 200), 0);
EXPECT_TRUE(TestIfMouseWarpsAt(gfx::Point(399, 200)));
EXPECT_EQ("401,200",
aura::Env::GetInstance()->last_mouse_location().ToString());
resizer->CompleteDrag();
}
}
} // namespace ash