pip_window_resizer_unittest.cc 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919
  1. // Copyright 2018 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/pip/pip_window_resizer.h"
  5. #include <memory>
  6. #include <string>
  7. #include <tuple>
  8. #include <utility>
  9. #include "ash/keyboard/ui/keyboard_ui_controller.h"
  10. #include "ash/keyboard/ui/test/keyboard_test_util.h"
  11. #include "ash/metrics/pip_uma.h"
  12. #include "ash/public/cpp/keyboard/keyboard_switches.h"
  13. #include "ash/shelf/shelf.h"
  14. #include "ash/shell.h"
  15. #include "ash/test/ash_test_base.h"
  16. #include "ash/wm/pip/pip_positioner.h"
  17. #include "ash/wm/pip/pip_test_utils.h"
  18. #include "ash/wm/tablet_mode/tablet_mode_controller.h"
  19. #include "ash/wm/window_state.h"
  20. #include "ash/wm/wm_event.h"
  21. #include "ash/wm/work_area_insets.h"
  22. #include "base/callback_helpers.h"
  23. #include "base/test/metrics/histogram_tester.h"
  24. #include "ui/aura/client/aura_constants.h"
  25. #include "ui/aura/window.h"
  26. #include "ui/base/hit_test.h"
  27. #include "ui/compositor/layer.h"
  28. #include "ui/display/scoped_display_for_new_windows.h"
  29. #include "ui/gfx/geometry/insets.h"
  30. #include "ui/views/widget/widget.h"
  31. #include "ui/wm/core/coordinate_conversion.h"
  32. namespace ash {
  33. namespace {
  34. using ::chromeos::WindowStateType;
  35. // WindowState based on a given initial state. Records the last resize bounds.
  36. class FakeWindowState : public WindowState::State {
  37. public:
  38. explicit FakeWindowState(WindowStateType initial_state_type)
  39. : state_type_(initial_state_type) {}
  40. FakeWindowState(const FakeWindowState&) = delete;
  41. FakeWindowState& operator=(const FakeWindowState&) = delete;
  42. ~FakeWindowState() override = default;
  43. // WindowState::State overrides:
  44. void OnWMEvent(WindowState* window_state, const WMEvent* event) override {
  45. if (event->IsBoundsEvent()) {
  46. if (event->type() == WM_EVENT_SET_BOUNDS) {
  47. const auto* set_bounds_event =
  48. static_cast<const SetBoundsWMEvent*>(event);
  49. last_bounds_ = set_bounds_event->requested_bounds();
  50. last_window_state_ = window_state;
  51. }
  52. }
  53. }
  54. WindowStateType GetType() const override { return state_type_; }
  55. void AttachState(WindowState* window_state,
  56. WindowState::State* previous_state) override {}
  57. void DetachState(WindowState* window_state) override {}
  58. const gfx::Rect& last_bounds() const { return last_bounds_; }
  59. WindowState* last_window_state() { return last_window_state_; }
  60. private:
  61. WindowStateType state_type_;
  62. gfx::Rect last_bounds_;
  63. WindowState* last_window_state_ = nullptr;
  64. };
  65. } // namespace
  66. using Sample = base::HistogramBase::Sample;
  67. class PipWindowResizerTest : public AshTestBase,
  68. public ::testing::WithParamInterface<
  69. std::tuple<std::string, std::size_t>> {
  70. public:
  71. PipWindowResizerTest() = default;
  72. PipWindowResizerTest(const PipWindowResizerTest&) = delete;
  73. PipWindowResizerTest& operator=(const PipWindowResizerTest&) = delete;
  74. ~PipWindowResizerTest() override = default;
  75. void SetUp() override {
  76. AshTestBase::SetUp();
  77. SetVirtualKeyboardEnabled(true);
  78. const std::string& display_string = std::get<0>(GetParam());
  79. const std::size_t root_window_index = std::get<1>(GetParam());
  80. UpdateWorkArea(display_string);
  81. ASSERT_LT(root_window_index, Shell::GetAllRootWindows().size());
  82. scoped_display_ = std::make_unique<display::ScopedDisplayForNewWindows>(
  83. Shell::GetAllRootWindows()[root_window_index]);
  84. ForceHideShelvesForTest();
  85. }
  86. void TearDown() override {
  87. scoped_display_.reset();
  88. SetVirtualKeyboardEnabled(false);
  89. AshTestBase::TearDown();
  90. }
  91. protected:
  92. views::Widget* widget() { return widget_.get(); }
  93. aura::Window* window() { return window_; }
  94. FakeWindowState* test_state() { return test_state_; }
  95. base::HistogramTester& histograms() { return histograms_; }
  96. std::unique_ptr<views::Widget> CreateWidgetForTest(const gfx::Rect& bounds) {
  97. auto* root_window = Shell::GetRootWindowForNewWindows();
  98. gfx::Rect screen_bounds = bounds;
  99. ::wm::ConvertRectToScreen(root_window, &screen_bounds);
  100. std::unique_ptr<views::Widget> widget(new views::Widget);
  101. views::Widget::InitParams params;
  102. params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  103. params.bounds = screen_bounds;
  104. params.z_order = ui::ZOrderLevel::kFloatingWindow;
  105. params.context = root_window;
  106. widget->Init(std::move(params));
  107. widget->Show();
  108. return widget;
  109. }
  110. PipWindowResizer* CreateResizerForTest(int window_component) {
  111. return CreateResizerForTest(window_component, window(),
  112. window()->bounds().CenterPoint());
  113. }
  114. PipWindowResizer* CreateResizerForTest(int window_component,
  115. const gfx::Point& point_in_parent) {
  116. return CreateResizerForTest(window_component, window(), point_in_parent);
  117. }
  118. PipWindowResizer* CreateResizerForTest(int window_component,
  119. aura::Window* window,
  120. const gfx::Point& point_in_parent) {
  121. WindowState* window_state = WindowState::Get(window);
  122. window_state->CreateDragDetails(gfx::PointF(point_in_parent),
  123. window_component,
  124. ::wm::WINDOW_MOVE_SOURCE_MOUSE);
  125. return new PipWindowResizer(window_state);
  126. }
  127. gfx::PointF CalculateDragPoint(const WindowResizer& resizer,
  128. int delta_x,
  129. int delta_y) const {
  130. gfx::PointF location = resizer.GetInitialLocation();
  131. location.set_x(location.x() + delta_x);
  132. location.set_y(location.y() + delta_y);
  133. return location;
  134. }
  135. void Fling(std::unique_ptr<WindowResizer> resizer,
  136. float velocity_x,
  137. float velocity_y) {
  138. aura::Window* target_window = resizer->GetTarget();
  139. base::TimeTicks timestamp = base::TimeTicks::Now();
  140. ui::GestureEventDetails details = ui::GestureEventDetails(
  141. ui::ET_SCROLL_FLING_START, velocity_x, velocity_y);
  142. ui::GestureEvent event = ui::GestureEvent(
  143. target_window->bounds().origin().x(),
  144. target_window->bounds().origin().y(), ui::EF_NONE, timestamp, details);
  145. ui::Event::DispatcherApi(&event).set_target(target_window);
  146. resizer->FlingOrSwipe(&event);
  147. }
  148. void PreparePipWindow(const gfx::Rect& bounds) {
  149. widget_ = CreateWidgetForTest(bounds);
  150. window_ = widget_->GetNativeWindow();
  151. test_state_ = new FakeWindowState(WindowStateType::kPip);
  152. WindowState::Get(window_)->SetStateObject(
  153. std::unique_ptr<WindowState::State>(test_state_));
  154. }
  155. private:
  156. std::unique_ptr<views::Widget> widget_;
  157. aura::Window* window_;
  158. FakeWindowState* test_state_;
  159. base::HistogramTester histograms_;
  160. std::unique_ptr<display::ScopedDisplayForNewWindows> scoped_display_;
  161. void UpdateWorkArea(const std::string& bounds) {
  162. UpdateDisplay(bounds);
  163. for (aura::Window* root : Shell::GetAllRootWindows()) {
  164. WorkAreaInsets::ForWindow(root)->UpdateWorkAreaInsetsForTest(
  165. root, gfx::Rect(), gfx::Insets(), gfx::Insets());
  166. }
  167. }
  168. };
  169. TEST_P(PipWindowResizerTest, PipWindowCanDrag) {
  170. PreparePipWindow(gfx::Rect(200, 200, 100, 100));
  171. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  172. ASSERT_TRUE(resizer.get());
  173. resizer->Drag(CalculateDragPoint(*resizer, 0, 10), 0);
  174. EXPECT_EQ(gfx::Rect(200, 210, 100, 100), test_state()->last_bounds());
  175. }
  176. TEST_P(PipWindowResizerTest, PipWindowCanResize) {
  177. PreparePipWindow(gfx::Rect(200, 200, 100, 100));
  178. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTBOTTOM));
  179. ASSERT_TRUE(resizer.get());
  180. resizer->Drag(CalculateDragPoint(*resizer, 0, 10), 0);
  181. EXPECT_EQ(gfx::Rect(200, 200, 100, 110), test_state()->last_bounds());
  182. }
  183. TEST_P(PipWindowResizerTest, PipWindowDragIsRestrictedToWorkArea) {
  184. PreparePipWindow(gfx::Rect(200, 200, 100, 100));
  185. // Specify point in parent as center so the drag point does not leave the
  186. // display. If the drag point is not in any display bounds, it causes the
  187. // window to be moved to the default display.
  188. auto landscape =
  189. display::Screen::GetScreen()->GetPrimaryDisplay().is_landscape();
  190. int right_x = landscape ? 392 : 292;
  191. int bottom_y = landscape ? 292 : 392;
  192. std::unique_ptr<PipWindowResizer> resizer(
  193. CreateResizerForTest(HTCAPTION, gfx::Point(250, 250)));
  194. ASSERT_TRUE(resizer.get());
  195. // Drag to the right.
  196. resizer->Drag(CalculateDragPoint(*resizer, 250, 0), 0);
  197. EXPECT_EQ(gfx::Rect(right_x, 200, 100, 100), test_state()->last_bounds());
  198. // Drag down.
  199. resizer->Drag(CalculateDragPoint(*resizer, 0, 250), 0);
  200. EXPECT_EQ(gfx::Rect(200, bottom_y, 100, 100), test_state()->last_bounds());
  201. // Drag to the left.
  202. resizer->Drag(CalculateDragPoint(*resizer, -250, 0), 0);
  203. EXPECT_EQ(gfx::Rect(8, 200, 100, 100), test_state()->last_bounds());
  204. // Drag up.
  205. resizer->Drag(CalculateDragPoint(*resizer, 0, -250), 0);
  206. EXPECT_EQ(gfx::Rect(200, 8, 100, 100), test_state()->last_bounds());
  207. }
  208. TEST_P(PipWindowResizerTest, PipWindowCanBeDraggedInTabletMode) {
  209. Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
  210. PreparePipWindow(gfx::Rect(200, 200, 100, 100));
  211. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  212. ASSERT_TRUE(resizer.get());
  213. resizer->Drag(CalculateDragPoint(*resizer, 0, 10), 0);
  214. EXPECT_EQ(gfx::Rect(200, 210, 100, 100), test_state()->last_bounds());
  215. }
  216. TEST_P(PipWindowResizerTest, PipWindowCanBeResizedInTabletMode) {
  217. Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
  218. PreparePipWindow(gfx::Rect(200, 200, 100, 100));
  219. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTBOTTOM));
  220. ASSERT_TRUE(resizer.get());
  221. resizer->Drag(CalculateDragPoint(*resizer, 0, 10), 0);
  222. EXPECT_EQ(gfx::Rect(200, 200, 100, 110), test_state()->last_bounds());
  223. }
  224. TEST_P(PipWindowResizerTest, ResizingPipWindowDoesNotTriggerFling) {
  225. PreparePipWindow(gfx::Rect(8, 8, 100, 100));
  226. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTBOTTOM));
  227. ASSERT_TRUE(resizer.get());
  228. Fling(std::move(resizer), 0.f, 4000.f);
  229. // Ensure that the PIP window isn't flung to the bottom edge during resize.
  230. EXPECT_EQ(gfx::Point(8, 8), test_state()->last_bounds().origin());
  231. }
  232. TEST_P(PipWindowResizerTest, PipWindowCanBeSwipeDismissed) {
  233. PreparePipWindow(gfx::Rect(8, 8, 100, 100));
  234. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  235. ASSERT_TRUE(resizer.get());
  236. // Drag to the left.
  237. resizer->Drag(CalculateDragPoint(*resizer, -100, 0), 0);
  238. // Should be dismissed when the drag completes.
  239. resizer->CompleteDrag();
  240. EXPECT_TRUE(widget()->IsClosed());
  241. }
  242. TEST_P(PipWindowResizerTest, PipWindowPartiallySwipedDoesNotDismiss) {
  243. PreparePipWindow(gfx::Rect(8, 8, 100, 100));
  244. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  245. ASSERT_TRUE(resizer.get());
  246. // Drag to the left, but only a little bit.
  247. resizer->Drag(CalculateDragPoint(*resizer, -30, 0), 0);
  248. // Should not be dismissed when the drag completes.
  249. resizer->CompleteDrag();
  250. EXPECT_FALSE(widget()->IsClosed());
  251. EXPECT_EQ(gfx::Rect(8, 8, 100, 100), test_state()->last_bounds());
  252. }
  253. TEST_P(PipWindowResizerTest, PipWindowInSwipeToDismissGestureLocksToAxis) {
  254. PreparePipWindow(gfx::Rect(8, 8, 100, 100));
  255. std::unique_ptr<PipWindowResizer> resizer(
  256. CreateResizerForTest(HTCAPTION, gfx::Point(50, 50)));
  257. ASSERT_TRUE(resizer.get());
  258. // Drag to the left, but only a little bit, to start a swipe-to-dismiss.
  259. resizer->Drag(CalculateDragPoint(*resizer, -30, 0), 0);
  260. EXPECT_EQ(gfx::Rect(-22, 8, 100, 100), test_state()->last_bounds());
  261. // Now try to drag down, it should be locked to the horizontal axis.
  262. resizer->Drag(CalculateDragPoint(*resizer, -30, 30), 0);
  263. EXPECT_EQ(gfx::Rect(-22, 8, 100, 100), test_state()->last_bounds());
  264. }
  265. TEST_P(PipWindowResizerTest,
  266. PipWindowMovedAwayFromScreenEdgeNoLongerCanSwipeToDismiss) {
  267. PreparePipWindow(gfx::Rect(8, 16, 100, 100));
  268. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  269. ASSERT_TRUE(resizer.get());
  270. // Drag to the right and up a bit.
  271. resizer->Drag(CalculateDragPoint(*resizer, 30, -8), 0);
  272. EXPECT_EQ(gfx::Rect(38, 8, 100, 100), test_state()->last_bounds());
  273. // Now try to drag to the left start a swipe-to-dismiss. It should stop
  274. // at the edge of the work area.
  275. resizer->Drag(CalculateDragPoint(*resizer, -30, -8), 0);
  276. EXPECT_EQ(gfx::Rect(8, 8, 100, 100), test_state()->last_bounds());
  277. }
  278. TEST_P(PipWindowResizerTest, PipWindowAtCornerLocksToOneAxisOnSwipeToDismiss) {
  279. PreparePipWindow(gfx::Rect(8, 8, 100, 100));
  280. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  281. ASSERT_TRUE(resizer.get());
  282. // Try dragging up and to the left. It should lock onto the axis with the
  283. // largest displacement.
  284. resizer->Drag(CalculateDragPoint(*resizer, -30, -40), 0);
  285. EXPECT_EQ(gfx::Rect(8, -32, 100, 100), test_state()->last_bounds());
  286. }
  287. TEST_P(
  288. PipWindowResizerTest,
  289. PipWindowMustBeDraggedMostlyInDirectionOfDismissToInitiateSwipeToDismiss) {
  290. PreparePipWindow(gfx::Rect(8, 8, 100, 100));
  291. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  292. ASSERT_TRUE(resizer.get());
  293. // Try a lot downward and a bit to the left. Swiping should not be initiated.
  294. resizer->Drag(CalculateDragPoint(*resizer, -30, 50), 0);
  295. EXPECT_EQ(gfx::Rect(8, 58, 100, 100), test_state()->last_bounds());
  296. }
  297. TEST_P(PipWindowResizerTest,
  298. PipWindowDoesNotMoveUntilStatusOfSwipeToDismissGestureIsKnown) {
  299. PreparePipWindow(gfx::Rect(8, 8, 100, 100));
  300. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  301. ASSERT_TRUE(resizer.get());
  302. // Move a small amount - this should not trigger any bounds change, since
  303. // we don't know whether a swipe will start or not.
  304. resizer->Drag(CalculateDragPoint(*resizer, -4, 0), 0);
  305. EXPECT_TRUE(test_state()->last_bounds().IsEmpty());
  306. }
  307. TEST_P(PipWindowResizerTest, PipWindowIsFlungToEdge) {
  308. PreparePipWindow(gfx::Rect(200, 200, 100, 100));
  309. auto landscape =
  310. display::Screen::GetScreen()->GetPrimaryDisplay().is_landscape();
  311. {
  312. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  313. ASSERT_TRUE(resizer.get());
  314. resizer->Drag(CalculateDragPoint(*resizer, 0, 10), 0);
  315. Fling(std::move(resizer), 0.f, 4000.f);
  316. auto origin = landscape ? gfx::Point(200, 292) : gfx::Point(200, 392);
  317. // Flung downwards.
  318. EXPECT_EQ(origin, test_state()->last_bounds().origin());
  319. }
  320. {
  321. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  322. ASSERT_TRUE(resizer.get());
  323. resizer->Drag(CalculateDragPoint(*resizer, 0, -10), 0);
  324. Fling(std::move(resizer), 0.f, -4000.f);
  325. // Flung upwards.
  326. EXPECT_EQ(gfx::Rect(200, 8, 100, 100), test_state()->last_bounds());
  327. }
  328. {
  329. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  330. ASSERT_TRUE(resizer.get());
  331. resizer->Drag(CalculateDragPoint(*resizer, 10, 0), 0);
  332. Fling(std::move(resizer), 4000.f, 0.f);
  333. auto origin = landscape ? gfx::Point(392, 200) : gfx::Point(292, 200);
  334. // Flung to the right.
  335. EXPECT_EQ(origin, test_state()->last_bounds().origin());
  336. }
  337. {
  338. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  339. ASSERT_TRUE(resizer.get());
  340. resizer->Drag(CalculateDragPoint(*resizer, -10, 0), 0);
  341. Fling(std::move(resizer), -4000.f, 0.f);
  342. // Flung to the left.
  343. EXPECT_EQ(gfx::Rect(8, 200, 100, 100), test_state()->last_bounds());
  344. }
  345. }
  346. TEST_P(PipWindowResizerTest, PipWindowIsFlungDiagonally) {
  347. PreparePipWindow(gfx::Rect(200, 200, 100, 100));
  348. auto landscape =
  349. display::Screen::GetScreen()->GetPrimaryDisplay().is_landscape();
  350. {
  351. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  352. ASSERT_TRUE(resizer.get());
  353. resizer->Drag(CalculateDragPoint(*resizer, 10, 10), 0);
  354. Fling(std::move(resizer), 3000.f, 3000.f);
  355. // Flung downward and to the right, into the corner.
  356. EXPECT_EQ(gfx::Rect(292, 292, 100, 100), test_state()->last_bounds());
  357. }
  358. {
  359. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  360. ASSERT_TRUE(resizer.get());
  361. resizer->Drag(CalculateDragPoint(*resizer, 3, 4), 0);
  362. Fling(std::move(resizer), 3000.f, 4000.f);
  363. gfx::Point origin = landscape ? gfx::Point(269, 292) : gfx::Point(292, 322);
  364. // Flung downward and to the right, but reaching the bottom edge first.
  365. EXPECT_EQ(origin, test_state()->last_bounds().origin());
  366. }
  367. {
  368. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  369. ASSERT_TRUE(resizer.get());
  370. resizer->Drag(CalculateDragPoint(*resizer, 4, 3), 0);
  371. Fling(std::move(resizer), 4000.f, 3000.f);
  372. gfx::Point origin = landscape ? gfx::Point(322, 292) : gfx::Point(292, 269);
  373. // Flung downward and to the right, but reaching the right edge first.
  374. EXPECT_EQ(origin, test_state()->last_bounds().origin());
  375. }
  376. {
  377. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  378. ASSERT_TRUE(resizer.get());
  379. resizer->Drag(CalculateDragPoint(*resizer, -3, -4), 0);
  380. Fling(std::move(resizer), -3000.f, -4000.f);
  381. // Flung upward and to the left, but reaching the top edge first.
  382. EXPECT_EQ(gfx::Point(56, 8), test_state()->last_bounds().origin());
  383. }
  384. {
  385. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  386. ASSERT_TRUE(resizer.get());
  387. resizer->Drag(CalculateDragPoint(*resizer, -4, -3), 0);
  388. Fling(std::move(resizer), -4000.f, -3000.f);
  389. // Flung upward and to the left, but reaching the left edge first.
  390. EXPECT_EQ(gfx::Rect(8, 56, 100, 100), test_state()->last_bounds());
  391. }
  392. {
  393. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  394. ASSERT_TRUE(resizer.get());
  395. resizer->Drag(CalculateDragPoint(*resizer, 3, -9), 0);
  396. Fling(std::move(resizer), 3000.f, -9000.f);
  397. // Flung upward and to the right, but reaching the top edge first.
  398. EXPECT_EQ(gfx::Rect(264, 8, 100, 100), test_state()->last_bounds());
  399. }
  400. {
  401. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  402. ASSERT_TRUE(resizer.get());
  403. resizer->Drag(CalculateDragPoint(*resizer, 3, -3), 0);
  404. Fling(std::move(resizer), 3000.f, -3000.f);
  405. gfx::Point origin = landscape ? gfx::Point(392, 8) : gfx::Point(292, 108);
  406. // Flung upward and to the right, but reaching the right edge first.
  407. EXPECT_EQ(origin, test_state()->last_bounds().origin());
  408. }
  409. {
  410. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  411. ASSERT_TRUE(resizer.get());
  412. resizer->Drag(CalculateDragPoint(*resizer, -3, 3), 0);
  413. Fling(std::move(resizer), -3000.f, 3000.f);
  414. gfx::Point origin = landscape ? gfx::Point(108, 292) : gfx::Point(8, 392);
  415. // Flung downward and to the left, but reaching the bottom edge first.
  416. EXPECT_EQ(origin, test_state()->last_bounds().origin());
  417. }
  418. {
  419. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  420. ASSERT_TRUE(resizer.get());
  421. resizer->Drag(CalculateDragPoint(*resizer, -9, 3), 0);
  422. Fling(std::move(resizer), -9000.f, 3000.f);
  423. // Flung downward and to the left, but reaching the left edge first.
  424. EXPECT_EQ(gfx::Rect(8, 264, 100, 100), test_state()->last_bounds());
  425. }
  426. }
  427. TEST_P(PipWindowResizerTest, PipWindowFlungAvoidsFloatingKeyboard) {
  428. PreparePipWindow(gfx::Rect(200, 200, 75, 75));
  429. auto* keyboard_controller = keyboard::KeyboardUIController::Get();
  430. keyboard_controller->SetContainerType(keyboard::ContainerType::kFloating,
  431. gfx::Rect(0, 0, 1, 1),
  432. base::DoNothing());
  433. const display::Display display = WindowState::Get(window())->GetDisplay();
  434. keyboard_controller->ShowKeyboardInDisplay(display);
  435. ASSERT_TRUE(keyboard::WaitUntilShown());
  436. aura::Window* keyboard_window = keyboard_controller->GetKeyboardWindow();
  437. keyboard_window->SetBounds(gfx::Rect(8, 150, 100, 100));
  438. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  439. ASSERT_TRUE(resizer.get());
  440. // Fling to the left - but don't intersect with the floating keyboard.
  441. resizer->Drag(CalculateDragPoint(*resizer, -10, 0), 0);
  442. Fling(std::move(resizer), -4000.f, 0.f);
  443. // Appear below the keyboard.
  444. EXPECT_EQ(gfx::Rect(8, 258, 75, 75), test_state()->last_bounds());
  445. }
  446. TEST_P(PipWindowResizerTest, PipWindowDoesNotChangeDisplayOnDrag) {
  447. PreparePipWindow(gfx::Rect(200, 200, 100, 100));
  448. const display::Display display = WindowState::Get(window())->GetDisplay();
  449. gfx::Rect rect_in_screen = window()->bounds();
  450. ::wm::ConvertRectToScreen(window()->parent(), &rect_in_screen);
  451. EXPECT_TRUE(display.bounds().Contains(rect_in_screen));
  452. // Drag inside the display.
  453. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  454. ASSERT_TRUE(resizer.get());
  455. resizer->Drag(CalculateDragPoint(*resizer, 10, 10), 0);
  456. // Ensure the position is still in the display.
  457. EXPECT_EQ(gfx::Rect(210, 210, 100, 100), test_state()->last_bounds());
  458. EXPECT_EQ(display.id(), test_state()->last_window_state()->GetDisplay().id());
  459. rect_in_screen = window()->bounds();
  460. ::wm::ConvertRectToScreen(window()->parent(), &rect_in_screen);
  461. EXPECT_TRUE(display.bounds().Contains(rect_in_screen));
  462. }
  463. TEST_P(PipWindowResizerTest, PipRestoreBoundsSetOnFling) {
  464. PreparePipWindow(gfx::Rect(200, 200, 100, 100));
  465. {
  466. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  467. ASSERT_TRUE(resizer.get());
  468. resizer->Drag(CalculateDragPoint(*resizer, 10, 10), 0);
  469. Fling(std::move(resizer), 3000.f, 3000.f);
  470. }
  471. WindowState* window_state = WindowState::Get(window());
  472. EXPECT_TRUE(PipPositioner::HasSnapFraction(window_state));
  473. }
  474. TEST_P(PipWindowResizerTest, PipStartAndFinishFreeResizeUmaMetrics) {
  475. PreparePipWindow(gfx::Rect(200, 200, 100, 100));
  476. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTBOTTOM));
  477. ASSERT_TRUE(resizer.get());
  478. EXPECT_EQ(1, histograms().GetBucketCount(kAshPipEventsHistogramName,
  479. Sample(AshPipEvents::FREE_RESIZE)));
  480. histograms().ExpectTotalCount(kAshPipEventsHistogramName, 1);
  481. resizer->Drag(CalculateDragPoint(*resizer, 100, 0), 0);
  482. resizer->CompleteDrag();
  483. histograms().ExpectTotalCount(kAshPipEventsHistogramName, 1);
  484. }
  485. TEST_P(PipWindowResizerTest, PipFreeResizeAreaUmaMetrics) {
  486. PreparePipWindow(gfx::Rect(200, 200, 100, 100));
  487. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTBOTTOM));
  488. ASSERT_TRUE(resizer.get());
  489. EXPECT_EQ(1, histograms().GetBucketCount(
  490. kAshPipFreeResizeInitialAreaHistogramName, Sample(5)));
  491. histograms().ExpectTotalCount(kAshPipFreeResizeInitialAreaHistogramName, 1);
  492. window()->layer()->SetBounds(gfx::Rect(200, 200, 100, 190));
  493. resizer->CompleteDrag();
  494. EXPECT_EQ(1, histograms().GetBucketCount(
  495. kAshPipFreeResizeFinishAreaHistogramName, Sample(10)));
  496. histograms().ExpectTotalCount(kAshPipFreeResizeFinishAreaHistogramName, 1);
  497. }
  498. TEST_P(PipWindowResizerTest, DragDetailsAreDestroyed) {
  499. PreparePipWindow(gfx::Rect(200, 200, 100, 100));
  500. WindowState* window_state = WindowState::Get(window());
  501. {
  502. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  503. ASSERT_TRUE(resizer.get());
  504. resizer->Drag(CalculateDragPoint(*resizer, 0, 10), 0);
  505. EXPECT_NE(nullptr, window_state->drag_details());
  506. resizer->CompleteDrag();
  507. EXPECT_NE(nullptr, window_state->drag_details());
  508. }
  509. EXPECT_EQ(nullptr, window_state->drag_details());
  510. }
  511. // TODO: UpdateDisplay() doesn't support different layouts of multiple displays.
  512. // We should add some way to try multiple layouts.
  513. INSTANTIATE_TEST_SUITE_P(All,
  514. PipWindowResizerTest,
  515. testing::Values(std::make_tuple("500x400", 0u),
  516. std::make_tuple("500x400/r", 0u),
  517. std::make_tuple("500x400/u", 0u),
  518. std::make_tuple("500x400/l", 0u),
  519. std::make_tuple("1000x800*2", 0u),
  520. std::make_tuple("500x400,500x400", 0u),
  521. std::make_tuple("500x400,500x400",
  522. 1u)));
  523. using PipWindowResizerNonSquareAspectRatioTest = PipWindowResizerTest;
  524. TEST_P(PipWindowResizerNonSquareAspectRatioTest, PipPositionUmaMetrics) {
  525. histograms().ExpectTotalCount(kAshPipPositionHistogramName, 0);
  526. {
  527. // Check TOP_LEFT.
  528. PreparePipWindow(gfx::Rect(0, 0, 100, 100));
  529. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  530. ASSERT_TRUE(resizer.get());
  531. resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0);
  532. resizer->CompleteDrag();
  533. EXPECT_EQ(1, histograms().GetBucketCount(kAshPipPositionHistogramName,
  534. Sample(AshPipPosition::TOP_LEFT)));
  535. histograms().ExpectTotalCount(kAshPipPositionHistogramName, 1);
  536. }
  537. {
  538. // Check TOP_MIDDLE.
  539. PreparePipWindow(gfx::Rect(100, 0, 100, 100));
  540. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  541. ASSERT_TRUE(resizer.get());
  542. resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0);
  543. resizer->CompleteDrag();
  544. EXPECT_EQ(1,
  545. histograms().GetBucketCount(kAshPipPositionHistogramName,
  546. Sample(AshPipPosition::TOP_MIDDLE)));
  547. histograms().ExpectTotalCount(kAshPipPositionHistogramName, 2);
  548. }
  549. {
  550. // Check TOP_RIGHT.
  551. PreparePipWindow(gfx::Rect(250, 0, 100, 100));
  552. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  553. ASSERT_TRUE(resizer.get());
  554. resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0);
  555. resizer->CompleteDrag();
  556. EXPECT_EQ(1,
  557. histograms().GetBucketCount(kAshPipPositionHistogramName,
  558. Sample(AshPipPosition::TOP_RIGHT)));
  559. histograms().ExpectTotalCount(kAshPipPositionHistogramName, 3);
  560. }
  561. {
  562. // Check MIDDLE_LEFT.
  563. PreparePipWindow(gfx::Rect(0, 100, 100, 100));
  564. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  565. ASSERT_TRUE(resizer.get());
  566. resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0);
  567. resizer->CompleteDrag();
  568. EXPECT_EQ(1,
  569. histograms().GetBucketCount(kAshPipPositionHistogramName,
  570. Sample(AshPipPosition::MIDDLE_LEFT)));
  571. histograms().ExpectTotalCount(kAshPipPositionHistogramName, 4);
  572. }
  573. {
  574. // Check MIDDLE.
  575. PreparePipWindow(gfx::Rect(100, 100, 100, 100));
  576. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  577. ASSERT_TRUE(resizer.get());
  578. resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0);
  579. resizer->CompleteDrag();
  580. EXPECT_EQ(1, histograms().GetBucketCount(kAshPipPositionHistogramName,
  581. Sample(AshPipPosition::MIDDLE)));
  582. histograms().ExpectTotalCount(kAshPipPositionHistogramName, 5);
  583. }
  584. {
  585. // Check MIDDLE_RIGHT.
  586. PreparePipWindow(gfx::Rect(250, 100, 100, 100));
  587. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  588. ASSERT_TRUE(resizer.get());
  589. resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0);
  590. resizer->CompleteDrag();
  591. EXPECT_EQ(
  592. 1, histograms().GetBucketCount(kAshPipPositionHistogramName,
  593. Sample(AshPipPosition::MIDDLE_RIGHT)));
  594. histograms().ExpectTotalCount(kAshPipPositionHistogramName, 6);
  595. }
  596. {
  597. // Check BOTTOM_LEFT.
  598. PreparePipWindow(gfx::Rect(0, 250, 100, 100));
  599. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  600. ASSERT_TRUE(resizer.get());
  601. resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0);
  602. resizer->CompleteDrag();
  603. EXPECT_EQ(1,
  604. histograms().GetBucketCount(kAshPipPositionHistogramName,
  605. Sample(AshPipPosition::BOTTOM_LEFT)));
  606. histograms().ExpectTotalCount(kAshPipPositionHistogramName, 7);
  607. }
  608. {
  609. // Check BOTTOM_MIDDLE.
  610. PreparePipWindow(gfx::Rect(100, 250, 100, 100));
  611. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  612. ASSERT_TRUE(resizer.get());
  613. resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0);
  614. resizer->CompleteDrag();
  615. EXPECT_EQ(
  616. 1, histograms().GetBucketCount(kAshPipPositionHistogramName,
  617. Sample(AshPipPosition::BOTTOM_MIDDLE)));
  618. histograms().ExpectTotalCount(kAshPipPositionHistogramName, 8);
  619. }
  620. {
  621. // Check BOTTOM_RIGHT.
  622. PreparePipWindow(gfx::Rect(250, 250, 100, 100));
  623. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  624. ASSERT_TRUE(resizer.get());
  625. resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0);
  626. resizer->CompleteDrag();
  627. EXPECT_EQ(
  628. 1, histograms().GetBucketCount(kAshPipPositionHistogramName,
  629. Sample(AshPipPosition::BOTTOM_RIGHT)));
  630. histograms().ExpectTotalCount(kAshPipPositionHistogramName, 9);
  631. }
  632. }
  633. TEST_P(PipWindowResizerNonSquareAspectRatioTest,
  634. PipPositionUmaMetricsCornerPriority) {
  635. histograms().ExpectTotalCount(kAshPipPositionHistogramName, 0);
  636. // Check corners are priotised over edges and middle.
  637. {
  638. // Check TOP_LEFT.
  639. PreparePipWindow(gfx::Rect(0, 0, 300, 210));
  640. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  641. ASSERT_TRUE(resizer.get());
  642. resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0);
  643. resizer->CompleteDrag();
  644. EXPECT_EQ(1, histograms().GetBucketCount(kAshPipPositionHistogramName,
  645. Sample(AshPipPosition::TOP_LEFT)));
  646. histograms().ExpectTotalCount(kAshPipPositionHistogramName, 1);
  647. }
  648. {
  649. // Check TOP_RIGHT.
  650. PreparePipWindow(gfx::Rect(100, 0, 300, 210));
  651. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  652. ASSERT_TRUE(resizer.get());
  653. resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0);
  654. resizer->CompleteDrag();
  655. EXPECT_EQ(1,
  656. histograms().GetBucketCount(kAshPipPositionHistogramName,
  657. Sample(AshPipPosition::TOP_RIGHT)));
  658. histograms().ExpectTotalCount(kAshPipPositionHistogramName, 2);
  659. }
  660. {
  661. // Check BOTTOM_LEFT.
  662. PreparePipWindow(gfx::Rect(0, 190, 300, 210));
  663. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  664. ASSERT_TRUE(resizer.get());
  665. resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0);
  666. resizer->CompleteDrag();
  667. EXPECT_EQ(1,
  668. histograms().GetBucketCount(kAshPipPositionHistogramName,
  669. Sample(AshPipPosition::BOTTOM_LEFT)));
  670. histograms().ExpectTotalCount(kAshPipPositionHistogramName, 3);
  671. }
  672. {
  673. // Check BOTTOM_RIGHT.
  674. PreparePipWindow(gfx::Rect(100, 190, 300, 210));
  675. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  676. ASSERT_TRUE(resizer.get());
  677. resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0);
  678. resizer->CompleteDrag();
  679. EXPECT_EQ(
  680. 1, histograms().GetBucketCount(kAshPipPositionHistogramName,
  681. Sample(AshPipPosition::BOTTOM_RIGHT)));
  682. histograms().ExpectTotalCount(kAshPipPositionHistogramName, 4);
  683. }
  684. }
  685. TEST_P(PipWindowResizerNonSquareAspectRatioTest,
  686. PipPositionUmaMetricsEdgePriority) {
  687. histograms().ExpectTotalCount(kAshPipPositionHistogramName, 0);
  688. // Test that edges are prioritised over middle.
  689. {
  690. // Check TOP_MIDDLE.
  691. PreparePipWindow(gfx::Rect(100, 0, 200, 220));
  692. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  693. ASSERT_TRUE(resizer.get());
  694. resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0);
  695. resizer->CompleteDrag();
  696. EXPECT_EQ(1,
  697. histograms().GetBucketCount(kAshPipPositionHistogramName,
  698. Sample(AshPipPosition::TOP_MIDDLE)));
  699. histograms().ExpectTotalCount(kAshPipPositionHistogramName, 1);
  700. }
  701. {
  702. // Check BOTTOM_MIDDLE.
  703. PreparePipWindow(gfx::Rect(100, 80, 200, 220));
  704. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  705. ASSERT_TRUE(resizer.get());
  706. resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0);
  707. resizer->CompleteDrag();
  708. EXPECT_EQ(
  709. 1, histograms().GetBucketCount(kAshPipPositionHistogramName,
  710. Sample(AshPipPosition::BOTTOM_MIDDLE)));
  711. histograms().ExpectTotalCount(kAshPipPositionHistogramName, 2);
  712. }
  713. {
  714. // Check MIDDLE_LEFT.
  715. PreparePipWindow(gfx::Rect(0, 90, 300, 120));
  716. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  717. ASSERT_TRUE(resizer.get());
  718. resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0);
  719. resizer->CompleteDrag();
  720. EXPECT_EQ(1,
  721. histograms().GetBucketCount(kAshPipPositionHistogramName,
  722. Sample(AshPipPosition::MIDDLE_LEFT)));
  723. histograms().ExpectTotalCount(kAshPipPositionHistogramName, 3);
  724. }
  725. {
  726. // Check MIDDLE_RIGHT.
  727. PreparePipWindow(gfx::Rect(100, 90, 300, 120));
  728. std::unique_ptr<PipWindowResizer> resizer(CreateResizerForTest(HTCAPTION));
  729. ASSERT_TRUE(resizer.get());
  730. resizer->Drag(CalculateDragPoint(*resizer, 0, 0), 0);
  731. resizer->CompleteDrag();
  732. EXPECT_EQ(
  733. 1, histograms().GetBucketCount(kAshPipPositionHistogramName,
  734. Sample(AshPipPosition::MIDDLE_RIGHT)));
  735. histograms().ExpectTotalCount(kAshPipPositionHistogramName, 4);
  736. }
  737. }
  738. INSTANTIATE_TEST_SUITE_P(
  739. All,
  740. PipWindowResizerNonSquareAspectRatioTest,
  741. testing::Values(std::make_tuple("400x300", 0u),
  742. std::make_tuple("400x300,4000x3000", 0u),
  743. std::make_tuple("4000x3000,400x300", 1u)));
  744. } // namespace ash