scrollable_shelf_view_unittest.cc 68 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621
  1. // Copyright (c) 2019 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/shelf/scrollable_shelf_view.h"
  5. #include "ash/app_list/app_list_controller_impl.h"
  6. #include "ash/app_list/app_list_presenter_impl.h"
  7. #include "ash/app_list/views/app_list_view.h"
  8. #include "ash/constants/ash_features.h"
  9. #include "ash/drag_drop/drag_image_view.h"
  10. #include "ash/public/cpp/shelf_config.h"
  11. #include "ash/root_window_controller.h"
  12. #include "ash/shelf/scrollable_shelf_constants.h"
  13. #include "ash/shelf/shelf_app_button.h"
  14. #include "ash/shelf/shelf_test_util.h"
  15. #include "ash/shelf/shelf_tooltip_manager.h"
  16. #include "ash/shelf/shelf_view_test_api.h"
  17. #include "ash/shelf/shelf_widget.h"
  18. #include "ash/shelf/test/scrollable_shelf_test_base.h"
  19. #include "ash/shell.h"
  20. #include "ash/test/ash_test_base.h"
  21. #include "ash/test/ash_test_util.h"
  22. #include "ash/wm/overview/overview_controller.h"
  23. #include "ash/wm/tablet_mode/tablet_mode_controller.h"
  24. #include "base/i18n/rtl.h"
  25. #include "base/test/icu_test_util.h"
  26. #include "base/test/metrics/histogram_tester.h"
  27. #include "base/test/scoped_feature_list.h"
  28. #include "ui/compositor/scoped_animation_duration_scale_mode.h"
  29. #include "ui/display/manager/display_manager.h"
  30. #include "ui/events/event_utils.h"
  31. #include "ui/views/animation/ink_drop.h"
  32. namespace ash {
  33. namespace {
  34. // A helper class to override the current time.
  35. struct TimeOverrideHelper {
  36. static base::Time TimeNow() { return current_time; }
  37. // Used as the current time in ash pixel diff tests.
  38. static base::Time current_time;
  39. };
  40. base::Time TimeOverrideHelper::current_time;
  41. } // namespace
  42. class PageFlipWaiter : public ScrollableShelfView::TestObserver {
  43. public:
  44. explicit PageFlipWaiter(ScrollableShelfView* scrollable_shelf_view)
  45. : scrollable_shelf_view_(scrollable_shelf_view) {
  46. scrollable_shelf_view->SetTestObserver(this);
  47. }
  48. ~PageFlipWaiter() override {
  49. scrollable_shelf_view_->SetTestObserver(nullptr);
  50. }
  51. void Wait() {
  52. run_loop_ = std::make_unique<base::RunLoop>();
  53. run_loop_->Run();
  54. }
  55. private:
  56. void OnPageFlipTimerFired() override {
  57. DCHECK(run_loop_.get());
  58. run_loop_->Quit();
  59. }
  60. ScrollableShelfView* scrollable_shelf_view_ = nullptr;
  61. std::unique_ptr<base::RunLoop> run_loop_;
  62. };
  63. class InkDropAnimationWaiter : public views::InkDropObserver {
  64. public:
  65. explicit InkDropAnimationWaiter(views::Button* button) : button_(button) {
  66. views::InkDrop::Get(button)->GetInkDrop()->AddObserver(this);
  67. }
  68. ~InkDropAnimationWaiter() override {
  69. views::InkDrop::Get(button_)->GetInkDrop()->RemoveObserver(this);
  70. }
  71. void Wait() {
  72. run_loop_ = std::make_unique<base::RunLoop>();
  73. run_loop_->Run();
  74. }
  75. private:
  76. void InkDropAnimationStarted() override {}
  77. void InkDropRippleAnimationEnded(
  78. views::InkDropState ink_drop_state) override {
  79. if (ink_drop_state != views::InkDropState::ACTIVATED &&
  80. ink_drop_state != views::InkDropState::HIDDEN)
  81. return;
  82. if (run_loop_.get())
  83. run_loop_->Quit();
  84. }
  85. views::Button* button_ = nullptr;
  86. std::unique_ptr<base::RunLoop> run_loop_;
  87. };
  88. class TestShelfItemDelegate : public ShelfItemDelegate {
  89. public:
  90. explicit TestShelfItemDelegate(const ShelfID& shelf_id)
  91. : ShelfItemDelegate(shelf_id) {}
  92. // ShelfItemDelegate:
  93. void ItemSelected(std::unique_ptr<ui::Event> event,
  94. int64_t display_id,
  95. ShelfLaunchSource source,
  96. ItemSelectedCallback callback,
  97. const ItemFilterPredicate& filter_predicate) override {
  98. std::move(callback).Run(SHELF_ACTION_WINDOW_ACTIVATED, {});
  99. }
  100. void ExecuteCommand(bool from_context_menu,
  101. int64_t command_id,
  102. int32_t event_flags,
  103. int64_t display_id) override {}
  104. void Close() override {}
  105. };
  106. class ScrollableShelfViewTest : public ScrollableShelfTestBase {
  107. public:
  108. ScrollableShelfViewTest() = default;
  109. ~ScrollableShelfViewTest() override = default;
  110. protected:
  111. ShelfID AddAppShortcut(ShelfItemType item_type = TYPE_PINNED_APP) {
  112. return AddAppShortcutWithIconColor(item_type, SK_ColorRED);
  113. }
  114. // Verifies that a tappable app icon should have the correct button state when
  115. // it is under mouse hover.
  116. void VerifyTappableAppIconsUnderMouseHover() {
  117. // Ensure that the mouse does not hover any app icon.
  118. GetEventGenerator()->MoveMouseTo(
  119. GetPrimaryDisplay().bounds().CenterPoint());
  120. for (size_t i = scrollable_shelf_view_->first_tappable_app_index().value();
  121. i <= scrollable_shelf_view_->last_tappable_app_index().value(); ++i) {
  122. const auto* shelf_app_icon = test_api_->GetButton(i);
  123. const gfx::Rect app_icon_screen_bounds =
  124. shelf_app_icon->GetBoundsInScreen();
  125. const gfx::Point app_icon_screen_center(
  126. app_icon_screen_bounds.CenterPoint());
  127. // `shelf_app_icon` is not hovered.
  128. ASSERT_EQ(views::Button::STATE_NORMAL, shelf_app_icon->GetState());
  129. // Move the mouse to `app_icon_screen_center` and verify that
  130. // `shelf_app_icon` is under mouse hover.
  131. GetEventGenerator()->MoveMouseTo(app_icon_screen_center);
  132. EXPECT_EQ(views::Button::STATE_HOVERED, shelf_app_icon->GetState());
  133. }
  134. }
  135. void AddAppShortcutsUntilRightArrowIsShown() {
  136. ASSERT_FALSE(scrollable_shelf_view_->right_arrow()->GetVisible());
  137. while (!scrollable_shelf_view_->right_arrow()->GetVisible())
  138. AddAppShortcut();
  139. }
  140. void CheckFirstAndLastTappableIconsBounds() {
  141. views::ViewModel* view_model = shelf_view_->view_model_for_test();
  142. gfx::Rect visible_space_in_screen = scrollable_shelf_view_->visible_space();
  143. views::View::ConvertRectToScreen(scrollable_shelf_view_,
  144. &visible_space_in_screen);
  145. views::View* last_tappable_icon = view_model->view_at(
  146. scrollable_shelf_view_->last_tappable_app_index().value());
  147. const gfx::Rect last_tappable_icon_bounds =
  148. last_tappable_icon->GetBoundsInScreen();
  149. // Expects that the last tappable icon is fully shown.
  150. EXPECT_TRUE(visible_space_in_screen.Contains(last_tappable_icon_bounds));
  151. views::View* first_tappable_icon = view_model->view_at(
  152. scrollable_shelf_view_->first_tappable_app_index().value());
  153. const gfx::Rect first_tappable_icon_bounds =
  154. first_tappable_icon->GetBoundsInScreen();
  155. // Expects that the first tappable icon is fully shown.
  156. EXPECT_TRUE(visible_space_in_screen.Contains(first_tappable_icon_bounds));
  157. }
  158. void VeirifyRippleRingWithinShelfContainer(
  159. const ShelfAppButton& button) const {
  160. const gfx::Rect shelf_container_bounds_in_screen =
  161. scrollable_shelf_view_->shelf_container_view()->GetBoundsInScreen();
  162. const gfx::Rect small_ripple_area = button.CalculateSmallRippleArea();
  163. const gfx::Point ripple_center = small_ripple_area.CenterPoint();
  164. const int ripple_radius = small_ripple_area.width() / 2;
  165. // Calculate the ripple's left end and right end in screen.
  166. const int ripple_center_x_in_screen =
  167. ripple_center.x() + button.GetBoundsInScreen().x();
  168. const int ripple_x = ripple_center_x_in_screen - ripple_radius;
  169. const int ripple_right = ripple_center_x_in_screen + ripple_radius;
  170. // Verify that both ends are within bounds of shelf container view.
  171. EXPECT_GE(ripple_x, shelf_container_bounds_in_screen.x());
  172. EXPECT_LE(ripple_right, shelf_container_bounds_in_screen.right());
  173. }
  174. bool HasRoundedCornersOnAppButtonAfterMouseRightClick(
  175. ShelfAppButton* button) {
  176. const gfx::Point location_within_button =
  177. button->GetBoundsInScreen().CenterPoint();
  178. GetEventGenerator()->MoveMouseTo(location_within_button);
  179. GetEventGenerator()->ClickRightButton();
  180. ui::Layer* layer = scrollable_shelf_view_->shelf_container_view()->layer();
  181. // The gfx::RoundedCornersF object is considered empty when all of the
  182. // corners are squared (no effective radius).
  183. const bool has_rounded_corners = !(layer->rounded_corner_radii().IsEmpty());
  184. // Click outside of |button|. Expects that the rounded corners should always
  185. // be empty.
  186. GetEventGenerator()->GestureTapAt(
  187. button->GetBoundsInScreen().bottom_center());
  188. EXPECT_TRUE(layer->rounded_corner_radii().IsEmpty());
  189. return has_rounded_corners;
  190. }
  191. };
  192. // Tests scrollable shelf's features under both LTR and RTL.
  193. class ScrollableShelfViewRTLTest : public ScrollableShelfViewTest,
  194. public testing::WithParamInterface<bool> {
  195. public:
  196. ScrollableShelfViewRTLTest() : scoped_locale_(GetParam() ? "ar" : "") {}
  197. ~ScrollableShelfViewRTLTest() override = default;
  198. private:
  199. base::test::ScopedRestoreICUDefaultLocale scoped_locale_;
  200. };
  201. INSTANTIATE_TEST_SUITE_P(RTL, ScrollableShelfViewRTLTest, testing::Bool());
  202. // Verifies that the display rotation from the short side to the long side
  203. // should not break the scrollable shelf's UI
  204. // behavior(https://crbug.com/1000764).
  205. TEST_F(ScrollableShelfViewTest, CorrectUIAfterDisplayRotationShortToLong) {
  206. // Changes the display setting in order that the display's height is greater
  207. // than the width.
  208. UpdateDisplay("600x800");
  209. display::Display display = GetPrimaryDisplay();
  210. // Adds enough app icons so that after display rotation the scrollable
  211. // shelf is still in overflow mode.
  212. const int num = display.bounds().height() / shelf_view_->GetButtonSize();
  213. for (int i = 0; i < num; i++)
  214. AddAppShortcut();
  215. // Because the display's height is greater than the display's width,
  216. // the scrollable shelf is in overflow mode before display rotation.
  217. ASSERT_EQ(ScrollableShelfView::kShowRightArrowButton,
  218. scrollable_shelf_view_->layout_strategy_for_test());
  219. // Presses the right arrow until reaching the last page of shelf icons.
  220. const views::View* right_arrow = scrollable_shelf_view_->right_arrow();
  221. const gfx::Point center_point =
  222. right_arrow->GetBoundsInScreen().CenterPoint();
  223. while (right_arrow->GetVisible()) {
  224. GetEventGenerator()->MoveMouseTo(center_point);
  225. GetEventGenerator()->PressLeftButton();
  226. GetEventGenerator()->ReleaseLeftButton();
  227. }
  228. ASSERT_EQ(ScrollableShelfView::kShowLeftArrowButton,
  229. scrollable_shelf_view_->layout_strategy_for_test());
  230. // Rotates the display by 90 degrees.
  231. display::DisplayManager* display_manager = Shell::Get()->display_manager();
  232. display_manager->SetDisplayRotation(display.id(), display::Display::ROTATE_90,
  233. display::Display::RotationSource::ACTIVE);
  234. // After rotation, checks the following things:
  235. // (1) The scrollable shelf has the correct layout strategy.
  236. // (2) The last app icon has the correct bounds.
  237. // (3) The scrollable shelf does not need further adjustment.
  238. EXPECT_EQ(ScrollableShelfView::kShowLeftArrowButton,
  239. scrollable_shelf_view_->layout_strategy_for_test());
  240. views::ViewModel* view_model = shelf_view_->view_model_for_test();
  241. const views::View* last_visible_icon = view_model->view_at(
  242. scrollable_shelf_view_->last_tappable_app_index().value());
  243. const gfx::Rect icon_bounds = last_visible_icon->GetBoundsInScreen();
  244. gfx::Rect visible_space = scrollable_shelf_view_->visible_space();
  245. views::View::ConvertRectToScreen(scrollable_shelf_view_, &visible_space);
  246. EXPECT_EQ(icon_bounds.right() +
  247. ShelfConfig::Get()->scrollable_shelf_ripple_padding() +
  248. ShelfConfig::Get()->GetAppIconEndPadding(),
  249. visible_space.right());
  250. EXPECT_FALSE(scrollable_shelf_view_->ShouldAdjustForTest());
  251. }
  252. // Verifies that the display rotation from the long side to the short side
  253. // should not break the scrollable shelf's UI behavior
  254. // (https://crbug.com/1000764).
  255. TEST_P(ScrollableShelfViewRTLTest, CorrectUIAfterDisplayRotationLongToShort) {
  256. // Changes the display setting in order that the display's width is greater
  257. // than the height.
  258. UpdateDisplay("600x300");
  259. display::Display display = GetPrimaryDisplay();
  260. AddAppShortcutsUntilOverflow();
  261. // Presses the right arrow until reaching the last page of shelf icons.
  262. const views::View* right_arrow = scrollable_shelf_view_->right_arrow();
  263. const gfx::Point center_point =
  264. right_arrow->GetBoundsInScreen().CenterPoint();
  265. while (right_arrow->GetVisible()) {
  266. GetEventGenerator()->MoveMouseTo(center_point);
  267. GetEventGenerator()->PressLeftButton();
  268. GetEventGenerator()->ReleaseLeftButton();
  269. }
  270. ASSERT_EQ(ScrollableShelfView::kShowLeftArrowButton,
  271. scrollable_shelf_view_->layout_strategy_for_test());
  272. // Rotates the display by 90 degrees. In order to reproduce the bug,
  273. // both arrow buttons should show after rotation.
  274. display::DisplayManager* display_manager = Shell::Get()->display_manager();
  275. display_manager->SetDisplayRotation(display.id(), display::Display::ROTATE_90,
  276. display::Display::RotationSource::ACTIVE);
  277. ASSERT_EQ(ScrollableShelfView::kShowButtons,
  278. scrollable_shelf_view_->layout_strategy_for_test());
  279. // Verifies that the scrollable shelf does not need further adjustment.
  280. EXPECT_FALSE(scrollable_shelf_view_->ShouldAdjustForTest());
  281. }
  282. // Verifies that the mask layer gradient shader is not applied when no arrow
  283. // button shows.
  284. TEST_P(ScrollableShelfViewRTLTest, VerifyApplyMaskGradientShaderWhenNeeded) {
  285. AddAppShortcut();
  286. ASSERT_EQ(ScrollableShelfView::LayoutStrategy::kNotShowArrowButtons,
  287. scrollable_shelf_view_->layout_strategy_for_test());
  288. EXPECT_FALSE(scrollable_shelf_view_->layer()->layer_mask_layer());
  289. AddAppShortcutsUntilOverflow();
  290. ASSERT_EQ(ScrollableShelfView::LayoutStrategy::kShowRightArrowButton,
  291. scrollable_shelf_view_->layout_strategy_for_test());
  292. EXPECT_TRUE(scrollable_shelf_view_->layer()->layer_mask_layer());
  293. }
  294. // When hovering mouse on a shelf icon, the tooltip only shows for the visible
  295. // icon (see https://crbug.com/997807).
  296. TEST_P(ScrollableShelfViewRTLTest, NotShowTooltipForHiddenIcons) {
  297. AddAppShortcutsUntilOverflow();
  298. ASSERT_EQ(ScrollableShelfView::kShowRightArrowButton,
  299. scrollable_shelf_view_->layout_strategy_for_test());
  300. views::ViewModel* view_model = shelf_view_->view_model_for_test();
  301. // Check the initial state of |tooltip_manager|.
  302. ShelfTooltipManager* tooltip_manager = test_api_->tooltip_manager();
  303. EXPECT_FALSE(tooltip_manager->IsVisible());
  304. // Verifies that tooltip should show for a visible shelf item.
  305. views::View* visible_icon = view_model->view_at(
  306. scrollable_shelf_view_->first_tappable_app_index().value());
  307. GetEventGenerator()->MoveMouseTo(
  308. visible_icon->GetBoundsInScreen().CenterPoint());
  309. tooltip_manager->ShowTooltip(visible_icon);
  310. EXPECT_TRUE(tooltip_manager->IsVisible());
  311. // Reset |tooltip_manager|.
  312. GetEventGenerator()->MoveMouseTo(gfx::Point());
  313. tooltip_manager->Close();
  314. EXPECT_FALSE(tooltip_manager->IsVisible());
  315. // Verifies that tooltip should not show for a hidden shelf item.
  316. views::View* hidden_icon = view_model->view_at(
  317. scrollable_shelf_view_->last_tappable_app_index().value() + 1);
  318. GetEventGenerator()->MoveMouseTo(
  319. hidden_icon->GetBoundsInScreen().CenterPoint());
  320. tooltip_manager->ShowTooltip(hidden_icon);
  321. EXPECT_FALSE(tooltip_manager->IsVisible());
  322. }
  323. // Test that tapping near the scroll arrow button triggers scrolling. (see
  324. // https://crbug.com/1004998)
  325. TEST_P(ScrollableShelfViewRTLTest, ScrollAfterTappingNearScrollArrow) {
  326. AddAppShortcutsUntilOverflow();
  327. ASSERT_EQ(ScrollableShelfView::kShowRightArrowButton,
  328. scrollable_shelf_view_->layout_strategy_for_test());
  329. // Tap right arrow and check that the scrollable shelf now shows the left
  330. // arrow only. Then do the same for the left arrow.
  331. const gfx::Rect right_arrow =
  332. scrollable_shelf_view_->right_arrow()->GetBoundsInScreen();
  333. GetEventGenerator()->GestureTapAt(right_arrow.CenterPoint());
  334. ASSERT_EQ(ScrollableShelfView::kShowLeftArrowButton,
  335. scrollable_shelf_view_->layout_strategy_for_test());
  336. const gfx::Rect left_arrow =
  337. scrollable_shelf_view_->left_arrow()->GetBoundsInScreen();
  338. GetEventGenerator()->GestureTapAt(left_arrow.CenterPoint());
  339. ASSERT_EQ(ScrollableShelfView::kShowRightArrowButton,
  340. scrollable_shelf_view_->layout_strategy_for_test());
  341. // Recalculate the right arrow bounds considering the padding for the tap
  342. // area.
  343. const int horizontalPadding = (32 - right_arrow.width()) / 2;
  344. const int verticalPadding =
  345. (shelf_view_->GetButtonSize() - right_arrow.height()) / 2;
  346. // Tap near the right arrow and check that the scrollable shelf now shows the
  347. // left arrow only. Then do the same for the left arrow.
  348. GetEventGenerator()->GestureTapAt(
  349. gfx::Point(right_arrow.top_right().x() - horizontalPadding,
  350. right_arrow.top_right().y() + verticalPadding));
  351. ASSERT_EQ(ScrollableShelfView::kShowLeftArrowButton,
  352. scrollable_shelf_view_->layout_strategy_for_test());
  353. GetEventGenerator()->GestureTapAt(
  354. gfx::Point(left_arrow.top_right().x(), left_arrow.top_right().y()));
  355. EXPECT_EQ(ScrollableShelfView::kShowRightArrowButton,
  356. scrollable_shelf_view_->layout_strategy_for_test());
  357. }
  358. // Verifies that in overflow mode, the app icons indexed by
  359. // |first_tappable_app_index_| and |last_tappable_app_index_| are completely
  360. // shown (https://crbug.com/1013811).
  361. TEST_P(ScrollableShelfViewRTLTest, VerifyTappableAppIndices) {
  362. AddAppShortcutsUntilOverflow();
  363. // Checks bounds when the layout strategy is kShowRightArrowButton.
  364. ASSERT_EQ(ScrollableShelfView::kShowRightArrowButton,
  365. scrollable_shelf_view_->layout_strategy_for_test());
  366. CheckFirstAndLastTappableIconsBounds();
  367. // Pins enough apps to Shelf to ensure that layout strategy will be
  368. // kShowButtons after pressing the right arrow button.
  369. const int view_size =
  370. scrollable_shelf_view_->shelf_view()->view_model_for_test()->view_size();
  371. PopulateAppShortcut(view_size + 1);
  372. GetEventGenerator()->GestureTapAt(
  373. scrollable_shelf_view_->right_arrow()->GetBoundsInScreen().CenterPoint());
  374. // Checks bounds when the layout strategy is kShowButtons.
  375. ASSERT_EQ(ScrollableShelfView::kShowButtons,
  376. scrollable_shelf_view_->layout_strategy_for_test());
  377. CheckFirstAndLastTappableIconsBounds();
  378. GetEventGenerator()->GestureTapAt(
  379. scrollable_shelf_view_->right_arrow()->GetBoundsInScreen().CenterPoint());
  380. // Checks bounds when the layout strategy is kShowLeftArrowButton.
  381. ASSERT_EQ(ScrollableShelfView::kShowLeftArrowButton,
  382. scrollable_shelf_view_->layout_strategy_for_test());
  383. CheckFirstAndLastTappableIconsBounds();
  384. }
  385. TEST_P(ScrollableShelfViewRTLTest, ShowTooltipForArrowButtons) {
  386. AddAppShortcutsUntilOverflow();
  387. ASSERT_EQ(ScrollableShelfView::kShowRightArrowButton,
  388. scrollable_shelf_view_->layout_strategy_for_test());
  389. // Check the initial state of |tooltip_manager|.
  390. ShelfTooltipManager* tooltip_manager = test_api_->tooltip_manager();
  391. EXPECT_FALSE(tooltip_manager->IsVisible());
  392. for (ShelfAlignment alignment :
  393. {ShelfAlignment::kBottom, ShelfAlignment::kLeft,
  394. ShelfAlignment::kRight}) {
  395. SCOPED_TRACE(testing::Message() << "Testing shelf with alignment "
  396. << static_cast<int>(alignment));
  397. GetPrimaryShelf()->SetAlignment(alignment);
  398. // Verifies that tooltip should show for a visible shelf item.
  399. views::View* right_arrow = scrollable_shelf_view_->right_arrow();
  400. GetEventGenerator()->MoveMouseTo(
  401. right_arrow->GetBoundsInScreen().CenterPoint());
  402. tooltip_manager->ShowTooltip(right_arrow);
  403. EXPECT_TRUE(tooltip_manager->IsVisible());
  404. // Click right arrow button to scroll the shelf and show left arrow button.
  405. GetEventGenerator()->ClickLeftButton();
  406. ASSERT_EQ(ScrollableShelfView::kShowLeftArrowButton,
  407. scrollable_shelf_view_->layout_strategy_for_test());
  408. // Reset |tooltip_manager|.
  409. GetEventGenerator()->MoveMouseTo(gfx::Point());
  410. tooltip_manager->Close();
  411. EXPECT_FALSE(tooltip_manager->IsVisible());
  412. views::View* left_arrow = scrollable_shelf_view_->left_arrow();
  413. GetEventGenerator()->MoveMouseTo(
  414. left_arrow->GetBoundsInScreen().CenterPoint());
  415. tooltip_manager->ShowTooltip(left_arrow);
  416. EXPECT_TRUE(tooltip_manager->IsVisible());
  417. tooltip_manager->Close();
  418. EXPECT_FALSE(tooltip_manager->IsVisible());
  419. }
  420. }
  421. // Verifies that dragging an app icon to a new shelf page works well. In
  422. // addition, the dragged icon moves with mouse before mouse release (see
  423. // https://crbug.com/1031367).
  424. TEST_P(ScrollableShelfViewRTLTest, DragIconToNewPage) {
  425. scrollable_shelf_view_->set_page_flip_time_threshold(base::Milliseconds(10));
  426. AddAppShortcutsUntilOverflow();
  427. GetEventGenerator()->GestureTapAt(
  428. scrollable_shelf_view_->right_arrow()->GetBoundsInScreen().CenterPoint());
  429. ASSERT_EQ(ScrollableShelfView::kShowLeftArrowButton,
  430. scrollable_shelf_view_->layout_strategy_for_test());
  431. views::ViewModel* view_model = shelf_view_->view_model_for_test();
  432. views::View* dragged_view = view_model->view_at(
  433. scrollable_shelf_view_->last_tappable_app_index().value());
  434. const gfx::Point drag_start_point =
  435. dragged_view->GetBoundsInScreen().CenterPoint();
  436. // Ensures that the app icon is not dragged to the ideal bounds directly.
  437. // It helps to construct a more complex scenario that the animation
  438. // is created to move the dropped icon to the target place after drag release.
  439. const gfx::Point drag_end_point =
  440. scrollable_shelf_view_->left_arrow()->GetBoundsInScreen().origin() -
  441. gfx::Vector2d(10, 0);
  442. ASSERT_NE(0u, view_model->GetIndexOfView(dragged_view));
  443. // Drag |dragged_view| from |drag_start_point| to |drag_end_point|. Wait
  444. // for enough time before releasing the mouse button.
  445. GetEventGenerator()->MoveMouseTo(drag_start_point);
  446. GetEventGenerator()->PressLeftButton();
  447. GetEventGenerator()->MoveMouseTo(drag_end_point);
  448. {
  449. PageFlipWaiter waiter(scrollable_shelf_view_);
  450. waiter.Wait();
  451. }
  452. // Expects that the drag icon moves with drag pointer before mouse release.
  453. const gfx::Rect intermediate_bounds =
  454. shelf_view_->GetDragIconBoundsInScreenForTest();
  455. EXPECT_EQ(drag_end_point, intermediate_bounds.CenterPoint());
  456. GetEventGenerator()->ReleaseLeftButton();
  457. ASSERT_NE(intermediate_bounds.CenterPoint(),
  458. dragged_view->GetBoundsInScreen().CenterPoint());
  459. // Expects that the proxy icon is deleted after mouse release.
  460. EXPECT_EQ(gfx::Rect(), shelf_view_->GetDragIconBoundsInScreenForTest());
  461. // Verifies that:
  462. // (1) Scrollable shelf view has the expected layout strategy.
  463. // (2) The dragged view has the correct view index.
  464. EXPECT_EQ(ScrollableShelfView::kShowRightArrowButton,
  465. scrollable_shelf_view_->layout_strategy_for_test());
  466. const auto view_index = view_model->GetIndexOfView(dragged_view);
  467. EXPECT_GE(view_index,
  468. scrollable_shelf_view_->first_tappable_app_index().value());
  469. EXPECT_LE(view_index,
  470. scrollable_shelf_view_->last_tappable_app_index().value());
  471. }
  472. // Verifies that after adding the second display, shelf icons showing on
  473. // the primary display are also visible on the second display
  474. // (https://crbug.com/1035596).
  475. TEST_P(ScrollableShelfViewRTLTest, CheckTappableIndicesOnSecondDisplay) {
  476. constexpr size_t icon_number = 5;
  477. for (size_t i = 0; i < icon_number; i++)
  478. AddAppShortcut();
  479. // Adds the second display.
  480. UpdateDisplay("600x800,600x800");
  481. Shelf* secondary_shelf =
  482. Shell::GetRootWindowControllerWithDisplayId(GetSecondaryDisplay().id())
  483. ->shelf();
  484. ScrollableShelfView* secondary_scrollable_shelf_view =
  485. secondary_shelf->shelf_widget()
  486. ->hotseat_widget()
  487. ->scrollable_shelf_view();
  488. // Verifies that the all icons are visible on the secondary display.
  489. EXPECT_EQ(icon_number - 1,
  490. secondary_scrollable_shelf_view->last_tappable_app_index().value());
  491. EXPECT_EQ(
  492. 0u, secondary_scrollable_shelf_view->first_tappable_app_index().value());
  493. }
  494. // Verifies that the scrollable shelf in oveflow mode has the correct layout
  495. // after switching to tablet mode (https://crbug.com/1017979).
  496. TEST_P(ScrollableShelfViewRTLTest, CorrectUIAfterSwitchingToTablet) {
  497. // Add enough app shortcuts to ensure that at least three pages of icons show.
  498. for (int i = 0; i < 25; i++)
  499. AddAppShortcut();
  500. ASSERT_EQ(ScrollableShelfView::kShowRightArrowButton,
  501. scrollable_shelf_view_->layout_strategy_for_test());
  502. // Calculate the hotseat background's screen bounds.
  503. gfx::Rect hotseat_background =
  504. scrollable_shelf_view_->GetHotseatBackgroundBounds();
  505. views::View::ConvertRectToScreen(scrollable_shelf_view_, &hotseat_background);
  506. // Verify that the right arrow button has the correct end padding.
  507. const gfx::Rect right_arrow_bounds =
  508. scrollable_shelf_view_->right_arrow()->GetBoundsInScreen();
  509. if (base::i18n::IsRTL()) {
  510. EXPECT_EQ(scrollable_shelf_constants::kArrowButtonEndPadding,
  511. right_arrow_bounds.x() - hotseat_background.x());
  512. } else {
  513. EXPECT_EQ(scrollable_shelf_constants::kArrowButtonEndPadding,
  514. hotseat_background.right() - right_arrow_bounds.right());
  515. }
  516. GetEventGenerator()->GestureTapAt(
  517. scrollable_shelf_view_->right_arrow()->GetBoundsInScreen().CenterPoint());
  518. ASSERT_EQ(ScrollableShelfView::kShowButtons,
  519. scrollable_shelf_view_->layout_strategy_for_test());
  520. Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
  521. base::RunLoop().RunUntilIdle();
  522. views::ViewModel* view_model = shelf_view_->view_model_for_test();
  523. views::View* first_tappable_view = view_model->view_at(
  524. scrollable_shelf_view_->first_tappable_app_index().value());
  525. // Verifies that the gap between the left arrow button and the first tappable
  526. // icon is expected.
  527. const gfx::Rect left_arrow_bounds =
  528. scrollable_shelf_view_->left_arrow()->GetBoundsInScreen();
  529. // Activate a shelf icon's ink drop. Verify that no crash happens.
  530. auto* ink_drop = views::InkDrop::Get(test_api_->GetButton(0))->GetInkDrop();
  531. ink_drop->SnapToActivated();
  532. EXPECT_EQ(views::InkDropState::ACTIVATED, ink_drop->GetTargetInkDropState());
  533. if (base::i18n::IsRTL()) {
  534. EXPECT_EQ(left_arrow_bounds.x() -
  535. scrollable_shelf_constants::kDistanceToArrowButton,
  536. first_tappable_view->GetBoundsInScreen().right());
  537. } else {
  538. EXPECT_EQ(left_arrow_bounds.right() +
  539. scrollable_shelf_constants::kDistanceToArrowButton,
  540. first_tappable_view->GetBoundsInScreen().x());
  541. }
  542. VerifyTappableAppIconsUnderMouseHover();
  543. }
  544. // Verifies that the scrollable shelf without overflow has the correct layout in
  545. // tablet mode.
  546. TEST_P(ScrollableShelfViewRTLTest, CorrectUIInTabletWithoutOverflow) {
  547. Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
  548. for (int i = 0; i < 3; i++)
  549. AddAppShortcut();
  550. ASSERT_EQ(ScrollableShelfView::kNotShowArrowButtons,
  551. scrollable_shelf_view_->layout_strategy_for_test());
  552. gfx::Rect hotseat_background =
  553. scrollable_shelf_view_->GetHotseatBackgroundBounds();
  554. views::View::ConvertRectToScreen(scrollable_shelf_view_, &hotseat_background);
  555. views::ViewModel* view_model = shelf_view_->view_model_for_test();
  556. const gfx::Rect first_tappable_icon_bounds =
  557. view_model
  558. ->view_at(scrollable_shelf_view_->first_tappable_app_index().value())
  559. ->GetBoundsInScreen();
  560. const gfx::Rect last_tappable_icon_bounds =
  561. view_model
  562. ->view_at(scrollable_shelf_view_->last_tappable_app_index().value())
  563. ->GetBoundsInScreen();
  564. if (base::i18n::IsRTL()) {
  565. EXPECT_EQ(hotseat_background.x() + scrollable_shelf_constants::kEndPadding,
  566. last_tappable_icon_bounds.x());
  567. EXPECT_EQ(
  568. hotseat_background.right() - scrollable_shelf_constants::kEndPadding,
  569. first_tappable_icon_bounds.right());
  570. } else {
  571. EXPECT_EQ(hotseat_background.x() + scrollable_shelf_constants::kEndPadding,
  572. first_tappable_icon_bounds.x());
  573. EXPECT_EQ(
  574. hotseat_background.right() - scrollable_shelf_constants::kEndPadding,
  575. last_tappable_icon_bounds.right());
  576. }
  577. VerifyTappableAppIconsUnderMouseHover();
  578. }
  579. // Verifies that activating a shelf icon's ripple ring does not bring crash
  580. // on an extremely small display. It is an edge case detected by fuzz tests.
  581. TEST_P(ScrollableShelfViewRTLTest, VerifyActivateIconRippleOnVerySmallDisplay) {
  582. AddAppShortcut();
  583. // Resize the display to ensure that no shelf icon is visible.
  584. UpdateDisplay("60x601");
  585. // Activate a shelf icon's ink drop. Verify that no crash happens.
  586. auto* ink_drop = views::InkDrop::Get(test_api_->GetButton(0))->GetInkDrop();
  587. ink_drop->SnapToActivated();
  588. EXPECT_EQ(views::InkDropState::ACTIVATED, ink_drop->GetTargetInkDropState());
  589. }
  590. // Verifies that the scrollable shelf without overflow has the correct layout in
  591. // tablet mode.
  592. TEST_P(ScrollableShelfViewRTLTest, CheckRoundedCornersSetForInkDrop) {
  593. Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
  594. AddAppShortcutsUntilOverflow();
  595. ASSERT_EQ(ScrollableShelfView::kShowRightArrowButton,
  596. scrollable_shelf_view_->layout_strategy_for_test());
  597. ASSERT_TRUE(scrollable_shelf_view_->shelf_container_view()
  598. ->layer()
  599. ->rounded_corner_radii()
  600. .IsEmpty());
  601. ShelfViewTestAPI shelf_view_test_api(shelf_view_);
  602. ShelfAppButton* first_icon = shelf_view_test_api.GetButton(
  603. scrollable_shelf_view_->first_tappable_app_index().value());
  604. ShelfAppButton* last_icon = shelf_view_test_api.GetButton(
  605. scrollable_shelf_view_->last_tappable_app_index().value());
  606. // When the right arrow is showing, check rounded corners are set if the ink
  607. // drop is visible for the first visible app.
  608. EXPECT_TRUE(HasRoundedCornersOnAppButtonAfterMouseRightClick(first_icon));
  609. // When the right arrow is showing, check rounded corners are not set if the
  610. // ink drop is visible for the last visible app
  611. EXPECT_FALSE(HasRoundedCornersOnAppButtonAfterMouseRightClick(last_icon));
  612. // Tap right arrow. Hotseat layout must now show left arrow.
  613. gfx::Rect right_arrow =
  614. scrollable_shelf_view_->right_arrow()->GetBoundsInScreen();
  615. GetEventGenerator()->GestureTapAt(right_arrow.CenterPoint());
  616. ASSERT_EQ(ScrollableShelfView::kShowLeftArrowButton,
  617. scrollable_shelf_view_->layout_strategy_for_test());
  618. // Recalculate first and last icons.
  619. first_icon = shelf_view_test_api.GetButton(
  620. scrollable_shelf_view_->first_tappable_app_index().value());
  621. last_icon = shelf_view_test_api.GetButton(
  622. scrollable_shelf_view_->last_tappable_app_index().value());
  623. // When the left arrow is showing, check rounded corners are set if the ink
  624. // drop is visible for the last visible app.
  625. EXPECT_TRUE(HasRoundedCornersOnAppButtonAfterMouseRightClick(last_icon));
  626. // When the left arrow is showing, check rounded corners are not set if the
  627. // ink drop is visible for the first visible app
  628. EXPECT_FALSE(HasRoundedCornersOnAppButtonAfterMouseRightClick(first_icon));
  629. }
  630. // Verify that the rounded corners work as expected after transition from
  631. // clamshell mode to tablet mode (https://crbug.com/1086484).
  632. TEST_P(ScrollableShelfViewRTLTest,
  633. CheckRoundedCornersAfterTabletStateTransition) {
  634. ui::ScopedAnimationDurationScaleMode regular_animations(
  635. ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
  636. PopulateAppShortcut(1);
  637. ShelfAppButton* icon = test_api_->GetButton(0);
  638. // Right click on the app button to activate the ripple ring.
  639. GetEventGenerator()->MoveMouseTo(icon->GetBoundsInScreen().CenterPoint());
  640. GetEventGenerator()->ClickRightButton();
  641. {
  642. InkDropAnimationWaiter waiter(icon);
  643. waiter.Wait();
  644. }
  645. ASSERT_EQ(views::InkDropState::ACTIVATED,
  646. views::InkDrop::Get(icon)->GetInkDrop()->GetTargetInkDropState());
  647. // Verify that in clamshell when the ripple ring is activated, the rounded
  648. // corners should not be applied.
  649. EXPECT_TRUE(
  650. scrollable_shelf_view_->IsAnyCornerButtonInkDropActivatedForTest());
  651. EXPECT_TRUE(scrollable_shelf_view_->shelf_container_view()
  652. ->layer()
  653. ->rounded_corner_radii()
  654. .IsEmpty());
  655. // Switch to tablet mode. The ripple ring should be hidden.
  656. Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
  657. {
  658. InkDropAnimationWaiter waiter(icon);
  659. waiter.Wait();
  660. }
  661. EXPECT_EQ(views::InkDropState::HIDDEN,
  662. views::InkDrop::Get(icon)->GetInkDrop()->GetTargetInkDropState());
  663. // Verify that the rounded corners should not be applied when the ripple ring
  664. // is hidden.
  665. ASSERT_TRUE(scrollable_shelf_view_->shelf_container_view()
  666. ->layer()
  667. ->rounded_corner_radii()
  668. .IsEmpty());
  669. EXPECT_FALSE(
  670. scrollable_shelf_view_->IsAnyCornerButtonInkDropActivatedForTest());
  671. }
  672. // Verify that the count of activated corner buttons is expected after removing
  673. // an app icon from context menu (https://crbug.com/1086484).
  674. TEST_F(ScrollableShelfViewTest,
  675. CheckRoundedCornersAfterUnpinningFromContextMenu) {
  676. ui::ScopedAnimationDurationScaleMode regular_animations(
  677. ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
  678. Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
  679. AddAppShortcut();
  680. const ShelfID app_id = AddAppShortcut();
  681. ASSERT_EQ(2u, test_api_->GetButtonCount());
  682. ShelfModel* shelf_model = ShelfModel::Get();
  683. const int index = shelf_model->ItemIndexByID(app_id);
  684. ShelfAppButton* icon = test_api_->GetButton(index);
  685. // Right click on the app button to activate the ripple ring.
  686. GetEventGenerator()->MoveMouseTo(icon->GetBoundsInScreen().CenterPoint());
  687. GetEventGenerator()->ClickRightButton();
  688. {
  689. InkDropAnimationWaiter waiter(icon);
  690. waiter.Wait();
  691. }
  692. EXPECT_EQ(views::InkDropState::ACTIVATED,
  693. views::InkDrop::Get(icon)->GetInkDrop()->GetTargetInkDropState());
  694. // Emulate to remove a shelf icon from context menu.
  695. shelf_model->RemoveItemAt(index);
  696. test_api_->RunMessageLoopUntilAnimationsDone();
  697. ASSERT_EQ(1u, test_api_->GetButtonCount());
  698. // Verify the count of activated corner buttons.
  699. EXPECT_FALSE(
  700. scrollable_shelf_view_->IsAnyCornerButtonInkDropActivatedForTest());
  701. }
  702. // Verifies that when two shelf app buttons are animating at the same time,
  703. // rounded corners are being kept if needed. (see https://crbug.com/1079330)
  704. TEST_F(ScrollableShelfViewTest, CheckRoundedCornersAfterLongPress) {
  705. // Enable animations so that we can make sure that they occur.
  706. ui::ScopedAnimationDurationScaleMode regular_animations(
  707. ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
  708. Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
  709. PopulateAppShortcut(3);
  710. ASSERT_EQ(ScrollableShelfView::kNotShowArrowButtons,
  711. scrollable_shelf_view_->layout_strategy_for_test());
  712. ASSERT_TRUE(scrollable_shelf_view_->shelf_container_view()
  713. ->layer()
  714. ->rounded_corner_radii()
  715. .IsEmpty());
  716. ui::Layer* layer = scrollable_shelf_view_->shelf_container_view()->layer();
  717. ShelfAppButton* first_icon = test_api_->GetButton(0);
  718. ShelfAppButton* last_icon = test_api_->GetButton(2);
  719. // Trigger the ripple animation over the leftmost icon and wait for it to
  720. // finish. Rounded corners should be set.
  721. GetEventGenerator()->MoveMouseTo(
  722. first_icon->GetBoundsInScreen().CenterPoint());
  723. GetEventGenerator()->ClickRightButton();
  724. {
  725. InkDropAnimationWaiter waiter(first_icon);
  726. waiter.Wait();
  727. }
  728. ASSERT_EQ(first_icon->GetInkDropForTesting()->GetTargetInkDropState(),
  729. views::InkDropState::ACTIVATED);
  730. // The gfx::RoundedCornersF object is considered empty when all of the
  731. // corners are squared (no effective radius).
  732. EXPECT_FALSE(layer->rounded_corner_radii().IsEmpty());
  733. // While ripple is showing on the leftmost icon, trigger the ripple animation
  734. // over the rightmost icon and wait for it to finish. Rounded corners should
  735. // be set.
  736. GetEventGenerator()->MoveMouseTo(
  737. last_icon->GetBoundsInScreen().CenterPoint());
  738. // Click once so the ripple on the leftmost icon will animate to hide.
  739. // Immediately click again to trigger the rightmost icon animation to show.
  740. GetEventGenerator()->ClickRightButton();
  741. GetEventGenerator()->ClickRightButton();
  742. {
  743. InkDropAnimationWaiter waiter(last_icon);
  744. waiter.Wait();
  745. }
  746. ASSERT_EQ(first_icon->GetInkDropForTesting()->GetTargetInkDropState(),
  747. views::InkDropState::HIDDEN);
  748. ASSERT_EQ(last_icon->GetInkDropForTesting()->GetTargetInkDropState(),
  749. views::InkDropState::ACTIVATED);
  750. EXPECT_FALSE(layer->rounded_corner_radii().IsEmpty());
  751. }
  752. // Verifies that doing a mousewheel scroll on the scrollable shelf does scroll
  753. // forward.
  754. TEST_P(ScrollableShelfViewRTLTest, ScrollWithMouseWheel) {
  755. // The scroll threshold. Taken from |kScrollOffsetThreshold| in
  756. // scrollable_shelf_view.cc.
  757. constexpr int scroll_threshold = 20;
  758. AddAppShortcutsUntilOverflow();
  759. ASSERT_EQ(ScrollableShelfView::kShowRightArrowButton,
  760. scrollable_shelf_view_->layout_strategy_for_test());
  761. // Do a mousewheel scroll with a positive offset bigger than the scroll
  762. // threshold to scroll forward. Unlike touchpad scrolls, mousewheel scrolls
  763. // can only be along the cross axis.
  764. GetEventGenerator()->MoveMouseTo(
  765. scrollable_shelf_view_->GetBoundsInScreen().CenterPoint());
  766. GetEventGenerator()->MoveMouseWheel(0, -(scroll_threshold + 1));
  767. ASSERT_EQ(ScrollableShelfView::kShowLeftArrowButton,
  768. scrollable_shelf_view_->layout_strategy_for_test());
  769. // Do a mousewheel scroll with a negative offset bigger than the scroll
  770. // threshold to scroll backwards.
  771. GetEventGenerator()->MoveMouseWheel(0, scroll_threshold + 1);
  772. ASSERT_EQ(ScrollableShelfView::kShowRightArrowButton,
  773. scrollable_shelf_view_->layout_strategy_for_test());
  774. // Do a mousewheel scroll with an offset smaller than the scroll
  775. // threshold should be ignored.
  776. GetEventGenerator()->MoveMouseWheel(0, scroll_threshold);
  777. ASSERT_EQ(ScrollableShelfView::kShowRightArrowButton,
  778. scrollable_shelf_view_->layout_strategy_for_test());
  779. GetEventGenerator()->MoveMouseWheel(0, -scroll_threshold);
  780. ASSERT_EQ(ScrollableShelfView::kShowRightArrowButton,
  781. scrollable_shelf_view_->layout_strategy_for_test());
  782. }
  783. // Verifies that removing a shelf icon by mouse works as expected on scrollable
  784. // shelf (see https://crbug.com/1033967).
  785. TEST_P(ScrollableShelfViewRTLTest, RipOffShelfItem) {
  786. AddAppShortcutsUntilOverflow();
  787. ASSERT_EQ(ScrollableShelfView::kShowRightArrowButton,
  788. scrollable_shelf_view_->layout_strategy_for_test());
  789. views::ViewModel* view_model = shelf_view_->view_model_for_test();
  790. const gfx::Rect first_tappable_view_bounds =
  791. view_model
  792. ->view_at(scrollable_shelf_view_->first_tappable_app_index().value())
  793. ->GetBoundsInScreen();
  794. const gfx::Point drag_start_location =
  795. first_tappable_view_bounds.CenterPoint();
  796. const gfx::Point drag_end_location = gfx::Point(
  797. drag_start_location.x(),
  798. drag_start_location.y() - 3 * ShelfConfig::Get()->shelf_size());
  799. // Drags an icon off the shelf to remove it.
  800. GetEventGenerator()->MoveMouseTo(drag_start_location);
  801. GetEventGenerator()->PressLeftButton();
  802. GetEventGenerator()->MoveMouseTo(drag_end_location);
  803. GetEventGenerator()->ReleaseLeftButton();
  804. // Expects that the scrollable shelf has the correct layout strategy.
  805. EXPECT_EQ(ScrollableShelfView::kNotShowArrowButtons,
  806. scrollable_shelf_view_->layout_strategy_for_test());
  807. }
  808. // Verifies that the scrollable shelf handles the mouse wheel event as expected.
  809. TEST_P(ScrollableShelfViewRTLTest, MouseWheelOnEmptyShelfShouldExpandAppList) {
  810. // First mouse wheel over apps and then over empty shelf. When apps are not
  811. // overflowing, and we mouse wheel over the app icons, the launcher should
  812. // stay hidden. When we mouse wheel over the empty area of the shelf, the
  813. // launcher should expand. https://crbug.com/1071218
  814. // This behavior only applies to peeking launcher.
  815. if (features::IsProductivityLauncherEnabled())
  816. return;
  817. // Add a couple of apps to start, so we have some to put the cursor over for
  818. // testing.
  819. AddAppShortcut();
  820. AddAppShortcut();
  821. GetEventGenerator()->MoveMouseTo(scrollable_shelf_view_->shelf_view()
  822. ->view_model_for_test()
  823. ->view_at(1)
  824. ->GetBoundsInScreen()
  825. .CenterPoint());
  826. int shelf_scroll_threshold =
  827. ShelfConfig::Get()->mousewheel_scroll_offset_threshold();
  828. GetEventGenerator()->MoveMouseWheel(0, shelf_scroll_threshold + 1);
  829. // The app list's view is lazily loaded. Since this is the first time, and we
  830. // didn't scroll in the right spot, it shouldn't have been created yet.
  831. auto* presenter = Shell::Get()->app_list_controller()->fullscreen_presenter();
  832. EXPECT_EQ(nullptr, presenter->GetView());
  833. auto empty_shelf_point = scrollable_shelf_view_->GetBoundsInScreen().origin();
  834. empty_shelf_point.Offset(10, 10);
  835. GetEventGenerator()->MoveMouseTo(empty_shelf_point);
  836. GetEventGenerator()->MoveMouseWheel(0, shelf_scroll_threshold + 1);
  837. auto* app_list_view = presenter->GetView();
  838. EXPECT_EQ(AppListViewState::kPeeking, app_list_view->app_list_state());
  839. // Scrolling again should expand to all apps.
  840. GetEventGenerator()->MoveMouseWheel(0, shelf_scroll_threshold + 1);
  841. EXPECT_EQ(AppListViewState::kFullscreenAllApps,
  842. app_list_view->app_list_state());
  843. // Scrolling up on fullscreen will do nothing.
  844. GetEventGenerator()->MoveMouseWheel(0, shelf_scroll_threshold + 1);
  845. EXPECT_EQ(AppListViewState::kFullscreenAllApps,
  846. app_list_view->app_list_state());
  847. // Scrolling down will close the app list.
  848. GetEventGenerator()->MoveMouseWheel(0, -shelf_scroll_threshold - 1);
  849. EXPECT_EQ(AppListViewState::kClosed, app_list_view->app_list_state());
  850. }
  851. TEST_P(ScrollableShelfViewRTLTest, ScrollsByMouseWheelEvent) {
  852. AddAppShortcutsUntilOverflow();
  853. ASSERT_EQ(ScrollableShelfView::kShowRightArrowButton,
  854. scrollable_shelf_view_->layout_strategy_for_test());
  855. GetEventGenerator()->MoveMouseTo(
  856. scrollable_shelf_view_->GetBoundsInScreen().CenterPoint());
  857. constexpr int scroll_threshold =
  858. scrollable_shelf_constants::kScrollOffsetThreshold;
  859. // Verifies that it should not scroll the shelf backward anymore if the layout
  860. // strategy is kShowRightArrowButton.
  861. GetEventGenerator()->MoveMouseWheel(scroll_threshold + 1, 0);
  862. EXPECT_EQ(ScrollableShelfView::kShowRightArrowButton,
  863. scrollable_shelf_view_->layout_strategy_for_test());
  864. // Verifies that the mouse wheel event with the offset smaller than the
  865. // threshold should be ignored.
  866. GetEventGenerator()->MoveMouseWheel(-scroll_threshold + 1, 0);
  867. EXPECT_EQ(ScrollableShelfView::kShowRightArrowButton,
  868. scrollable_shelf_view_->layout_strategy_for_test());
  869. GetEventGenerator()->MoveMouseWheel(-scroll_threshold - 1, 0);
  870. EXPECT_EQ(ScrollableShelfView::kShowLeftArrowButton,
  871. scrollable_shelf_view_->layout_strategy_for_test());
  872. }
  873. // Verifies that the scrollable shelf handles the scroll event (usually
  874. // generated by the touchpad scroll) as expected.
  875. TEST_P(ScrollableShelfViewRTLTest, VerifyScrollEvent) {
  876. AddAppShortcutsUntilOverflow();
  877. // Checks the default state of the scrollable shelf and the launcher.
  878. constexpr ScrollableShelfView::LayoutStrategy default_strategy =
  879. ScrollableShelfView::kShowRightArrowButton;
  880. ASSERT_EQ(default_strategy,
  881. scrollable_shelf_view_->layout_strategy_for_test());
  882. const gfx::Point start_point =
  883. scrollable_shelf_view_->GetBoundsInScreen().CenterPoint();
  884. constexpr int scroll_steps = 1;
  885. constexpr int num_fingers = 2;
  886. // Sufficient speed to exceed the threshold.
  887. constexpr int scroll_speed = 50;
  888. if (!features::IsProductivityLauncherEnabled()) {
  889. // Verifies that scrolling vertically on scrollable shelf should open the
  890. // launcher.
  891. GetEventGenerator()->ScrollSequence(start_point, base::TimeDelta(),
  892. /*x_offset=*/0, scroll_speed,
  893. scroll_steps, num_fingers);
  894. EXPECT_EQ(AppListViewState::kPeeking, Shell::Get()
  895. ->app_list_controller()
  896. ->fullscreen_presenter()
  897. ->GetView()
  898. ->app_list_state());
  899. EXPECT_EQ(default_strategy,
  900. scrollable_shelf_view_->layout_strategy_for_test());
  901. }
  902. // Verifies that scrolling horizontally should be handled by the
  903. // scrollable shelf.
  904. GetEventGenerator()->ScrollSequence(start_point, base::TimeDelta(),
  905. -scroll_speed, /*y_offset*/ 0,
  906. scroll_steps, num_fingers);
  907. EXPECT_EQ(ScrollableShelfView::kShowLeftArrowButton,
  908. scrollable_shelf_view_->layout_strategy_for_test());
  909. }
  910. // Verifies that the scrollable shelf handles horizontal scroll events on a left
  911. // or right oriented shelf correctly. These tests live here to make sure the
  912. // scrollable shelf view doesn't swallow the events like it did before this
  913. // behavior was added.
  914. TEST_P(ScrollableShelfViewRTLTest, HorizontalScrollingOnVerticalShelf) {
  915. // This behavior only applies to peeking launcher.
  916. if (features::IsProductivityLauncherEnabled())
  917. return;
  918. constexpr int scroll_steps = 1;
  919. constexpr int num_fingers = 2;
  920. // Sufficient speed to exceed the threshold.
  921. constexpr int scroll_speed = 50;
  922. GetPrimaryShelf()->SetAlignment(ShelfAlignment::kLeft);
  923. gfx::Point start_point =
  924. scrollable_shelf_view_->GetBoundsInScreen().CenterPoint();
  925. // Scrolling away from the side of the screen the shelf is on should expand
  926. // it.
  927. GetEventGenerator()->ScrollSequence(start_point, base::TimeDelta(),
  928. -scroll_speed, /*y_offset*/ 0,
  929. scroll_steps, num_fingers);
  930. auto* app_list_view =
  931. Shell::Get()->app_list_controller()->fullscreen_presenter()->GetView();
  932. EXPECT_EQ(AppListViewState::kFullscreenAllApps,
  933. app_list_view->app_list_state());
  934. // Scrolling the same way again should do nothing.
  935. GetEventGenerator()->ScrollSequence(start_point, base::TimeDelta(),
  936. -scroll_speed, /*y_offset*/ 0,
  937. scroll_steps, num_fingers);
  938. EXPECT_EQ(AppListViewState::kFullscreenAllApps,
  939. app_list_view->app_list_state());
  940. // Scrolling toward the side of the screen the shelf is on should collapse it.
  941. GetEventGenerator()->ScrollSequence(start_point, base::TimeDelta(),
  942. scroll_speed, /*y_offset*/ 0,
  943. scroll_steps, num_fingers);
  944. EXPECT_EQ(AppListViewState::kClosed, app_list_view->app_list_state());
  945. // Scrolling the same way again should do nothing.
  946. GetEventGenerator()->ScrollSequence(start_point, base::TimeDelta(),
  947. scroll_speed, /*y_offset*/ 0,
  948. scroll_steps, num_fingers);
  949. EXPECT_EQ(AppListViewState::kClosed, app_list_view->app_list_state());
  950. // Now we test the opposite side with the opposite scroll values.
  951. GetPrimaryShelf()->SetAlignment(ShelfAlignment::kRight);
  952. start_point = scrollable_shelf_view_->GetBoundsInScreen().CenterPoint();
  953. // Scrolling away from the side of the screen the shelf is on should expand
  954. // it.
  955. GetEventGenerator()->ScrollSequence(start_point, base::TimeDelta(),
  956. scroll_speed, /*y_offset*/ 0,
  957. scroll_steps, num_fingers);
  958. EXPECT_EQ(AppListViewState::kFullscreenAllApps,
  959. app_list_view->app_list_state());
  960. // Scrolling toward the side of the screen the shelf is on should collapse it.
  961. GetEventGenerator()->ScrollSequence(start_point, base::TimeDelta(),
  962. -scroll_speed, /*y_offset*/ 0,
  963. scroll_steps, num_fingers);
  964. EXPECT_EQ(AppListViewState::kClosed, app_list_view->app_list_state());
  965. }
  966. // Verify that the ripple ring of the first/last app icon is fully shown
  967. // (https://crbug.com/1057710).
  968. TEST_P(ScrollableShelfViewRTLTest, CheckInkDropRippleOfEdgeIcons) {
  969. AddAppShortcutsUntilOverflow();
  970. ASSERT_EQ(ScrollableShelfView::kShowRightArrowButton,
  971. scrollable_shelf_view_->layout_strategy_for_test());
  972. ShelfViewTestAPI shelf_view_test_api(shelf_view_);
  973. ShelfAppButton* first_app_button = shelf_view_test_api.GetButton(
  974. scrollable_shelf_view_->first_tappable_app_index().value());
  975. VeirifyRippleRingWithinShelfContainer(*first_app_button);
  976. // Tap at the right arrow. Hotseat layout should show the left arrow.
  977. gfx::Rect right_arrow =
  978. scrollable_shelf_view_->right_arrow()->GetBoundsInScreen();
  979. GetEventGenerator()->GestureTapAt(right_arrow.CenterPoint());
  980. ASSERT_EQ(ScrollableShelfView::kShowLeftArrowButton,
  981. scrollable_shelf_view_->layout_strategy_for_test());
  982. ShelfAppButton* last_app_button = shelf_view_test_api.GetButton(
  983. scrollable_shelf_view_->last_tappable_app_index().value());
  984. VeirifyRippleRingWithinShelfContainer(*last_app_button);
  985. }
  986. // Verifies that right-click on the last shelf icon should open the icon's
  987. // context menu instead of the shelf's (https://crbug.com/1041702).
  988. TEST_P(ScrollableShelfViewRTLTest, ClickAtLastIcon) {
  989. AddAppShortcutsUntilOverflow();
  990. ASSERT_EQ(ScrollableShelfView::kShowRightArrowButton,
  991. scrollable_shelf_view_->layout_strategy_for_test());
  992. // Taps at the right arrow. Hotseat layout should show the left arrow.
  993. gfx::Rect right_arrow =
  994. scrollable_shelf_view_->right_arrow()->GetBoundsInScreen();
  995. GetEventGenerator()->GestureTapAt(right_arrow.CenterPoint());
  996. ASSERT_EQ(ScrollableShelfView::kShowLeftArrowButton,
  997. scrollable_shelf_view_->layout_strategy_for_test());
  998. // Right-click on the edge of the last icon.
  999. const views::View* last_icon = shelf_view_->view_model_for_test()->view_at(
  1000. scrollable_shelf_view_->last_tappable_app_index().value());
  1001. gfx::Point click_point = last_icon->GetBoundsInScreen().right_center();
  1002. click_point.Offset(-1, 0);
  1003. GetEventGenerator()->MoveMouseTo(click_point);
  1004. GetEventGenerator()->ClickRightButton();
  1005. // Verifies that the context menu of |last_icon| should show.
  1006. EXPECT_TRUE(shelf_view_->IsShowingMenuForView(last_icon));
  1007. // Verfies that after left-click, the context menu should be closed.
  1008. GetEventGenerator()->ClickLeftButton();
  1009. EXPECT_FALSE(shelf_view_->IsShowingMenuForView(last_icon));
  1010. }
  1011. // Verifies that mouse click at the second last shelf item during the last item
  1012. // removal animation does not lead to crash (see https://crbug.com/1300561).
  1013. TEST_F(ScrollableShelfViewTest, RemoveLastItemWhileClickingSeoncdLastOne) {
  1014. PopulateAppShortcut(3);
  1015. ASSERT_EQ(ScrollableShelfView::kNotShowArrowButtons,
  1016. scrollable_shelf_view_->layout_strategy_for_test());
  1017. const int view_size_before_removal =
  1018. shelf_view_->view_model_for_test()->view_size();
  1019. {
  1020. // Remove the last shelf item with animation enabled.
  1021. ui::ScopedAnimationDurationScaleMode regular_animations(
  1022. ui::ScopedAnimationDurationScaleMode::SLOW_DURATION);
  1023. ShelfModel::Get()->RemoveItemAt(view_size_before_removal - 1);
  1024. EXPECT_TRUE(shelf_view_->IsAnimating());
  1025. }
  1026. // Mouse right click at the second last item and wait for the ink drop
  1027. // animation to complete.
  1028. ShelfAppButton* second_last_item =
  1029. ShelfViewTestAPI(shelf_view_).GetButton(view_size_before_removal - 2);
  1030. GetEventGenerator()->MoveMouseTo(
  1031. second_last_item->GetBoundsInScreen().CenterPoint());
  1032. InkDropAnimationWaiter waiter(second_last_item);
  1033. GetEventGenerator()->ClickRightButton();
  1034. waiter.Wait();
  1035. }
  1036. // Verifies that presentation time for shelf gesture scroll is recorded as
  1037. // expected (https://crbug.com/1095259).
  1038. TEST_F(ScrollableShelfViewTest, PresentationTimeMetricsForGestureScroll) {
  1039. ui::PresentationTimeRecorder::SetReportPresentationTimeImmediatelyForTest(
  1040. true);
  1041. AddAppShortcutsUntilOverflow();
  1042. ASSERT_EQ(ScrollableShelfView::kShowRightArrowButton,
  1043. scrollable_shelf_view_->layout_strategy_for_test());
  1044. views::ViewModel* view_model = shelf_view_->view_model_for_test();
  1045. ASSERT_GT(view_model->view_size(), 2u);
  1046. // |gesture_start_point| is between the first and the second shelf icon. It
  1047. // ensures that gesture scroll does not start from a point within any shelf
  1048. // icon's bounds.
  1049. const gfx::Point first_icon_center =
  1050. view_model->view_at(0)->GetBoundsInScreen().CenterPoint();
  1051. const gfx::Point second_icon_center =
  1052. view_model->view_at(1)->GetBoundsInScreen().CenterPoint();
  1053. ASSERT_EQ(first_icon_center.y(), second_icon_center.y());
  1054. const gfx::Point gesture_start_point(
  1055. (first_icon_center.x() + second_icon_center.x()) / 2,
  1056. first_icon_center.y());
  1057. GetEventGenerator()->PressTouch(gesture_start_point);
  1058. base::HistogramTester histogram_tester;
  1059. auto check_bucket_size = [&histogram_tester](
  1060. int presentation_time_expected_size,
  1061. int max_latency_expected_size) {
  1062. histogram_tester.ExpectTotalCount(
  1063. "Apps.ScrollableShelf.Drag.PresentationTime.ClamshellMode."
  1064. "LauncherHidden",
  1065. presentation_time_expected_size);
  1066. histogram_tester.ExpectTotalCount(
  1067. "Apps.ScrollableShelf.Drag.PresentationTime.MaxLatency.ClamshellMode."
  1068. "LauncherHidden",
  1069. max_latency_expected_size);
  1070. };
  1071. int last_scroll_distance = 0;
  1072. ScrollableShelfView* const scrollable_shelf_view = scrollable_shelf_view_;
  1073. // Helper function to update |last_scroll_distance| and check whether shelf
  1074. // is scrolled successfully.
  1075. auto shelf_scrolled = [&last_scroll_distance,
  1076. scrollable_shelf_view]() -> bool {
  1077. const int current_scroll_distance =
  1078. scrollable_shelf_view->scroll_offset_for_test().x();
  1079. const bool scrolled = last_scroll_distance != current_scroll_distance;
  1080. last_scroll_distance = current_scroll_distance;
  1081. return scrolled;
  1082. };
  1083. // Scroll the shelf leftward. Because scrollable shelf already reaches the
  1084. // left end. So shelf should not be scrolled successfully and the bucket
  1085. // number should not change.
  1086. GetEventGenerator()->MoveTouchBy(10, 0);
  1087. ASSERT_EQ(0, last_scroll_distance);
  1088. EXPECT_FALSE(shelf_scrolled());
  1089. check_bucket_size(0, 0);
  1090. // Scroll the shelf rightward. Verify that shelf should be scrolled to the
  1091. // right end. The bucket number changes as expected.
  1092. GetEventGenerator()->MoveTouchBy(
  1093. -scrollable_shelf_view_->GetScrollUpperBoundForTest() - 5, 0);
  1094. EXPECT_TRUE(shelf_scrolled());
  1095. ASSERT_EQ(ScrollableShelfView::kShowLeftArrowButton,
  1096. scrollable_shelf_view_->layout_strategy_for_test());
  1097. check_bucket_size(1, 0);
  1098. // Scroll the shelf rightward. Because scrollable shelf already reaches the
  1099. // right end. So shelf should not be scrolled successfully and the bucket
  1100. // number should not change.
  1101. GetEventGenerator()->MoveTouchBy(-10, 0);
  1102. EXPECT_FALSE(shelf_scrolled());
  1103. check_bucket_size(1, 0);
  1104. // End gesture. Verify that the max latency is recorded.
  1105. GetEventGenerator()->ReleaseTouch();
  1106. check_bucket_size(1, 1);
  1107. }
  1108. // Verifies that the shelf is scrolled to show the pinned app after pinning.
  1109. TEST_P(ScrollableShelfViewRTLTest, FeedbackForAppPinning) {
  1110. AddAppShortcutsUntilOverflow();
  1111. ASSERT_EQ(ScrollableShelfView::kShowRightArrowButton,
  1112. scrollable_shelf_view_->layout_strategy_for_test());
  1113. // |num| is the minimum of app icons to enter overflow mode.
  1114. const size_t num = shelf_view_->view_model_for_test()->view_size();
  1115. ShelfModel::ScopedUserTriggeredMutation user_triggered(
  1116. scrollable_shelf_view_->shelf_view()->model());
  1117. {
  1118. ShelfID shelf_id = AddAppShortcut();
  1119. const size_t view_index =
  1120. shelf_view_->model()->ItemIndexByAppID(shelf_id.app_id);
  1121. ASSERT_EQ(view_index, num);
  1122. // When shelf only contains pinned apps, expects that the shelf is scrolled
  1123. // to the last page to show the latest pinned app.
  1124. EXPECT_EQ(ScrollableShelfView::kShowLeftArrowButton,
  1125. scrollable_shelf_view_->layout_strategy_for_test());
  1126. }
  1127. GetEventGenerator()->GestureTapAt(
  1128. scrollable_shelf_view_->left_arrow()->GetBoundsInScreen().CenterPoint());
  1129. ASSERT_EQ(ScrollableShelfView::kShowRightArrowButton,
  1130. scrollable_shelf_view_->layout_strategy_for_test());
  1131. // Pins the icons of running apps to the shelf.
  1132. for (size_t i = 0; i < 2 * num; i++)
  1133. AddAppShortcut(ShelfItemType::TYPE_APP);
  1134. {
  1135. ShelfID shelf_id = AddAppShortcut();
  1136. const size_t view_index =
  1137. shelf_view_->model()->ItemIndexByAppID(shelf_id.app_id);
  1138. ASSERT_EQ(view_index, num + 1);
  1139. // Scrolls the shelf to show the pinned app. Expects that the shelf is
  1140. // scrolled to the correct page. Notes that the pinned app should be placed
  1141. // ahead of running apps.
  1142. EXPECT_LT(view_index,
  1143. scrollable_shelf_view_->last_tappable_app_index().value());
  1144. EXPECT_GT(view_index,
  1145. scrollable_shelf_view_->first_tappable_app_index().value());
  1146. EXPECT_EQ(ScrollableShelfView::kShowButtons,
  1147. scrollable_shelf_view_->layout_strategy_for_test());
  1148. }
  1149. }
  1150. class ScrollableShelfViewWithAppScalingTest : public ScrollableShelfViewTest {
  1151. public:
  1152. ScrollableShelfViewWithAppScalingTest() = default;
  1153. ~ScrollableShelfViewWithAppScalingTest() override = default;
  1154. void SetUp() override {
  1155. // When the calendar view is enabled, the status widget's bounds could vary
  1156. // under different dates. For example, "June 10" is longer than "June 9".
  1157. // Therefore, the code below sets the constant date to avoid flakiness.
  1158. if (features::IsCalendarViewEnabled()) {
  1159. scoped_locale_ =
  1160. std::make_unique<base::test::ScopedRestoreICUDefaultLocale>("en_US"),
  1161. time_zone_ = std::make_unique<base::test::ScopedRestoreDefaultTimezone>(
  1162. "America/Chicago");
  1163. constexpr char kFakeNowTimeString[] = "Sunday, 5 June 2022 14:30:00 CDT";
  1164. ASSERT_TRUE(base::Time::FromString(kFakeNowTimeString,
  1165. &TimeOverrideHelper::current_time));
  1166. time_override_ = std::make_unique<base::subtle::ScopedTimeClockOverrides>(
  1167. &TimeOverrideHelper::TimeNow, /*time_ticks_override=*/nullptr,
  1168. /*thread_ticks_override=*/nullptr);
  1169. }
  1170. ScrollableShelfViewTest::SetUp();
  1171. // Display should be big enough (width and height are bigger than 600).
  1172. // Otherwise, shelf is in dense mode by default.
  1173. // Note that the display width is hard coded. The display width should
  1174. // ensure that the two-stage scaling is possible to happen. Otherwise,
  1175. // there may be insufficient space to accommodate shelf icons in
  1176. // |ShelfConfig::shelf_button_mediate_size_| then hotseat density may switch
  1177. // from kNormal to kDense directly.
  1178. UpdateDisplay("820x601");
  1179. // App scaling is only used in tablet mode.
  1180. Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
  1181. base::RunLoop().RunUntilIdle();
  1182. ASSERT_FALSE(ShelfConfig::Get()->is_dense());
  1183. }
  1184. protected:
  1185. // |kAppCount| is a magic number, which satisfies the following
  1186. // conditions:
  1187. // (1) Scrollable shelf shows |kAppCount| app buttons in size of
  1188. // |ShelfConfig::shelf_button_size_| without scrolling.
  1189. // (2) Scrollable shelf shows (|kAppCount| + 1) app buttons in size of
  1190. // |ShelfConfig::shelf_button_mediate_size_| without scrolling.
  1191. // (3) Scrollable shelf cannot show (|kAppCount| + 2) app buttons in size of
  1192. // |ShelfConfig::shelf_button_mediate_size_| without scrolling.
  1193. // Addition or removal of shelf button should not change hotseat state.
  1194. // So Hotseat widget's width is a constant. Then |kAppCount| is in the range
  1195. // of [1, (hotseat width) / (shelf button + button spacing) + 1].
  1196. // So we can get |kAppCount| in that range manually
  1197. static constexpr int kAppCount = 10;
  1198. // If calendar view is enabled, the space is a little smaller and can only
  1199. // show 9 apps at one time.
  1200. static constexpr int kAppCountWithShowingDateTray = 9;
  1201. // Ensures that the current time is constant if the calendar view is enabled.
  1202. std::unique_ptr<base::test::ScopedRestoreICUDefaultLocale> scoped_locale_;
  1203. std::unique_ptr<base::test::ScopedRestoreDefaultTimezone> time_zone_;
  1204. std::unique_ptr<base::subtle::ScopedTimeClockOverrides> time_override_;
  1205. };
  1206. // Verifies the basic function of app scaling which scales down the hotseat and
  1207. // its children's sizes if there is insufficient space for shelf buttons to show
  1208. // without scrolling.
  1209. TEST_F(ScrollableShelfViewWithAppScalingTest, AppScalingBasics) {
  1210. if (features::IsCalendarViewEnabled())
  1211. PopulateAppShortcut(kAppCountWithShowingDateTray);
  1212. else
  1213. PopulateAppShortcut(kAppCount);
  1214. HotseatWidget* hotseat_widget =
  1215. GetPrimaryShelf()->shelf_widget()->hotseat_widget();
  1216. EXPECT_EQ(HotseatDensity::kNormal, hotseat_widget->target_hotseat_density());
  1217. EXPECT_EQ(ScrollableShelfView::kNotShowArrowButtons,
  1218. scrollable_shelf_view_->layout_strategy_for_test());
  1219. // Pin an app icon. Verify that hotseat's target density updates as expected.
  1220. AddAppShortcut();
  1221. EXPECT_EQ(HotseatDensity::kSemiDense,
  1222. hotseat_widget->target_hotseat_density());
  1223. EXPECT_EQ(ScrollableShelfView::kNotShowArrowButtons,
  1224. scrollable_shelf_view_->layout_strategy_for_test());
  1225. // Pin another app icon. Verify that hotseat's target density updates as
  1226. // expected.
  1227. const ShelfID shelf_id = AddAppShortcut();
  1228. EXPECT_EQ(HotseatDensity::kDense, hotseat_widget->target_hotseat_density());
  1229. // Unpin an app icon.
  1230. ShelfModel* shelf_model = ShelfModel::Get();
  1231. const gfx::Rect bounds_before_unpin =
  1232. hotseat_widget->GetWindowBoundsInScreen();
  1233. const int shelf_button_size_before = shelf_view_->GetButtonSize();
  1234. shelf_model->RemoveItemAt(shelf_model->ItemIndexByID(shelf_id));
  1235. test_api_->RunMessageLoopUntilAnimationsDone();
  1236. // Verify that:
  1237. // (1) After unpinning, hotseat's target density is expected.
  1238. // (2) Hotseat widget's size and the shelf button size are expected.
  1239. const gfx::Rect bounds_after_unpin =
  1240. hotseat_widget->GetWindowBoundsInScreen();
  1241. const int shelf_button_size_after = shelf_view_->GetButtonSize();
  1242. EXPECT_EQ(HotseatDensity::kSemiDense,
  1243. hotseat_widget->target_hotseat_density());
  1244. EXPECT_EQ(bounds_before_unpin.width(), bounds_after_unpin.width());
  1245. EXPECT_LT(bounds_before_unpin.height(), bounds_after_unpin.height());
  1246. EXPECT_LT(shelf_button_size_before, shelf_button_size_after);
  1247. }
  1248. // Verifies that app scaling works as expected with hotseat state transition.
  1249. TEST_F(ScrollableShelfViewWithAppScalingTest,
  1250. VerifyWithHotseatStateTransition) {
  1251. if (features::IsCalendarViewEnabled())
  1252. PopulateAppShortcut(kAppCountWithShowingDateTray);
  1253. else
  1254. PopulateAppShortcut(kAppCount);
  1255. HotseatWidget* hotseat_widget =
  1256. GetPrimaryShelf()->shelf_widget()->hotseat_widget();
  1257. EXPECT_EQ(HotseatDensity::kNormal, hotseat_widget->target_hotseat_density());
  1258. // Pin an app icon then enter the overview mode. Verify that app scaling is
  1259. // turned on.
  1260. const ShelfID shelf_id = AddAppShortcut();
  1261. EnterOverview();
  1262. WaitForOverviewAnimation(/*enter=*/true);
  1263. EXPECT_EQ(HotseatDensity::kSemiDense,
  1264. hotseat_widget->target_hotseat_density());
  1265. // Unpin an app icon. Verify that hotseat density updates.
  1266. ShelfModel* shelf_model = ShelfModel::Get();
  1267. shelf_model->RemoveItemAt(shelf_model->ItemIndexByID(shelf_id));
  1268. test_api_->RunMessageLoopUntilAnimationsDone();
  1269. EXPECT_EQ(HotseatDensity::kNormal, hotseat_widget->target_hotseat_density());
  1270. // Exit overview mode. Verify the hotseat density.
  1271. ExitOverview();
  1272. WaitForOverviewAnimation(/*enter=*/false);
  1273. EXPECT_EQ(HotseatDensity::kNormal, hotseat_widget->target_hotseat_density());
  1274. }
  1275. // Verifies that right-click on scroll arrows shows shelf's context menu
  1276. // (https://crbug.com/1324741).
  1277. TEST_F(ScrollableShelfViewTest, RightClickArrows) {
  1278. AddAppShortcutsUntilOverflow();
  1279. ASSERT_EQ(ScrollableShelfView::kShowRightArrowButton,
  1280. scrollable_shelf_view_->layout_strategy_for_test());
  1281. // Right click on the right arrow. Shelf context menu should show.
  1282. gfx::Rect right_arrow =
  1283. scrollable_shelf_view_->right_arrow()->GetBoundsInScreen();
  1284. GetEventGenerator()->MoveMouseTo(right_arrow.CenterPoint());
  1285. GetEventGenerator()->ClickRightButton();
  1286. EXPECT_TRUE(
  1287. shelf_view_->IsShowingMenuForView(scrollable_shelf_view_->right_arrow()));
  1288. // Now click on the right arrow. Hotseat layout should show the left arrow.
  1289. GetEventGenerator()->ClickLeftButton();
  1290. EXPECT_FALSE(
  1291. shelf_view_->IsShowingMenuForView(scrollable_shelf_view_->right_arrow()));
  1292. GetEventGenerator()->ClickLeftButton();
  1293. ASSERT_EQ(ScrollableShelfView::kShowLeftArrowButton,
  1294. scrollable_shelf_view_->layout_strategy_for_test());
  1295. // Right-click on the left arrow. Shelf context menu should show.
  1296. gfx::Rect left_arrow =
  1297. scrollable_shelf_view_->left_arrow()->GetBoundsInScreen();
  1298. GetEventGenerator()->MoveMouseTo(left_arrow.CenterPoint());
  1299. GetEventGenerator()->ClickRightButton();
  1300. EXPECT_TRUE(
  1301. shelf_view_->IsShowingMenuForView(scrollable_shelf_view_->left_arrow()));
  1302. // After left-click, the context menu should be closed.
  1303. GetEventGenerator()->ClickLeftButton();
  1304. EXPECT_FALSE(
  1305. shelf_view_->IsShowingMenuForView(scrollable_shelf_view_->left_arrow()));
  1306. }
  1307. // Verifies that activating an app will automatically scroll the shelf to show
  1308. // the activated app icon.
  1309. TEST_P(ScrollableShelfViewRTLTest, ActivateAppScrollShelfToMakeAppVisible) {
  1310. AddAppShortcutsUntilOverflow();
  1311. const ShelfButton* first_button = test_api_->GetButton(0);
  1312. const int last_button_index = test_api_->GetButtonCount() - 1;
  1313. const ShelfButton* last_button = test_api_->GetButton(last_button_index);
  1314. gfx::Rect visible_space_in_screen = scrollable_shelf_view_->visible_space();
  1315. views::View::ConvertRectToScreen(scrollable_shelf_view_,
  1316. &visible_space_in_screen);
  1317. // Initially, the first button will show while the last one will not because
  1318. // of the overflow.
  1319. EXPECT_TRUE(
  1320. visible_space_in_screen.Contains(first_button->GetBoundsInScreen()));
  1321. EXPECT_FALSE(
  1322. visible_space_in_screen.Contains(last_button->GetBoundsInScreen()));
  1323. gfx::Rect first_window_bounds(0, 0, 200, 200);
  1324. gfx::Rect last_window_bounds(200, 0, 200, 200);
  1325. // Create new windows for the first and last apps on the shelf. Set the
  1326. // `kShelfIDKey` property to associate windows with shelf apps.
  1327. std::unique_ptr<aura::Window> first_app_window =
  1328. CreateTestWindow(first_window_bounds);
  1329. first_app_window->SetProperty(kShelfIDKey,
  1330. ShelfModel::Get()->items()[0].id.Serialize());
  1331. std::unique_ptr<aura::Window> last_app_window =
  1332. CreateTestWindow(last_window_bounds);
  1333. last_app_window->SetProperty(
  1334. kShelfIDKey,
  1335. ShelfModel::Get()->items()[last_button_index].id.Serialize());
  1336. // Activate `last_app_window` by mouse click. It emulates the process that an
  1337. // app window is activated after clicking at a shelf app icon.
  1338. GetEventGenerator()->MoveMouseTo(last_window_bounds.CenterPoint());
  1339. GetEventGenerator()->ClickLeftButton();
  1340. // Get the new visible space of `scrollable_shelf_view_`
  1341. visible_space_in_screen = scrollable_shelf_view_->visible_space();
  1342. views::View::ConvertRectToScreen(scrollable_shelf_view_,
  1343. &visible_space_in_screen);
  1344. // The last app button should be showing while the first app button is not
  1345. // because the shelf is scrolled.
  1346. EXPECT_FALSE(
  1347. visible_space_in_screen.Contains(first_button->GetBoundsInScreen()));
  1348. EXPECT_TRUE(
  1349. visible_space_in_screen.Contains(last_button->GetBoundsInScreen()));
  1350. // Activate `last_app_window` by mouse click. It emulates the process that an
  1351. // app window is activated after clicking at a shelf app icon.
  1352. GetEventGenerator()->MoveMouseTo(first_window_bounds.CenterPoint());
  1353. GetEventGenerator()->ClickLeftButton();
  1354. // Get the new visible space of `scrollable_shelf_view_`
  1355. visible_space_in_screen = scrollable_shelf_view_->visible_space();
  1356. views::View::ConvertRectToScreen(scrollable_shelf_view_,
  1357. &visible_space_in_screen);
  1358. // The first app button should be showing while the last app button is not
  1359. // because the shelf is scrolled.
  1360. EXPECT_TRUE(
  1361. visible_space_in_screen.Contains(first_button->GetBoundsInScreen()));
  1362. EXPECT_FALSE(
  1363. visible_space_in_screen.Contains(last_button->GetBoundsInScreen()));
  1364. }
  1365. } // namespace ash