multi_window_resize_controller_unittest.cc 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693
  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/workspace/multi_window_resize_controller.h"
  5. #include "ash/frame/non_client_frame_view_ash.h"
  6. #include "ash/public/cpp/shelf_config.h"
  7. #include "ash/public/cpp/test/shell_test_api.h"
  8. #include "ash/shell.h"
  9. #include "ash/test/ash_test_base.h"
  10. #include "ash/test/test_window_builder.h"
  11. #include "ash/wm/overview/overview_controller.h"
  12. #include "ash/wm/tablet_mode/tablet_mode_controller.h"
  13. #include "ash/wm/window_state.h"
  14. #include "ash/wm/window_state_delegate.h"
  15. #include "ash/wm/window_util.h"
  16. #include "ash/wm/wm_event.h"
  17. #include "ash/wm/workspace/workspace_event_handler_test_helper.h"
  18. #include "ash/wm/workspace_controller.h"
  19. #include "ash/wm/workspace_controller_test_api.h"
  20. #include "base/containers/contains.h"
  21. #include "base/memory/ptr_util.h"
  22. #include "chromeos/ui/base/chromeos_ui_constants.h"
  23. #include "ui/aura/client/aura_constants.h"
  24. #include "ui/aura/test/test_window_delegate.h"
  25. #include "ui/aura/window.h"
  26. #include "ui/base/class_property.h"
  27. #include "ui/base/hit_test.h"
  28. #include "ui/events/test/event_generator.h"
  29. #include "ui/views/widget/widget.h"
  30. #include "ui/views/widget/widget_delegate.h"
  31. using chromeos::kResizeInsideBoundsSize;
  32. using chromeos::kResizeOutsideBoundsSize;
  33. using chromeos::WindowStateType;
  34. namespace ash {
  35. namespace {
  36. // WidgetDelegate for a resizable widget which creates a NonClientFrameView
  37. // which is actually used in Ash.
  38. class TestWidgetDelegate : public views::WidgetDelegateView {
  39. public:
  40. TestWidgetDelegate() {}
  41. TestWidgetDelegate(const TestWidgetDelegate&) = delete;
  42. TestWidgetDelegate& operator=(const TestWidgetDelegate&) = delete;
  43. ~TestWidgetDelegate() override = default;
  44. std::unique_ptr<views::NonClientFrameView> CreateNonClientFrameView(
  45. views::Widget* widget) override {
  46. return std::make_unique<NonClientFrameViewAsh>(widget);
  47. }
  48. };
  49. } // namespace
  50. class MultiWindowResizeControllerTest : public AshTestBase {
  51. public:
  52. MultiWindowResizeControllerTest() = default;
  53. MultiWindowResizeControllerTest(const MultiWindowResizeControllerTest&) =
  54. delete;
  55. MultiWindowResizeControllerTest& operator=(
  56. const MultiWindowResizeControllerTest&) = delete;
  57. ~MultiWindowResizeControllerTest() override = default;
  58. void SetUp() override {
  59. AshTestBase::SetUp();
  60. WorkspaceController* wc = ShellTestApi().workspace_controller();
  61. WorkspaceEventHandler* event_handler =
  62. WorkspaceControllerTestApi(wc).GetEventHandler();
  63. resize_controller_ =
  64. WorkspaceEventHandlerTestHelper(event_handler).resize_controller();
  65. }
  66. protected:
  67. void ShowNow() { resize_controller_->ShowNow(); }
  68. bool IsShowing() { return resize_controller_->IsShowing(); }
  69. bool HasPendingShow() { return resize_controller_->show_timer_.IsRunning(); }
  70. bool HasTarget(aura::Window* window) {
  71. if (!resize_controller_->windows_.is_valid())
  72. return false;
  73. if (resize_controller_->windows_.window1 == window ||
  74. resize_controller_->windows_.window2 == window) {
  75. return true;
  76. }
  77. return base::Contains(resize_controller_->windows_.other_windows, window);
  78. }
  79. bool IsOverWindows(const gfx::Point& loc) {
  80. return resize_controller_->IsOverWindows(loc);
  81. }
  82. views::Widget* resize_widget() {
  83. return resize_controller_->resize_widget_.get();
  84. }
  85. MultiWindowResizeController* resize_controller_ = nullptr;
  86. };
  87. // Assertions around moving mouse over 2 windows.
  88. TEST_F(MultiWindowResizeControllerTest, BasicTests) {
  89. aura::test::TestWindowDelegate delegate1;
  90. std::unique_ptr<aura::Window> w1(CreateTestWindowInShellWithDelegate(
  91. &delegate1, -1, gfx::Rect(0, 0, 100, 100)));
  92. delegate1.set_window_component(HTRIGHT);
  93. aura::test::TestWindowDelegate delegate2;
  94. std::unique_ptr<aura::Window> w2(CreateTestWindowInShellWithDelegate(
  95. &delegate2, -2, gfx::Rect(100, 0, 100, 100)));
  96. delegate2.set_window_component(HTRIGHT);
  97. ui::test::EventGenerator* generator = GetEventGenerator();
  98. generator->MoveMouseTo(w1->bounds().CenterPoint());
  99. EXPECT_TRUE(HasPendingShow());
  100. EXPECT_TRUE(IsShowing());
  101. // Force a show now.
  102. ShowNow();
  103. EXPECT_FALSE(HasPendingShow());
  104. EXPECT_TRUE(IsShowing());
  105. EXPECT_FALSE(IsOverWindows(gfx::Point(200, 200)));
  106. // Have to explicitly invoke this as MouseWatcher listens for native events.
  107. resize_controller_->MouseMovedOutOfHost();
  108. EXPECT_FALSE(HasPendingShow());
  109. EXPECT_FALSE(IsShowing());
  110. }
  111. // Test the behavior of IsOverWindows().
  112. TEST_F(MultiWindowResizeControllerTest, IsOverWindows) {
  113. // Create the following layout:
  114. // __________________
  115. // | w1 | w2 |
  116. // | |________|
  117. // | | w3 |
  118. // |________|________|
  119. std::unique_ptr<views::Widget> w1(new views::Widget);
  120. views::Widget::InitParams params1;
  121. params1.delegate = new TestWidgetDelegate;
  122. params1.delegate->SetCanResize(true);
  123. params1.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  124. params1.bounds = gfx::Rect(100, 200);
  125. params1.context = GetContext();
  126. w1->Init(std::move(params1));
  127. w1->Show();
  128. std::unique_ptr<views::Widget> w2(new views::Widget);
  129. views::Widget::InitParams params2;
  130. params2.delegate = new TestWidgetDelegate;
  131. params2.delegate->SetCanResize(true);
  132. params2.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  133. params2.bounds = gfx::Rect(100, 0, 100, 100);
  134. params2.context = GetContext();
  135. w2->Init(std::move(params2));
  136. w2->Show();
  137. std::unique_ptr<views::Widget> w3(new views::Widget);
  138. views::Widget::InitParams params3;
  139. params3.delegate = new TestWidgetDelegate;
  140. params3.delegate->SetCanResize(true);
  141. params3.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  142. params3.bounds = gfx::Rect(100, 100, 100, 100);
  143. params3.context = GetContext();
  144. w3->Init(std::move(params3));
  145. w3->Show();
  146. ui::test::EventGenerator* generator = GetEventGenerator();
  147. generator->MoveMouseTo(gfx::Point(100, 150));
  148. EXPECT_TRUE(HasPendingShow());
  149. EXPECT_TRUE(IsShowing());
  150. ShowNow();
  151. EXPECT_TRUE(IsShowing());
  152. // Check that the multi-window resize handle does not hide while the mouse is
  153. // over a window's resize area. A window's resize area extends outside the
  154. // window's bounds.
  155. EXPECT_TRUE(w3->IsActive());
  156. ASSERT_LT(kResizeInsideBoundsSize, kResizeOutsideBoundsSize);
  157. EXPECT_TRUE(IsOverWindows(gfx::Point(100, 150)));
  158. EXPECT_TRUE(IsOverWindows(gfx::Point(100 - kResizeOutsideBoundsSize, 150)));
  159. EXPECT_FALSE(
  160. IsOverWindows(gfx::Point(100 - kResizeOutsideBoundsSize - 1, 150)));
  161. EXPECT_TRUE(
  162. IsOverWindows(gfx::Point(100 + kResizeInsideBoundsSize - 1, 150)));
  163. EXPECT_FALSE(IsOverWindows(gfx::Point(100 + kResizeInsideBoundsSize, 150)));
  164. EXPECT_FALSE(
  165. IsOverWindows(gfx::Point(100 + kResizeOutsideBoundsSize - 1, 150)));
  166. w1->Activate();
  167. EXPECT_TRUE(IsOverWindows(gfx::Point(100, 150)));
  168. EXPECT_TRUE(IsOverWindows(gfx::Point(100 - kResizeInsideBoundsSize, 150)));
  169. EXPECT_FALSE(
  170. IsOverWindows(gfx::Point(100 - kResizeInsideBoundsSize - 1, 150)));
  171. EXPECT_FALSE(IsOverWindows(gfx::Point(100 - kResizeOutsideBoundsSize, 150)));
  172. EXPECT_TRUE(
  173. IsOverWindows(gfx::Point(100 + kResizeOutsideBoundsSize - 1, 150)));
  174. EXPECT_FALSE(IsOverWindows(gfx::Point(100 + kResizeOutsideBoundsSize, 150)));
  175. // Check that the multi-window resize handles eventually hide if the mouse
  176. // moves between |w1| and |w2|.
  177. EXPECT_FALSE(IsOverWindows(gfx::Point(100, 50)));
  178. }
  179. // Makes sure deleting a window hides.
  180. TEST_F(MultiWindowResizeControllerTest, DeleteWindow) {
  181. aura::test::TestWindowDelegate delegate1;
  182. std::unique_ptr<aura::Window> w1(CreateTestWindowInShellWithDelegate(
  183. &delegate1, -1, gfx::Rect(0, 0, 100, 100)));
  184. delegate1.set_window_component(HTRIGHT);
  185. aura::test::TestWindowDelegate delegate2;
  186. std::unique_ptr<aura::Window> w2(CreateTestWindowInShellWithDelegate(
  187. &delegate2, -2, gfx::Rect(100, 0, 100, 100)));
  188. delegate2.set_window_component(HTRIGHT);
  189. ui::test::EventGenerator* generator = GetEventGenerator();
  190. generator->MoveMouseTo(w1->bounds().CenterPoint());
  191. EXPECT_TRUE(HasPendingShow());
  192. EXPECT_TRUE(IsShowing());
  193. // Force a show now.
  194. ShowNow();
  195. EXPECT_FALSE(HasPendingShow());
  196. EXPECT_TRUE(IsShowing());
  197. // Move the mouse over the resize widget.
  198. ASSERT_TRUE(resize_widget());
  199. gfx::Rect bounds(resize_widget()->GetWindowBoundsInScreen());
  200. generator->MoveMouseTo(bounds.x() + 1, bounds.y() + 1);
  201. EXPECT_FALSE(HasPendingShow());
  202. EXPECT_TRUE(IsShowing());
  203. // Move the resize widget
  204. generator->PressLeftButton();
  205. generator->MoveMouseTo(bounds.x() + 10, bounds.y() + 10);
  206. // Delete w2.
  207. w2.reset();
  208. EXPECT_FALSE(resize_widget());
  209. EXPECT_FALSE(HasPendingShow());
  210. EXPECT_FALSE(IsShowing());
  211. EXPECT_FALSE(HasTarget(w1.get()));
  212. }
  213. // Tests resizing.
  214. TEST_F(MultiWindowResizeControllerTest, Drag) {
  215. aura::test::TestWindowDelegate delegate1;
  216. std::unique_ptr<aura::Window> w1(CreateTestWindowInShellWithDelegate(
  217. &delegate1, -1, gfx::Rect(0, 0, 100, 100)));
  218. delegate1.set_window_component(HTRIGHT);
  219. aura::test::TestWindowDelegate delegate2;
  220. std::unique_ptr<aura::Window> w2(CreateTestWindowInShellWithDelegate(
  221. &delegate2, -2, gfx::Rect(100, 0, 100, 100)));
  222. delegate2.set_window_component(HTRIGHT);
  223. ui::test::EventGenerator* generator = GetEventGenerator();
  224. generator->MoveMouseTo(w1->bounds().CenterPoint());
  225. EXPECT_TRUE(HasPendingShow());
  226. EXPECT_TRUE(IsShowing());
  227. // Force a show now.
  228. ShowNow();
  229. EXPECT_FALSE(HasPendingShow());
  230. EXPECT_TRUE(IsShowing());
  231. // Move the mouse over the resize widget.
  232. ASSERT_TRUE(resize_widget());
  233. gfx::Rect bounds(resize_widget()->GetWindowBoundsInScreen());
  234. generator->MoveMouseTo(bounds.x() + 1, bounds.y() + 1);
  235. EXPECT_FALSE(HasPendingShow());
  236. EXPECT_TRUE(IsShowing());
  237. // Move the resize widget
  238. generator->PressLeftButton();
  239. generator->MoveMouseTo(bounds.x() + 11, bounds.y() + 10);
  240. generator->ReleaseLeftButton();
  241. EXPECT_TRUE(resize_widget());
  242. EXPECT_FALSE(HasPendingShow());
  243. EXPECT_TRUE(IsShowing());
  244. EXPECT_EQ(gfx::Rect(0, 0, 110, 100), w1->bounds());
  245. EXPECT_EQ(gfx::Rect(110, 0, 100, 100), w2->bounds());
  246. // It should be possible to move 1px.
  247. bounds = resize_widget()->GetWindowBoundsInScreen();
  248. generator->MoveMouseTo(bounds.x() + 1, bounds.y() + 1);
  249. generator->PressLeftButton();
  250. generator->MoveMouseBy(1, 0);
  251. generator->ReleaseLeftButton();
  252. EXPECT_EQ(gfx::Rect(0, 0, 111, 100), w1->bounds());
  253. EXPECT_EQ(gfx::Rect(111, 0, 100, 100), w2->bounds());
  254. }
  255. // Makes sure three windows are picked up.
  256. TEST_F(MultiWindowResizeControllerTest, Three) {
  257. aura::test::TestWindowDelegate delegate1;
  258. std::unique_ptr<aura::Window> w1(CreateTestWindowInShellWithDelegate(
  259. &delegate1, -1, gfx::Rect(0, 0, 100, 100)));
  260. delegate1.set_window_component(HTRIGHT);
  261. aura::test::TestWindowDelegate delegate2;
  262. std::unique_ptr<aura::Window> w2(CreateTestWindowInShellWithDelegate(
  263. &delegate2, -2, gfx::Rect(100, 0, 100, 100)));
  264. delegate2.set_window_component(HTRIGHT);
  265. aura::test::TestWindowDelegate delegate3;
  266. std::unique_ptr<aura::Window> w3(CreateTestWindowInShellWithDelegate(
  267. &delegate3, -3, gfx::Rect(200, 0, 100, 100)));
  268. delegate3.set_window_component(HTRIGHT);
  269. ui::test::EventGenerator* generator = GetEventGenerator();
  270. generator->MoveMouseTo(w1->bounds().CenterPoint());
  271. EXPECT_TRUE(HasPendingShow());
  272. EXPECT_TRUE(IsShowing());
  273. EXPECT_FALSE(HasTarget(w3.get()));
  274. ShowNow();
  275. EXPECT_FALSE(HasPendingShow());
  276. EXPECT_TRUE(IsShowing());
  277. // w3 should be picked up when resize is started.
  278. gfx::Rect bounds(resize_widget()->GetWindowBoundsInScreen());
  279. generator->MoveMouseTo(bounds.x() + 1, bounds.y() + 1);
  280. generator->PressLeftButton();
  281. // Test that when drag starts, drag details are created for each window.
  282. EXPECT_TRUE(WindowState::Get(w1.get())->is_dragged());
  283. EXPECT_TRUE(WindowState::Get(w2.get())->is_dragged());
  284. EXPECT_TRUE(WindowState::Get(w3.get())->is_dragged());
  285. // Test the window components for each window.
  286. EXPECT_EQ(WindowState::Get(w1.get())->drag_details()->window_component,
  287. HTRIGHT);
  288. EXPECT_EQ(WindowState::Get(w2.get())->drag_details()->window_component,
  289. HTLEFT);
  290. EXPECT_EQ(WindowState::Get(w3.get())->drag_details()->window_component,
  291. HTLEFT);
  292. generator->MoveMouseTo(bounds.x() + 11, bounds.y() + 10);
  293. // Drag details should exist during dragging.
  294. EXPECT_TRUE(WindowState::Get(w1.get())->is_dragged());
  295. EXPECT_TRUE(WindowState::Get(w2.get())->is_dragged());
  296. EXPECT_TRUE(WindowState::Get(w3.get())->is_dragged());
  297. EXPECT_TRUE(HasTarget(w3.get()));
  298. // Release the mouse. The resizer should still be visible and a subsequent
  299. // press should not trigger a DCHECK.
  300. generator->ReleaseLeftButton();
  301. EXPECT_TRUE(IsShowing());
  302. // Test that drag details are correctly deleted after dragging.
  303. EXPECT_FALSE(WindowState::Get(w1.get())->is_dragged());
  304. EXPECT_FALSE(WindowState::Get(w2.get())->is_dragged());
  305. EXPECT_FALSE(WindowState::Get(w3.get())->is_dragged());
  306. generator->PressLeftButton();
  307. }
  308. // Tests that clicking outside of the resize handle dismisses it.
  309. TEST_F(MultiWindowResizeControllerTest, ClickOutside) {
  310. aura::test::TestWindowDelegate delegate1;
  311. std::unique_ptr<aura::Window> w1(CreateTestWindowInShellWithDelegate(
  312. &delegate1, -1, gfx::Rect(0, 0, 100, 100)));
  313. delegate1.set_window_component(HTRIGHT);
  314. aura::test::TestWindowDelegate delegate2;
  315. std::unique_ptr<aura::Window> w2(CreateTestWindowInShellWithDelegate(
  316. &delegate2, -2, gfx::Rect(100, 0, 100, 100)));
  317. delegate2.set_window_component(HTLEFT);
  318. ui::test::EventGenerator* generator = GetEventGenerator();
  319. gfx::Point w1_center_in_screen = w1->GetBoundsInScreen().CenterPoint();
  320. generator->MoveMouseTo(w1_center_in_screen);
  321. EXPECT_TRUE(HasPendingShow());
  322. EXPECT_TRUE(IsShowing());
  323. ShowNow();
  324. EXPECT_TRUE(IsShowing());
  325. gfx::Rect resize_widget_bounds_in_screen =
  326. resize_widget()->GetWindowBoundsInScreen();
  327. // Clicking on the resize handle should not do anything.
  328. generator->MoveMouseTo(resize_widget_bounds_in_screen.CenterPoint());
  329. generator->ClickLeftButton();
  330. EXPECT_TRUE(IsShowing());
  331. // Clicking outside the resize handle should immediately hide the resize
  332. // handle.
  333. EXPECT_FALSE(resize_widget_bounds_in_screen.Contains(w1_center_in_screen));
  334. generator->MoveMouseTo(w1_center_in_screen);
  335. generator->ClickLeftButton();
  336. EXPECT_FALSE(IsShowing());
  337. }
  338. // Tests that if the resized window is maximized/fullscreen/minimized, the
  339. // resizer widget should be dismissed.
  340. TEST_F(MultiWindowResizeControllerTest, WindowStateChange) {
  341. aura::test::TestWindowDelegate delegate1;
  342. std::unique_ptr<aura::Window> w1(CreateTestWindowInShellWithDelegate(
  343. &delegate1, -1, gfx::Rect(0, 0, 100, 100)));
  344. delegate1.set_window_component(HTRIGHT);
  345. aura::test::TestWindowDelegate delegate2;
  346. std::unique_ptr<aura::Window> w2(CreateTestWindowInShellWithDelegate(
  347. &delegate2, -2, gfx::Rect(100, 0, 100, 100)));
  348. delegate2.set_window_component(HTLEFT);
  349. ui::test::EventGenerator* generator = GetEventGenerator();
  350. gfx::Point w1_center_in_screen = w1->GetBoundsInScreen().CenterPoint();
  351. generator->MoveMouseTo(w1_center_in_screen);
  352. ShowNow();
  353. EXPECT_TRUE(IsShowing());
  354. // Maxmize one window should dismiss the resizer.
  355. w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
  356. EXPECT_FALSE(IsShowing());
  357. w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
  358. generator->MoveMouseTo(w1_center_in_screen);
  359. ShowNow();
  360. EXPECT_TRUE(IsShowing());
  361. // Entering Fullscreen should dismiss the resizer.
  362. w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
  363. EXPECT_FALSE(IsShowing());
  364. w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
  365. generator->MoveMouseTo(w1_center_in_screen);
  366. ShowNow();
  367. EXPECT_TRUE(IsShowing());
  368. // Minimize one window should dimiss the resizer.
  369. w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
  370. EXPECT_FALSE(IsShowing());
  371. w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
  372. generator->MoveMouseTo(w1_center_in_screen);
  373. ShowNow();
  374. EXPECT_TRUE(IsShowing());
  375. // When entering tablet mode, the windows will be maximized, thus the resizer
  376. // widget should be dismissed.
  377. Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
  378. EXPECT_FALSE(IsShowing());
  379. }
  380. // Tests that if one of the resized windows visibility changes to hidden, the
  381. // resize widget should be dismissed.
  382. TEST_F(MultiWindowResizeControllerTest, HideWindowTest) {
  383. aura::test::TestWindowDelegate delegate1;
  384. std::unique_ptr<aura::Window> w1(CreateTestWindowInShellWithDelegate(
  385. &delegate1, -1, gfx::Rect(0, 0, 100, 100)));
  386. delegate1.set_window_component(HTRIGHT);
  387. auto child_of_w1 = ChildTestWindowBuilder(w1.get()).Build();
  388. aura::test::TestWindowDelegate delegate2;
  389. std::unique_ptr<aura::Window> w2(CreateTestWindowInShellWithDelegate(
  390. &delegate2, -2, gfx::Rect(100, 0, 100, 100)));
  391. delegate2.set_window_component(HTLEFT);
  392. ui::test::EventGenerator* generator = GetEventGenerator();
  393. gfx::Point w1_center_in_screen = w1->GetBoundsInScreen().CenterPoint();
  394. generator->MoveMouseTo(w1_center_in_screen);
  395. ShowNow();
  396. EXPECT_TRUE(IsShowing());
  397. // Hiding child window shouldn't dismiss the resizer.
  398. child_of_w1->Hide();
  399. EXPECT_TRUE(IsShowing());
  400. // Hide one window should dimiss the resizer.
  401. w1->Hide();
  402. EXPECT_FALSE(IsShowing());
  403. }
  404. // Tests that the resizer does not appear while the mouse resides in a
  405. // non-resizeable window.
  406. TEST_F(MultiWindowResizeControllerTest, NonResizeableWindowTestA) {
  407. aura::test::TestWindowDelegate delegate1;
  408. std::unique_ptr<aura::Window> w1(CreateTestWindowInShellWithDelegate(
  409. &delegate1, -1, gfx::Rect(0, 0, 100, 100)));
  410. delegate1.set_window_component(HTRIGHT);
  411. aura::test::TestWindowDelegate delegate2;
  412. std::unique_ptr<aura::Window> w2(CreateTestWindowInShellWithDelegate(
  413. &delegate2, -2, gfx::Rect(0, 0, 100, 100)));
  414. w2->SetProperty(aura::client::kResizeBehaviorKey, 0);
  415. delegate2.set_window_component(HTRIGHT);
  416. aura::test::TestWindowDelegate delegate3;
  417. std::unique_ptr<aura::Window> w3(CreateTestWindowInShellWithDelegate(
  418. &delegate3, -3, gfx::Rect(100, 0, 100, 100)));
  419. delegate3.set_window_component(HTRIGHT);
  420. ui::test::EventGenerator* generator = GetEventGenerator();
  421. generator->MoveMouseTo(w1->bounds().CenterPoint());
  422. EXPECT_FALSE(HasPendingShow());
  423. }
  424. // Tests that the resizer does not appear while the mouse resides in a window
  425. // bordering two other windows, one of which is non-resizeable and obscures the
  426. // other.
  427. TEST_F(MultiWindowResizeControllerTest, NonResizeableWindowTestB) {
  428. aura::test::TestWindowDelegate delegate1;
  429. std::unique_ptr<aura::Window> w1(CreateTestWindowInShellWithDelegate(
  430. &delegate1, -1, gfx::Rect(0, 0, 100, 100)));
  431. delegate1.set_window_component(HTRIGHT);
  432. aura::test::TestWindowDelegate delegate2;
  433. std::unique_ptr<aura::Window> w2(CreateTestWindowInShellWithDelegate(
  434. &delegate2, -2, gfx::Rect(100, 0, 100, 100)));
  435. delegate2.set_window_component(HTRIGHT);
  436. aura::test::TestWindowDelegate delegate3;
  437. std::unique_ptr<aura::Window> w3(CreateTestWindowInShellWithDelegate(
  438. &delegate3, -3, gfx::Rect(100, 0, 100, 100)));
  439. w3->SetProperty(aura::client::kResizeBehaviorKey, 0);
  440. delegate3.set_window_component(HTRIGHT);
  441. ui::test::EventGenerator* generator = GetEventGenerator();
  442. generator->MoveMouseTo(w1->bounds().CenterPoint());
  443. EXPECT_FALSE(HasPendingShow());
  444. }
  445. // Tests that the resizer appears while the mouse resides in a window bordering
  446. // two other windows, one of which is non-resizeable but obscured by the other.
  447. TEST_F(MultiWindowResizeControllerTest, NonResizeableWindowTestC) {
  448. aura::test::TestWindowDelegate delegate1;
  449. std::unique_ptr<aura::Window> w1(CreateTestWindowInShellWithDelegate(
  450. &delegate1, -1, gfx::Rect(0, 0, 100, 100)));
  451. delegate1.set_window_component(HTRIGHT);
  452. aura::test::TestWindowDelegate delegate2;
  453. std::unique_ptr<aura::Window> w2(CreateTestWindowInShellWithDelegate(
  454. &delegate2, -2, gfx::Rect(100, 0, 100, 100)));
  455. w2->SetProperty(aura::client::kResizeBehaviorKey, 0);
  456. delegate2.set_window_component(HTRIGHT);
  457. aura::test::TestWindowDelegate delegate3;
  458. std::unique_ptr<aura::Window> w3(CreateTestWindowInShellWithDelegate(
  459. &delegate3, -3, gfx::Rect(100, 0, 100, 100)));
  460. delegate3.set_window_component(HTRIGHT);
  461. ui::test::EventGenerator* generator = GetEventGenerator();
  462. generator->MoveMouseTo(w1->bounds().CenterPoint());
  463. EXPECT_TRUE(HasPendingShow());
  464. EXPECT_FALSE(HasTarget(w2.get()));
  465. }
  466. // Tests that the resizer is dismissed when one of the resized windows becomes
  467. // non-resizeable.
  468. TEST_F(MultiWindowResizeControllerTest, MakeWindowNonResizeable) {
  469. aura::test::TestWindowDelegate delegate1;
  470. std::unique_ptr<aura::Window> w1(CreateTestWindowInShellWithDelegate(
  471. &delegate1, -1, gfx::Rect(0, 0, 100, 100)));
  472. delegate1.set_window_component(HTRIGHT);
  473. aura::test::TestWindowDelegate delegate2;
  474. std::unique_ptr<aura::Window> w2(CreateTestWindowInShellWithDelegate(
  475. &delegate2, -2, gfx::Rect(100, 0, 100, 100)));
  476. delegate2.set_window_component(HTLEFT);
  477. ui::test::EventGenerator* generator = GetEventGenerator();
  478. gfx::Point w1_center_in_screen = w1->GetBoundsInScreen().CenterPoint();
  479. generator->MoveMouseTo(w1_center_in_screen);
  480. ShowNow();
  481. EXPECT_TRUE(IsShowing());
  482. // Making one window non-resizeable should dismiss the resizer.
  483. w1->SetProperty(aura::client::kResizeBehaviorKey, 0);
  484. EXPECT_FALSE(IsShowing());
  485. }
  486. namespace {
  487. class TestWindowStateDelegate : public WindowStateDelegate {
  488. public:
  489. TestWindowStateDelegate() = default;
  490. TestWindowStateDelegate(const TestWindowStateDelegate&) = delete;
  491. TestWindowStateDelegate& operator=(const TestWindowStateDelegate&) = delete;
  492. ~TestWindowStateDelegate() override = default;
  493. // WindowStateDelegate:
  494. std::unique_ptr<PresentationTimeRecorder> OnDragStarted(
  495. int component) override {
  496. component_ = component;
  497. return nullptr;
  498. }
  499. void OnDragFinished(bool cancel, const gfx::PointF& location) override {
  500. location_ = location;
  501. }
  502. int GetComponentAndReset() {
  503. int result = component_;
  504. component_ = -1;
  505. return result;
  506. }
  507. gfx::PointF GetLocationAndReset() {
  508. gfx::PointF p = location_;
  509. location_.SetPoint(0, 0);
  510. return p;
  511. }
  512. private:
  513. gfx::PointF location_;
  514. int component_ = -1;
  515. };
  516. } // namespace
  517. // Tests dragging to resize two snapped windows.
  518. TEST_F(MultiWindowResizeControllerTest, TwoSnappedWindows) {
  519. UpdateDisplay("400x300");
  520. const int bottom_inset = 300 - ShelfConfig::Get()->shelf_size();
  521. // Create two snapped windows, one left snapped, one right snapped.
  522. aura::test::TestWindowDelegate delegate1;
  523. std::unique_ptr<aura::Window> w1(CreateTestWindowInShellWithDelegate(
  524. &delegate1, -1, gfx::Rect(100, 100, 100, 100)));
  525. delegate1.set_window_component(HTRIGHT);
  526. WindowState* w1_state = WindowState::Get(w1.get());
  527. const WindowSnapWMEvent snap_left(WM_EVENT_SNAP_PRIMARY);
  528. w1_state->OnWMEvent(&snap_left);
  529. EXPECT_EQ(WindowStateType::kPrimarySnapped, w1_state->GetStateType());
  530. aura::test::TestWindowDelegate delegate2;
  531. std::unique_ptr<aura::Window> w2(CreateTestWindowInShellWithDelegate(
  532. &delegate2, -2, gfx::Rect(100, 100, 100, 100)));
  533. delegate2.set_window_component(HTRIGHT);
  534. WindowState* w2_state = WindowState::Get(w2.get());
  535. const WindowSnapWMEvent snap_right(WM_EVENT_SNAP_SECONDARY);
  536. w2_state->OnWMEvent(&snap_right);
  537. EXPECT_EQ(WindowStateType::kSecondarySnapped, w2_state->GetStateType());
  538. EXPECT_EQ(0.5f, *w1_state->snap_ratio());
  539. EXPECT_EQ(0.5f, *w2_state->snap_ratio());
  540. ui::test::EventGenerator* generator = GetEventGenerator();
  541. generator->MoveMouseTo(w1->bounds().CenterPoint());
  542. EXPECT_TRUE(HasPendingShow());
  543. EXPECT_TRUE(IsShowing());
  544. // Force a show now.
  545. ShowNow();
  546. EXPECT_FALSE(HasPendingShow());
  547. EXPECT_TRUE(IsShowing());
  548. // Setup delegates
  549. auto* window_state_delegate1 = new TestWindowStateDelegate();
  550. w1_state->SetDelegate(base::WrapUnique(window_state_delegate1));
  551. // Move the mouse over the resize widget.
  552. ASSERT_TRUE(resize_widget());
  553. gfx::Rect bounds(resize_widget()->GetWindowBoundsInScreen());
  554. gfx::Point resize_widget_center = bounds.CenterPoint();
  555. generator->MoveMouseTo(resize_widget_center);
  556. EXPECT_FALSE(HasPendingShow());
  557. EXPECT_TRUE(IsShowing());
  558. // Move the resize widget.
  559. generator->PressLeftButton();
  560. generator->MoveMouseTo(resize_widget_center.x() + 100,
  561. resize_widget_center.y());
  562. generator->ReleaseLeftButton();
  563. // Check snapped states and bounds.
  564. EXPECT_EQ(WindowStateType::kPrimarySnapped, w1_state->GetStateType());
  565. EXPECT_EQ(gfx::Rect(0, 0, 300, bottom_inset), w1->bounds());
  566. EXPECT_EQ(WindowStateType::kSecondarySnapped, w2_state->GetStateType());
  567. EXPECT_EQ(gfx::Rect(300, 0, 100, bottom_inset), w2->bounds());
  568. EXPECT_EQ(0.75f, *w1_state->snap_ratio());
  569. EXPECT_EQ(0.25f, *w2_state->snap_ratio());
  570. // Dragging should call the WindowStateDelegate.
  571. EXPECT_EQ(HTRIGHT, window_state_delegate1->GetComponentAndReset());
  572. EXPECT_EQ(gfx::PointF(300, resize_widget_center.y()),
  573. window_state_delegate1->GetLocationAndReset());
  574. }
  575. TEST_F(MultiWindowResizeControllerTest, HiddenInOverview) {
  576. // Create two windows side by side, but not overlapping horizontally. Note
  577. // that when creating a window, the window is slightly larger than the given
  578. // bounds so position |window2| accordingly.
  579. auto window1 = CreateAppWindow(gfx::Rect(0, 0, 100, 100));
  580. auto window2 = CreateAppWindow(gfx::Rect(104, 0, 100, 100));
  581. // Move the mouse to the middle of the two windows. The multi window resizer
  582. // should appear.
  583. GetEventGenerator()->MoveMouseTo(gfx::Point(104, 50));
  584. EXPECT_TRUE(HasPendingShow());
  585. EXPECT_TRUE(IsShowing());
  586. // Tests that after starting overview, the widget is hidden.
  587. EnterOverview();
  588. EXPECT_FALSE(HasPendingShow());
  589. EXPECT_FALSE(IsShowing());
  590. }
  591. } // namespace ash