drag_window_resizer_unittest.cc 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856
  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/wm/drag_window_resizer.h"
  5. #include "ash/display/mouse_cursor_event_filter.h"
  6. #include "ash/public/cpp/shelf_config.h"
  7. #include "ash/public/cpp/shell_window_ids.h"
  8. #include "ash/root_window_controller.h"
  9. #include "ash/shelf/shelf_layout_manager.h"
  10. #include "ash/shell.h"
  11. #include "ash/test/ash_test_base.h"
  12. #include "ash/wm/desks/desks_util.h"
  13. #include "ash/wm/drag_window_controller.h"
  14. #include "ash/wm/window_positioning_utils.h"
  15. #include "ash/wm/window_util.h"
  16. #include "base/strings/string_number_conversions.h"
  17. #include "base/strings/stringprintf.h"
  18. #include "third_party/abseil-cpp/absl/types/optional.h"
  19. #include "ui/aura/client/aura_constants.h"
  20. #include "ui/aura/env.h"
  21. #include "ui/aura/test/test_window_delegate.h"
  22. #include "ui/aura/window.h"
  23. #include "ui/base/cursor/cursor.h"
  24. #include "ui/base/hit_test.h"
  25. #include "ui/base/ui_base_types.h"
  26. #include "ui/compositor/layer.h"
  27. #include "ui/compositor/layer_delegate.h"
  28. #include "ui/compositor/layer_tree_owner.h"
  29. #include "ui/compositor_extra/shadow.h"
  30. #include "ui/display/display_layout.h"
  31. #include "ui/display/display_layout_builder.h"
  32. #include "ui/display/manager/display_manager.h"
  33. #include "ui/gfx/geometry/insets.h"
  34. #include "ui/views/widget/widget.h"
  35. #include "ui/wm/core/cursor_manager.h"
  36. #include "ui/wm/core/window_util.h"
  37. namespace ash {
  38. namespace {
  39. const int kRootHeight = 600;
  40. // Used to test if the OnPaintLayer is called by DragWindowLayerDelegate.
  41. class TestLayerDelegate : public ui::LayerDelegate {
  42. public:
  43. TestLayerDelegate() = default;
  44. TestLayerDelegate(const TestLayerDelegate&) = delete;
  45. TestLayerDelegate& operator=(const TestLayerDelegate&) = delete;
  46. ~TestLayerDelegate() override = default;
  47. int paint_count() const { return paint_count_; }
  48. private:
  49. // Paint content for the layer to the specified context.
  50. void OnPaintLayer(const ui::PaintContext& context) override {
  51. paint_count_++;
  52. }
  53. void OnDeviceScaleFactorChanged(float old_device_scale_factor,
  54. float new_device_scale_factor) override {}
  55. int paint_count_ = 0;
  56. };
  57. } // namespace
  58. class DragWindowResizerTest : public AshTestBase {
  59. public:
  60. DragWindowResizerTest() : transient_child_(nullptr) {}
  61. DragWindowResizerTest(const DragWindowResizerTest&) = delete;
  62. DragWindowResizerTest& operator=(const DragWindowResizerTest&) = delete;
  63. ~DragWindowResizerTest() override = default;
  64. void SetUp() override {
  65. AshTestBase::SetUp();
  66. UpdateDisplay(base::StringPrintf("800x%d", kRootHeight));
  67. aura::Window* root = Shell::GetPrimaryRootWindow();
  68. gfx::Rect root_bounds(root->bounds());
  69. EXPECT_EQ(kRootHeight, root_bounds.height());
  70. EXPECT_EQ(800, root_bounds.width());
  71. window_ = std::make_unique<aura::Window>(&delegate_);
  72. window_->SetType(aura::client::WINDOW_TYPE_NORMAL);
  73. window_->Init(ui::LAYER_NOT_DRAWN);
  74. ParentWindowInPrimaryRootWindow(window_.get());
  75. window_->SetId(1);
  76. always_on_top_window_ = std::make_unique<aura::Window>(&delegate2_);
  77. always_on_top_window_->SetType(aura::client::WINDOW_TYPE_NORMAL);
  78. always_on_top_window_->SetProperty(aura::client::kZOrderingKey,
  79. ui::ZOrderLevel::kFloatingWindow);
  80. always_on_top_window_->Init(ui::LAYER_NOT_DRAWN);
  81. ParentWindowInPrimaryRootWindow(always_on_top_window_.get());
  82. always_on_top_window_->SetId(2);
  83. system_modal_window_ = std::make_unique<aura::Window>(&delegate3_);
  84. system_modal_window_->SetType(aura::client::WINDOW_TYPE_NORMAL);
  85. system_modal_window_->SetProperty(aura::client::kModalKey,
  86. ui::MODAL_TYPE_SYSTEM);
  87. system_modal_window_->Init(ui::LAYER_NOT_DRAWN);
  88. ParentWindowInPrimaryRootWindow(system_modal_window_.get());
  89. system_modal_window_->SetId(3);
  90. transient_child_ = new aura::Window(&delegate4_);
  91. transient_child_->SetType(aura::client::WINDOW_TYPE_NORMAL);
  92. transient_child_->Init(ui::LAYER_NOT_DRAWN);
  93. ParentWindowInPrimaryRootWindow(transient_child_);
  94. transient_child_->SetId(4);
  95. transient_parent_ = std::make_unique<aura::Window>(&delegate5_);
  96. transient_parent_->SetType(aura::client::WINDOW_TYPE_NORMAL);
  97. transient_parent_->Init(ui::LAYER_NOT_DRAWN);
  98. ParentWindowInPrimaryRootWindow(transient_parent_.get());
  99. ::wm::AddTransientChild(transient_parent_.get(), transient_child_);
  100. transient_parent_->SetId(5);
  101. }
  102. void TearDown() override {
  103. window_.reset();
  104. always_on_top_window_.reset();
  105. system_modal_window_.reset();
  106. transient_parent_.reset();
  107. AshTestBase::TearDown();
  108. }
  109. protected:
  110. gfx::PointF CalculateDragPoint(const WindowResizer& resizer,
  111. int delta_x,
  112. int delta_y) const {
  113. gfx::PointF location = resizer.GetInitialLocation();
  114. location.set_x(location.x() + delta_x);
  115. location.set_y(location.y() + delta_y);
  116. return location;
  117. }
  118. ShelfLayoutManager* shelf_layout_manager() {
  119. return Shell::GetPrimaryRootWindowController()->GetShelfLayoutManager();
  120. }
  121. static WindowResizer* CreateDragWindowResizer(
  122. aura::Window* window,
  123. const gfx::Point& point_in_parent,
  124. int window_component) {
  125. return CreateWindowResizer(window, gfx::PointF(point_in_parent),
  126. window_component, ::wm::WINDOW_MOVE_SOURCE_MOUSE)
  127. .release();
  128. }
  129. bool TestIfMouseWarpsAt(const gfx::Point& point_in_screen) {
  130. return AshTestBase::TestIfMouseWarpsAt(GetEventGenerator(),
  131. point_in_screen);
  132. }
  133. aura::test::TestWindowDelegate delegate_;
  134. aura::test::TestWindowDelegate delegate2_;
  135. aura::test::TestWindowDelegate delegate3_;
  136. aura::test::TestWindowDelegate delegate4_;
  137. aura::test::TestWindowDelegate delegate5_;
  138. aura::test::TestWindowDelegate delegate6_;
  139. std::unique_ptr<aura::Window> window_;
  140. std::unique_ptr<aura::Window> always_on_top_window_;
  141. std::unique_ptr<aura::Window> system_modal_window_;
  142. aura::Window* transient_child_;
  143. std::unique_ptr<aura::Window> transient_parent_;
  144. };
  145. // Verifies a window can be moved from the primary display to another.
  146. TEST_F(DragWindowResizerTest, WindowDragWithMultiDisplays) {
  147. // Prevent snapping |window_|. We will drag within 25px of a display edge, for
  148. // the purpose of testing that a minimum visibility of 25px is enforced. When
  149. // a drag ends at that distance (or up to 32px) from a left or right display
  150. // edge, the dragged window is snapped. As that would only distract from the
  151. // purposes of the test, we avoid it by making |window_| unsnappable.
  152. // TODO: Remove this code after adding functionality where the mouse has to
  153. // dwell in the snap region before the dragged window can get snapped.
  154. window_->SetProperty(aura::client::kResizeBehaviorKey,
  155. aura::client::kResizeBehaviorNone);
  156. ASSERT_FALSE(WindowState::Get(window_.get())->CanSnap());
  157. // The secondary display is logically on the right, but on the system (e.g. X)
  158. // layer, it's below the primary one. See UpdateDisplay() in ash_test_base.cc.
  159. UpdateDisplay("800x600,400x300");
  160. aura::Window::Windows root_windows = Shell::GetAllRootWindows();
  161. ASSERT_EQ(2U, root_windows.size());
  162. window_->SetBoundsInScreen(gfx::Rect(0, 0, 50, 60),
  163. display::Screen::GetScreen()->GetPrimaryDisplay());
  164. EXPECT_EQ(root_windows[0], window_->GetRootWindow());
  165. {
  166. // Grab (0, 0) of the window.
  167. std::unique_ptr<WindowResizer> resizer(
  168. CreateDragWindowResizer(window_.get(), gfx::Point(), HTCAPTION));
  169. ASSERT_TRUE(resizer.get());
  170. // Drag the pointer to the right. Once it reaches the right edge of the
  171. // primary display, it warps to the secondary.
  172. // TODO(crbug.com/990589): Unit tests should be able to simulate mouse input
  173. // without having to call |CursorManager::SetDisplay|.
  174. Shell::Get()->cursor_manager()->SetDisplay(
  175. display::Screen::GetScreen()->GetDisplayNearestWindow(root_windows[1]));
  176. resizer->Drag(CalculateDragPoint(*resizer, 800, 10), 0);
  177. resizer->CompleteDrag();
  178. // The whole window is on the secondary display now. The parent should be
  179. // changed.
  180. EXPECT_EQ(root_windows[1], window_->GetRootWindow());
  181. EXPECT_EQ("0,10 50x60", window_->bounds().ToString());
  182. }
  183. window_->SetBoundsInScreen(gfx::Rect(0, 0, 50, 60),
  184. display::Screen::GetScreen()->GetPrimaryDisplay());
  185. EXPECT_EQ(root_windows[0], window_->GetRootWindow());
  186. {
  187. // Grab (0, 0) of the window and move the pointer to (795, 10).
  188. std::unique_ptr<WindowResizer> resizer(
  189. CreateDragWindowResizer(window_.get(), gfx::Point(), HTCAPTION));
  190. ASSERT_TRUE(resizer.get());
  191. // TODO(crbug.com/990589): Unit tests should be able to simulate mouse input
  192. // without having to call |CursorManager::SetDisplay|.
  193. Shell::Get()->cursor_manager()->SetDisplay(
  194. display::Screen::GetScreen()->GetDisplayNearestWindow(root_windows[0]));
  195. resizer->Drag(CalculateDragPoint(*resizer, 795, 10), 0);
  196. // Window should be adjusted for minimum visibility (25px) during the drag.
  197. EXPECT_EQ("775,10 50x60", window_->bounds().ToString());
  198. resizer->CompleteDrag();
  199. // Since the pointer is still on the primary root window, the parent should
  200. // not be changed.
  201. // Window origin should be adjusted for minimum visibility (25px).
  202. EXPECT_EQ(root_windows[0], window_->GetRootWindow());
  203. EXPECT_EQ("775,10 50x60", window_->bounds().ToString());
  204. }
  205. window_->SetBoundsInScreen(gfx::Rect(0, 0, 50, 60),
  206. display::Screen::GetScreen()->GetPrimaryDisplay());
  207. EXPECT_EQ(root_windows[0], window_->GetRootWindow());
  208. {
  209. // Grab the top-right edge of the window and move the pointer to (0, 10)
  210. // in the secondary root window's coordinates.
  211. std::unique_ptr<WindowResizer> resizer(
  212. CreateDragWindowResizer(window_.get(), gfx::Point(49, 0), HTCAPTION));
  213. ASSERT_TRUE(resizer.get());
  214. // TODO(crbug.com/990589): Unit tests should be able to simulate mouse input
  215. // without having to call |CursorManager::SetDisplay|.
  216. Shell::Get()->cursor_manager()->SetDisplay(
  217. display::Screen::GetScreen()->GetDisplayNearestWindow(root_windows[1]));
  218. resizer->Drag(CalculateDragPoint(*resizer, 751, 10), ui::EF_CONTROL_DOWN);
  219. resizer->CompleteDrag();
  220. // Since the pointer is on the secondary, the parent should be changed
  221. // even though only small fraction of the window is within the secondary
  222. // root window's bounds.
  223. EXPECT_EQ(root_windows[1], window_->GetRootWindow());
  224. // Window origin should be adjusted for minimum visibility (25px).
  225. int expected_x = -50 + kMinimumOnScreenArea;
  226. EXPECT_EQ(base::NumberToString(expected_x) + ",10 50x60",
  227. window_->bounds().ToString());
  228. }
  229. // Dropping a window that is larger than the destination work area
  230. // will shrink to fit to the work area.
  231. window_->SetBoundsInScreen(gfx::Rect(0, 0, 700, 500),
  232. display::Screen::GetScreen()->GetPrimaryDisplay());
  233. const int shelf_inset = 300 - ShelfConfig::Get()->shelf_size();
  234. EXPECT_EQ(root_windows[0], window_->GetRootWindow());
  235. {
  236. // Grab the top-right edge of the window and move the pointer to (0, 10)
  237. // in the secondary root window's coordinates.
  238. std::unique_ptr<WindowResizer> resizer(
  239. CreateDragWindowResizer(window_.get(), gfx::Point(699, 0), HTCAPTION));
  240. ASSERT_TRUE(resizer.get());
  241. resizer->Drag(CalculateDragPoint(*resizer, 101, 10), ui::EF_CONTROL_DOWN);
  242. resizer->CompleteDrag();
  243. EXPECT_EQ(root_windows[1], window_->GetRootWindow());
  244. // Window size should be adjusted to fit to the work area
  245. EXPECT_EQ(gfx::Size(400, shelf_inset).ToString(),
  246. window_->bounds().size().ToString());
  247. gfx::Rect window_bounds_in_screen = window_->GetBoundsInScreen();
  248. gfx::Rect intersect(window_->GetRootWindow()->GetBoundsInScreen());
  249. intersect.Intersect(window_bounds_in_screen);
  250. EXPECT_LE(10, intersect.width());
  251. EXPECT_LE(10, intersect.height());
  252. EXPECT_TRUE(window_bounds_in_screen.Contains(gfx::Point(800, 10)));
  253. }
  254. // Dropping a window that is larger than the destination work area
  255. // will shrink to fit to the work area.
  256. window_->SetBoundsInScreen(gfx::Rect(0, 0, 700, 500),
  257. display::Screen::GetScreen()->GetPrimaryDisplay());
  258. EXPECT_EQ(root_windows[0], window_->GetRootWindow());
  259. {
  260. // Grab the top-left edge of the window and move the pointer to (150, 10)
  261. // in the secondary root window's coordinates. Make sure the window is
  262. // shrink in such a way that it keeps the cursor within.
  263. std::unique_ptr<WindowResizer> resizer(
  264. CreateDragWindowResizer(window_.get(), gfx::Point(0, 0), HTCAPTION));
  265. ASSERT_TRUE(resizer.get());
  266. resizer->Drag(CalculateDragPoint(*resizer, 799, 10), ui::EF_CONTROL_DOWN);
  267. resizer->Drag(CalculateDragPoint(*resizer, 850, 10), ui::EF_CONTROL_DOWN);
  268. resizer->CompleteDrag();
  269. EXPECT_EQ(root_windows[1], window_->GetRootWindow());
  270. // Window size should be adjusted to fit to the work area
  271. EXPECT_EQ(gfx::Size(400, shelf_inset).ToString(),
  272. window_->bounds().size().ToString());
  273. gfx::Rect window_bounds_in_screen = window_->GetBoundsInScreen();
  274. gfx::Rect intersect(window_->GetRootWindow()->GetBoundsInScreen());
  275. intersect.Intersect(window_bounds_in_screen);
  276. EXPECT_LE(10, intersect.width());
  277. EXPECT_LE(10, intersect.height());
  278. EXPECT_TRUE(window_bounds_in_screen.Contains(gfx::Point(850, 10)));
  279. }
  280. }
  281. // Verifies that dragging the active window to another display makes the new
  282. // root window the active root window.
  283. TEST_F(DragWindowResizerTest, WindowDragWithMultiDisplaysActiveRoot) {
  284. // The secondary display is logically on the right, but on the system (e.g. X)
  285. // layer, it's below the primary one. See UpdateDisplay() in ash_test_base.cc.
  286. UpdateDisplay("800x600,800x600");
  287. aura::Window::Windows root_windows = Shell::GetAllRootWindows();
  288. ASSERT_EQ(2U, root_windows.size());
  289. aura::test::TestWindowDelegate delegate;
  290. std::unique_ptr<aura::Window> window =
  291. std::make_unique<aura::Window>(&delegate);
  292. window->SetType(aura::client::WINDOW_TYPE_NORMAL);
  293. window->Init(ui::LAYER_TEXTURED);
  294. ParentWindowInPrimaryRootWindow(window.get());
  295. window->SetBoundsInScreen(gfx::Rect(0, 0, 50, 60),
  296. display::Screen::GetScreen()->GetPrimaryDisplay());
  297. window->Show();
  298. EXPECT_TRUE(wm::CanActivateWindow(window.get()));
  299. wm::ActivateWindow(window.get());
  300. EXPECT_EQ(root_windows[0], window->GetRootWindow());
  301. EXPECT_EQ(root_windows[0], Shell::GetRootWindowForNewWindows());
  302. {
  303. // Grab (0, 0) of the window.
  304. std::unique_ptr<WindowResizer> resizer(
  305. CreateDragWindowResizer(window.get(), gfx::Point(), HTCAPTION));
  306. ASSERT_TRUE(resizer.get());
  307. // Drag the pointer to the right. Once it reaches the right edge of the
  308. // primary display, it warps to the secondary.
  309. // TODO(crbug.com/990589): Unit tests should be able to simulate mouse input
  310. // without having to call |CursorManager::SetDisplay|.
  311. Shell::Get()->cursor_manager()->SetDisplay(
  312. display::Screen::GetScreen()->GetDisplayNearestWindow(root_windows[1]));
  313. resizer->Drag(CalculateDragPoint(*resizer, 800, 10), 0);
  314. resizer->CompleteDrag();
  315. // The whole window is on the secondary display now. The parent should be
  316. // changed.
  317. EXPECT_EQ(root_windows[1], window->GetRootWindow());
  318. EXPECT_EQ(root_windows[1], Shell::GetRootWindowForNewWindows());
  319. }
  320. }
  321. // Verifies a window can be moved from the secondary display to primary.
  322. TEST_F(DragWindowResizerTest, WindowDragWithMultiDisplaysRightToLeft) {
  323. // See comment on similar code at beginning of |WindowDragWithMultiDisplays|.
  324. // TODO: Same TODO as with that other comment.
  325. window_->SetProperty(aura::client::kResizeBehaviorKey,
  326. aura::client::kResizeBehaviorNone);
  327. ASSERT_FALSE(WindowState::Get(window_.get())->CanSnap());
  328. UpdateDisplay("800x600,800x600");
  329. aura::Window::Windows root_windows = Shell::GetAllRootWindows();
  330. ASSERT_EQ(2U, root_windows.size());
  331. window_->SetBoundsInScreen(
  332. gfx::Rect(800, 00, 50, 60),
  333. display::Screen::GetScreen()->GetDisplayNearestWindow(root_windows[1]));
  334. EXPECT_EQ(root_windows[1], window_->GetRootWindow());
  335. {
  336. // Grab (0, 0) of the window.
  337. std::unique_ptr<WindowResizer> resizer(
  338. CreateDragWindowResizer(window_.get(), gfx::Point(), HTCAPTION));
  339. ASSERT_TRUE(resizer.get());
  340. // Move the mouse near the right edge, (798, 0), of the primary display.
  341. resizer->Drag(CalculateDragPoint(*resizer, -2, 0), ui::EF_CONTROL_DOWN);
  342. resizer->CompleteDrag();
  343. EXPECT_EQ(root_windows[0], window_->GetRootWindow());
  344. // Window origin should be adjusted for minimum visibility (25px).
  345. EXPECT_EQ("775,0 50x60", window_->bounds().ToString());
  346. }
  347. }
  348. // Verifies the drag window is shown correctly.
  349. TEST_F(DragWindowResizerTest, DragWindowController) {
  350. UpdateDisplay("800x600,800x600");
  351. aura::Window::Windows root_windows = Shell::GetAllRootWindows();
  352. ASSERT_EQ(2U, root_windows.size());
  353. TestLayerDelegate delegate;
  354. window_->layer()->set_delegate(&delegate);
  355. window_->SetBoundsInScreen(gfx::Rect(0, 0, 50, 60),
  356. display::Screen::GetScreen()->GetPrimaryDisplay());
  357. EXPECT_EQ(root_windows[0], window_->GetRootWindow());
  358. EXPECT_FLOAT_EQ(1.0f, window_->layer()->opacity());
  359. {
  360. // Hold the center of the window so that the window doesn't stick to the
  361. // edge when dragging around the edge of the display.
  362. std::unique_ptr<WindowResizer> resizer(
  363. CreateDragWindowResizer(window_.get(), gfx::Point(25, 30), HTCAPTION));
  364. ASSERT_TRUE(resizer.get());
  365. DragWindowResizer* drag_resizer = DragWindowResizer::instance_;
  366. ASSERT_TRUE(drag_resizer);
  367. EXPECT_FALSE(drag_resizer->drag_window_controller_.get());
  368. // The pointer is inside the primary root. The drag window controller
  369. // should be NULL.
  370. ASSERT_EQ(display::Screen::GetScreen()
  371. ->GetDisplayNearestWindow(root_windows[0])
  372. .id(),
  373. Shell::Get()->cursor_manager()->GetDisplay().id());
  374. resizer->Drag(CalculateDragPoint(*resizer, 10, 10), 0);
  375. DragWindowController* controller =
  376. drag_resizer->drag_window_controller_.get();
  377. EXPECT_EQ(0, controller->GetDragWindowsCountForTest());
  378. // The window spans both root windows.
  379. resizer->Drag(CalculateDragPoint(*resizer, 773, 10), 0);
  380. EXPECT_EQ(1, controller->GetDragWindowsCountForTest());
  381. const aura::Window* drag_window = controller->GetDragWindowForTest(0);
  382. ASSERT_TRUE(drag_window);
  383. const ui::Layer* drag_layer = drag_window->layer();
  384. ASSERT_TRUE(drag_layer);
  385. // Check if |resizer->layer_| is properly set to the drag widget.
  386. EXPECT_FALSE(drag_layer->children().empty());
  387. // The paint request on a drag window should reach the original delegate.
  388. controller->RequestLayerPaintForTest();
  389. EXPECT_EQ(1, delegate.paint_count());
  390. // Invalidating the delegate on the original layer should prevent
  391. // calling the OnPaintLayer on the original delegate from new delegate.
  392. window_->layer()->set_delegate(nullptr);
  393. controller->RequestLayerPaintForTest();
  394. EXPECT_EQ(1, delegate.paint_count());
  395. // |window_| should be opaque since the pointer is still on the primary
  396. // root window. The drag window should be semi-transparent.
  397. EXPECT_FLOAT_EQ(1.0f, window_->layer()->opacity());
  398. EXPECT_GT(1.0f, drag_layer->opacity());
  399. // Enter the pointer to the secondary display.
  400. // TODO(crbug.com/990589): Unit tests should be able to simulate mouse input
  401. // without having to call |CursorManager::SetDisplay|.
  402. Shell::Get()->cursor_manager()->SetDisplay(
  403. display::Screen::GetScreen()->GetDisplayNearestWindow(root_windows[1]));
  404. resizer->Drag(CalculateDragPoint(*resizer, 775, 10), 0);
  405. EXPECT_EQ(1, controller->GetDragWindowsCountForTest());
  406. // |window_| should be transparent, and the drag window should be opaque.
  407. EXPECT_GT(1.0f, window_->layer()->opacity());
  408. EXPECT_FLOAT_EQ(1.0f, drag_layer->opacity());
  409. resizer->CompleteDrag();
  410. EXPECT_EQ(root_windows[1], window_->GetRootWindow());
  411. EXPECT_FLOAT_EQ(1.0f, window_->layer()->opacity());
  412. }
  413. // Do the same test with RevertDrag().
  414. window_->SetBoundsInScreen(gfx::Rect(0, 0, 50, 60),
  415. display::Screen::GetScreen()->GetPrimaryDisplay());
  416. EXPECT_EQ(root_windows[0], window_->GetRootWindow());
  417. EXPECT_FLOAT_EQ(1.0f, window_->layer()->opacity());
  418. {
  419. std::unique_ptr<WindowResizer> resizer(
  420. CreateDragWindowResizer(window_.get(), gfx::Point(), HTCAPTION));
  421. ASSERT_TRUE(resizer.get());
  422. DragWindowResizer* drag_resizer = DragWindowResizer::instance_;
  423. DragWindowController* controller =
  424. drag_resizer->drag_window_controller_.get();
  425. ASSERT_TRUE(drag_resizer);
  426. EXPECT_FALSE(controller);
  427. resizer->Drag(CalculateDragPoint(*resizer, 0, 610), 0);
  428. resizer->RevertDrag();
  429. EXPECT_EQ(root_windows[0], window_->GetRootWindow());
  430. EXPECT_FLOAT_EQ(1.0f, window_->layer()->opacity());
  431. }
  432. }
  433. TEST_F(DragWindowResizerTest, DragWindowControllerAcrossThreeDisplays) {
  434. UpdateDisplay("400x600,400x600,800x600");
  435. aura::Window::Windows root_windows = Shell::GetAllRootWindows();
  436. // Layout so that all three displays touch each other.
  437. display::DisplayIdList list = display_manager()->GetConnectedDisplayIdList();
  438. ASSERT_EQ(3u, list.size());
  439. ASSERT_EQ(display::Screen::GetScreen()->GetPrimaryDisplay().id(), list[0]);
  440. display::DisplayLayoutBuilder builder(list[0]);
  441. builder.AddDisplayPlacement(list[1], list[0],
  442. display::DisplayPlacement::RIGHT, 0);
  443. builder.AddDisplayPlacement(list[2], list[0],
  444. display::DisplayPlacement::BOTTOM, 0);
  445. display_manager()->SetLayoutForCurrentDisplays(builder.Build());
  446. // Sanity check.
  447. ASSERT_EQ(gfx::Rect(0, 000, 400, 600),
  448. display_manager()->GetDisplayForId(list[0]).bounds());
  449. ASSERT_EQ(gfx::Rect(400, 0, 400, 600),
  450. display_manager()->GetDisplayForId(list[1]).bounds());
  451. ASSERT_EQ(gfx::Rect(0, 600, 800, 600),
  452. display_manager()->GetDisplayForId(list[2]).bounds());
  453. // Create a window on 2nd display.
  454. window_->SetBoundsInScreen(gfx::Rect(400, 0, 100, 100),
  455. display_manager()->GetDisplayForId(list[1]));
  456. ASSERT_EQ(root_windows[1], window_->GetRootWindow());
  457. // Hold the center of the window so that the window doesn't stick to the edge
  458. // when dragging around the edge of the display.
  459. std::unique_ptr<WindowResizer> resizer(
  460. CreateDragWindowResizer(window_.get(), gfx::Point(50, 50), HTCAPTION));
  461. ASSERT_TRUE(resizer.get());
  462. DragWindowResizer* drag_resizer = DragWindowResizer::instance_;
  463. ASSERT_TRUE(drag_resizer);
  464. EXPECT_FALSE(drag_resizer->drag_window_controller_.get());
  465. // TODO(crbug.com/990589): Unit tests should be able to simulate mouse input
  466. // without having to call |CursorManager::SetDisplay|.
  467. Shell::Get()->cursor_manager()->SetDisplay(
  468. display::Screen::GetScreen()->GetDisplayNearestWindow(root_windows[1]));
  469. resizer->Drag(CalculateDragPoint(*resizer, -50, 0), 0);
  470. DragWindowController* controller =
  471. drag_resizer->drag_window_controller_.get();
  472. ASSERT_TRUE(controller);
  473. ASSERT_EQ(1, controller->GetDragWindowsCountForTest());
  474. const aura::Window* drag_window0 = controller->GetDragWindowForTest(0);
  475. ASSERT_TRUE(drag_window0);
  476. const ui::Layer* drag_layer0 = drag_window0->layer();
  477. EXPECT_EQ(root_windows[0], drag_window0->GetRootWindow());
  478. // |window_| should be opaque since the pointer is still on the primary
  479. // root window. The drag window should be semi-transparent.
  480. EXPECT_FLOAT_EQ(1.0f, window_->layer()->opacity());
  481. EXPECT_GT(1.0f, drag_layer0->opacity());
  482. // The window spans across 3 displays, dragging to 3rd display.
  483. resizer->Drag(CalculateDragPoint(*resizer, -50, 549), 0);
  484. ASSERT_EQ(2, controller->GetDragWindowsCountForTest());
  485. drag_window0 = controller->GetDragWindowForTest(0);
  486. const aura::Window* drag_window1 = controller->GetDragWindowForTest(1);
  487. drag_layer0 = drag_window0->layer();
  488. const ui::Layer* drag_layer1 = drag_window1->layer();
  489. EXPECT_EQ(root_windows[0], drag_window0->GetRootWindow());
  490. EXPECT_EQ(root_windows[2], drag_window1->GetRootWindow());
  491. // |window_| should be opaque since the pointer is still on the 2nd
  492. // root window. The drag window should be semi-transparent.
  493. EXPECT_FLOAT_EQ(1.0f, window_->layer()->opacity());
  494. EXPECT_GT(1.0f, drag_layer0->opacity());
  495. EXPECT_GT(1.0f, drag_layer1->opacity());
  496. // TODO(crbug.com/990589): Unit tests should be able to simulate mouse input
  497. // without having to call |CursorManager::SetDisplay|.
  498. Shell::Get()->cursor_manager()->SetDisplay(
  499. display::Screen::GetScreen()->GetDisplayNearestWindow(root_windows[0]));
  500. resizer->Drag(CalculateDragPoint(*resizer, -51, 549), 0);
  501. ASSERT_EQ(2, controller->GetDragWindowsCountForTest());
  502. drag_window0 = controller->GetDragWindowForTest(0);
  503. drag_window1 = controller->GetDragWindowForTest(1);
  504. drag_layer0 = drag_window0->layer();
  505. drag_layer1 = drag_window1->layer();
  506. EXPECT_EQ(root_windows[0], drag_window0->GetRootWindow());
  507. EXPECT_EQ(root_windows[2], drag_window1->GetRootWindow());
  508. // |window_| should be transparent since the pointer is still on the primary
  509. // root window. The drag window should be semi-transparent.
  510. EXPECT_GT(1.0f, window_->layer()->opacity());
  511. EXPECT_FLOAT_EQ(1.0f, drag_layer0->opacity());
  512. EXPECT_GT(1.0f, drag_layer1->opacity());
  513. // Enter the pointer to the 3rd. Since it's bottom, the window snaps and
  514. // no drag windwos are created.
  515. // TODO(crbug.com/990589): Unit tests should be able to simulate mouse input
  516. // without having to call |CursorManager::SetDisplay|.
  517. Shell::Get()->cursor_manager()->SetDisplay(
  518. display::Screen::GetScreen()->GetDisplayNearestWindow(root_windows[2]));
  519. resizer->Drag(CalculateDragPoint(*resizer, -51, 551), 0);
  520. ASSERT_EQ(1, controller->GetDragWindowsCountForTest());
  521. drag_window0 = controller->GetDragWindowForTest(0);
  522. drag_layer0 = drag_window0->layer();
  523. EXPECT_EQ(root_windows[2], drag_window0->GetRootWindow());
  524. // |window_| should be transparent, and the drag window should be opaque.
  525. EXPECT_FLOAT_EQ(0.0f, window_->layer()->opacity());
  526. EXPECT_FLOAT_EQ(1.0f, drag_layer0->opacity());
  527. resizer->CompleteDrag();
  528. EXPECT_EQ(root_windows[2], window_->GetRootWindow());
  529. EXPECT_FLOAT_EQ(1.0f, window_->layer()->opacity());
  530. }
  531. TEST_F(DragWindowResizerTest, DragWindowControllerWithCustomShadowBounds) {
  532. UpdateDisplay("400x600,400x600,800x600");
  533. aura::Window::Windows root_windows = Shell::GetAllRootWindows();
  534. // Layout so that all three displays touch each other.
  535. display::DisplayIdList list = display_manager()->GetConnectedDisplayIdList();
  536. ASSERT_EQ(3u, list.size());
  537. ASSERT_EQ(display::Screen::GetScreen()->GetPrimaryDisplay().id(), list[0]);
  538. display::DisplayLayoutBuilder builder(list[0]);
  539. builder.AddDisplayPlacement(list[1], list[0],
  540. display::DisplayPlacement::RIGHT, 0);
  541. builder.AddDisplayPlacement(list[2], list[0],
  542. display::DisplayPlacement::BOTTOM, 0);
  543. display_manager()->SetLayoutForCurrentDisplays(builder.Build());
  544. const display::Display& display0 =
  545. display_manager()->GetDisplayForId(list[0]);
  546. const display::Display& display1 =
  547. display_manager()->GetDisplayForId(list[1]);
  548. const display::Display& display2 =
  549. display_manager()->GetDisplayForId(list[2]);
  550. // Sanity check.
  551. ASSERT_EQ(gfx::Rect(0, 000, 400, 600), display0.bounds());
  552. ASSERT_EQ(gfx::Rect(400, 0, 400, 600), display1.bounds());
  553. ASSERT_EQ(gfx::Rect(0, 600, 800, 600), display2.bounds());
  554. const gfx::Rect shadow_bounds(10, 10, 200, 200);
  555. const auto update_controller_and_check_root_and_shadow =
  556. [&shadow_bounds](DragWindowController* controller,
  557. aura::Window* root_window) {
  558. controller->Update();
  559. ASSERT_EQ(1, controller->GetDragWindowsCountForTest());
  560. EXPECT_EQ(root_window,
  561. controller->GetDragWindowForTest(0)->GetRootWindow());
  562. const ui::Shadow* shadow = controller->GetDragWindowShadowForTest(0);
  563. ASSERT_TRUE(shadow);
  564. ASSERT_TRUE(shadow->layer());
  565. EXPECT_TRUE(shadow->layer()->visible());
  566. EXPECT_EQ(shadow_bounds, shadow->content_bounds());
  567. };
  568. // Test mouse dragging.
  569. {
  570. wm::CursorManager* cursor_manager = Shell::Get()->cursor_manager();
  571. // Start on |display1|.
  572. cursor_manager->SetDisplay(display1);
  573. window_->SetBoundsInScreen(gfx::Rect(420, 20, 100, 100), display1);
  574. DragWindowController controller(window_.get(), /*is_touch_dragging=*/false,
  575. absl::make_optional(shadow_bounds));
  576. // Move to |display0|.
  577. cursor_manager->SetDisplay(display0);
  578. window_->SetBoundsInScreen(gfx::Rect(20, 20, 100, 100), display0);
  579. update_controller_and_check_root_and_shadow(&controller, root_windows[0]);
  580. // Move to |display2|.
  581. cursor_manager->SetDisplay(display2);
  582. window_->SetBoundsInScreen(gfx::Rect(20, 620, 100, 100), display2);
  583. update_controller_and_check_root_and_shadow(&controller, root_windows[2]);
  584. }
  585. // Test touch dragging.
  586. {
  587. // Start on |display0|.
  588. window_->SetBoundsInScreen(gfx::Rect(20, 20, 100, 100), display0);
  589. DragWindowController controller(window_.get(), /*is_touch_dragging=*/true,
  590. absl::make_optional(shadow_bounds));
  591. // Move the window so some is visible on |display1|.
  592. window_->SetBoundsInScreen(gfx::Rect(380, 20, 100, 100), display0);
  593. update_controller_and_check_root_and_shadow(&controller, root_windows[1]);
  594. // Move the window so some is visible on |display2|.
  595. window_->SetBoundsInScreen(gfx::Rect(20, 580, 100, 100), display0);
  596. update_controller_and_check_root_and_shadow(&controller, root_windows[2]);
  597. }
  598. }
  599. // Verifies if the resizer sets and resets
  600. // MouseCursorEventFilter::mouse_warp_mode_ as expected.
  601. TEST_F(DragWindowResizerTest, WarpMousePointer) {
  602. MouseCursorEventFilter* event_filter = Shell::Get()->mouse_cursor_filter();
  603. ASSERT_TRUE(event_filter);
  604. window_->SetBounds(gfx::Rect(0, 0, 50, 60));
  605. EXPECT_TRUE(event_filter->mouse_warp_enabled_);
  606. {
  607. std::unique_ptr<WindowResizer> resizer(
  608. CreateDragWindowResizer(window_.get(), gfx::Point(), HTCAPTION));
  609. // While dragging a window, warp should be allowed.
  610. EXPECT_TRUE(event_filter->mouse_warp_enabled_);
  611. resizer->CompleteDrag();
  612. }
  613. EXPECT_TRUE(event_filter->mouse_warp_enabled_);
  614. {
  615. std::unique_ptr<WindowResizer> resizer(
  616. CreateDragWindowResizer(window_.get(), gfx::Point(), HTCAPTION));
  617. EXPECT_TRUE(event_filter->mouse_warp_enabled_);
  618. resizer->RevertDrag();
  619. }
  620. EXPECT_TRUE(event_filter->mouse_warp_enabled_);
  621. {
  622. std::unique_ptr<WindowResizer> resizer(
  623. CreateDragWindowResizer(window_.get(), gfx::Point(), HTRIGHT));
  624. // While resizing a window, warp should NOT be allowed.
  625. EXPECT_FALSE(event_filter->mouse_warp_enabled_);
  626. resizer->CompleteDrag();
  627. }
  628. EXPECT_TRUE(event_filter->mouse_warp_enabled_);
  629. {
  630. std::unique_ptr<WindowResizer> resizer(
  631. CreateDragWindowResizer(window_.get(), gfx::Point(), HTRIGHT));
  632. EXPECT_FALSE(event_filter->mouse_warp_enabled_);
  633. resizer->RevertDrag();
  634. }
  635. EXPECT_TRUE(event_filter->mouse_warp_enabled_);
  636. }
  637. // Verifies cursor's device scale factor is updated whe a window is moved across
  638. // root windows with different device scale factors (http://crbug.com/154183).
  639. TEST_F(DragWindowResizerTest, CursorDeviceScaleFactor) {
  640. // The secondary display is logically on the right, but on the system (e.g. X)
  641. // layer, it's below the primary one. See UpdateDisplay() in ash_test_base.cc.
  642. UpdateDisplay("600x400,1000x800*2");
  643. aura::Window::Windows root_windows = Shell::GetAllRootWindows();
  644. ASSERT_EQ(2U, root_windows.size());
  645. const display::Display display0 =
  646. display::Screen::GetScreen()->GetDisplayNearestWindow(root_windows[0]);
  647. const display::Display display1 =
  648. display::Screen::GetScreen()->GetDisplayNearestWindow(root_windows[1]);
  649. auto* cursor_manager = Shell::Get()->cursor_manager();
  650. // Move window from the root window with 1.0 device scale factor to the root
  651. // window with 2.0 device scale factor.
  652. {
  653. window_->SetBoundsInScreen(gfx::Rect(0, 0, 50, 60), display0);
  654. EXPECT_EQ(root_windows[0], window_->GetRootWindow());
  655. // Grab (0, 0) of the window.
  656. std::unique_ptr<WindowResizer> resizer(
  657. CreateDragWindowResizer(window_.get(), gfx::Point(), HTCAPTION));
  658. EXPECT_EQ(1.0f, cursor_manager->GetCursor().image_scale_factor());
  659. ASSERT_TRUE(resizer.get());
  660. resizer->Drag(CalculateDragPoint(*resizer, 399, 200), 0);
  661. TestIfMouseWarpsAt(gfx::Point(699, 200));
  662. EXPECT_EQ(2.0f, cursor_manager->GetCursor().image_scale_factor());
  663. resizer->CompleteDrag();
  664. EXPECT_EQ(2.0f, cursor_manager->GetCursor().image_scale_factor());
  665. }
  666. // Move window from the root window with 2.0 device scale factor to the root
  667. // window with 1.0 device scale factor.
  668. {
  669. window_->SetBoundsInScreen(gfx::Rect(600, 0, 50, 60), display1);
  670. EXPECT_EQ(root_windows[1], window_->GetRootWindow());
  671. // Grab (0, 0) of the window.
  672. std::unique_ptr<WindowResizer> resizer(
  673. CreateDragWindowResizer(window_.get(), gfx::Point(), HTCAPTION));
  674. EXPECT_EQ(2.0f, cursor_manager->GetCursor().image_scale_factor());
  675. ASSERT_TRUE(resizer.get());
  676. resizer->Drag(CalculateDragPoint(*resizer, -200, 200), 0);
  677. TestIfMouseWarpsAt(gfx::Point(400, 200));
  678. EXPECT_EQ(1.0f, cursor_manager->GetCursor().image_scale_factor());
  679. resizer->CompleteDrag();
  680. EXPECT_EQ(1.0f, cursor_manager->GetCursor().image_scale_factor());
  681. }
  682. }
  683. // Verifies several kinds of windows can be moved across displays.
  684. TEST_F(DragWindowResizerTest, MoveWindowAcrossDisplays) {
  685. // The secondary display is logically on the right, but on the system (e.g. X)
  686. // layer, it's below the primary one. See UpdateDisplay() in ash_test_base.cc.
  687. UpdateDisplay("400x300,400x300");
  688. aura::Window::Windows root_windows = Shell::GetAllRootWindows();
  689. ASSERT_EQ(2U, root_windows.size());
  690. // Normal window can be moved across display.
  691. {
  692. aura::Window* window = window_.get();
  693. window->SetBoundsInScreen(
  694. gfx::Rect(0, 0, 50, 60),
  695. display::Screen::GetScreen()->GetPrimaryDisplay());
  696. // Grab (0, 0) of the window.
  697. std::unique_ptr<WindowResizer> resizer(
  698. CreateDragWindowResizer(window, gfx::Point(), HTCAPTION));
  699. ASSERT_TRUE(resizer.get());
  700. resizer->Drag(CalculateDragPoint(*resizer, 399, 200), 0);
  701. EXPECT_TRUE(TestIfMouseWarpsAt(gfx::Point(399, 200)));
  702. EXPECT_EQ("401,200",
  703. aura::Env::GetInstance()->last_mouse_location().ToString());
  704. resizer->CompleteDrag();
  705. }
  706. // Always on top window can be moved across display.
  707. {
  708. aura::Window* window = always_on_top_window_.get();
  709. window->SetBoundsInScreen(
  710. gfx::Rect(0, 0, 50, 60),
  711. display::Screen::GetScreen()->GetPrimaryDisplay());
  712. // Grab (0, 0) of the window.
  713. std::unique_ptr<WindowResizer> resizer(
  714. CreateDragWindowResizer(window, gfx::Point(), HTCAPTION));
  715. ASSERT_TRUE(resizer.get());
  716. resizer->Drag(CalculateDragPoint(*resizer, 399, 200), 0);
  717. EXPECT_TRUE(TestIfMouseWarpsAt(gfx::Point(399, 200)));
  718. EXPECT_EQ("401,200",
  719. aura::Env::GetInstance()->last_mouse_location().ToString());
  720. resizer->CompleteDrag();
  721. }
  722. // System modal window can be moved across display.
  723. {
  724. aura::Window* window = system_modal_window_.get();
  725. window->SetBoundsInScreen(
  726. gfx::Rect(0, 0, 50, 60),
  727. display::Screen::GetScreen()->GetPrimaryDisplay());
  728. aura::Env::GetInstance()->SetLastMouseLocation(gfx::Point(0, 0));
  729. // Grab (0, 0) of the window.
  730. std::unique_ptr<WindowResizer> resizer(
  731. CreateDragWindowResizer(window, gfx::Point(), HTCAPTION));
  732. ASSERT_TRUE(resizer.get());
  733. resizer->Drag(CalculateDragPoint(*resizer, 399, 200), 0);
  734. EXPECT_TRUE(TestIfMouseWarpsAt(gfx::Point(399, 200)));
  735. EXPECT_EQ("401,200",
  736. aura::Env::GetInstance()->last_mouse_location().ToString());
  737. resizer->CompleteDrag();
  738. }
  739. // Transient window cannot be moved across display.
  740. {
  741. aura::Window* window = transient_child_;
  742. window->SetBoundsInScreen(
  743. gfx::Rect(0, 0, 50, 60),
  744. display::Screen::GetScreen()->GetPrimaryDisplay());
  745. // Grab (0, 0) of the window.
  746. std::unique_ptr<WindowResizer> resizer(
  747. CreateDragWindowResizer(window, gfx::Point(), HTCAPTION));
  748. ASSERT_TRUE(resizer.get());
  749. resizer->Drag(CalculateDragPoint(*resizer, 399, 200), 0);
  750. EXPECT_FALSE(TestIfMouseWarpsAt(gfx::Point(399, 200)));
  751. EXPECT_EQ("399,200",
  752. aura::Env::GetInstance()->last_mouse_location().ToString());
  753. resizer->CompleteDrag();
  754. }
  755. // The parent of transient window can be moved across display.
  756. {
  757. aura::Window* window = transient_parent_.get();
  758. window->SetBoundsInScreen(
  759. gfx::Rect(0, 0, 50, 60),
  760. display::Screen::GetScreen()->GetPrimaryDisplay());
  761. // Grab (0, 0) of the window.
  762. std::unique_ptr<WindowResizer> resizer(
  763. CreateDragWindowResizer(window, gfx::Point(), HTCAPTION));
  764. ASSERT_TRUE(resizer.get());
  765. resizer->Drag(CalculateDragPoint(*resizer, 399, 200), 0);
  766. EXPECT_TRUE(TestIfMouseWarpsAt(gfx::Point(399, 200)));
  767. EXPECT_EQ("401,200",
  768. aura::Env::GetInstance()->last_mouse_location().ToString());
  769. resizer->CompleteDrag();
  770. }
  771. }
  772. } // namespace ash