shelf_view_unittest.cc 142 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573
  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/shelf/shelf_view.h"
  5. #include <algorithm>
  6. #include <cmath>
  7. #include <memory>
  8. #include <utility>
  9. #include <vector>
  10. #include "ash/accessibility/accessibility_controller_impl.h"
  11. #include "ash/app_list/test/app_list_test_helper.h"
  12. #include "ash/app_list/views/app_list_view.h"
  13. #include "ash/constants/ash_features.h"
  14. #include "ash/display/screen_orientation_controller.h"
  15. #include "ash/display/screen_orientation_controller_test_api.h"
  16. #include "ash/focus_cycler.h"
  17. #include "ash/ime/ime_controller_impl.h"
  18. #include "ash/public/cpp/shelf_config.h"
  19. #include "ash/public/cpp/shelf_model.h"
  20. #include "ash/public/cpp/shelf_prefs.h"
  21. #include "ash/public/cpp/shell_window_ids.h"
  22. #include "ash/public/cpp/test/test_shelf_item_delegate.h"
  23. #include "ash/public/cpp/window_properties.h"
  24. #include "ash/root_window_controller.h"
  25. #include "ash/screen_util.h"
  26. #include "ash/session/session_controller_impl.h"
  27. #include "ash/shelf/back_button.h"
  28. #include "ash/shelf/home_button.h"
  29. #include "ash/shelf/shelf.h"
  30. #include "ash/shelf/shelf_app_button.h"
  31. #include "ash/shelf/shelf_focus_cycler.h"
  32. #include "ash/shelf/shelf_navigation_widget.h"
  33. #include "ash/shelf/shelf_observer.h"
  34. #include "ash/shelf/shelf_test_util.h"
  35. #include "ash/shelf/shelf_tooltip_manager.h"
  36. #include "ash/shelf/shelf_view_test_api.h"
  37. #include "ash/shelf/shelf_widget.h"
  38. #include "ash/shell.h"
  39. #include "ash/strings/grit/ash_strings.h"
  40. #include "ash/system/status_area_widget.h"
  41. #include "ash/test/ash_test_base.h"
  42. #include "ash/test/ash_test_helper.h"
  43. #include "ash/test/ui_controls_factory_ash.h"
  44. #include "ash/utility/haptics_tracking_test_input_controller.h"
  45. #include "ash/wallpaper/wallpaper_controller_impl.h"
  46. #include "ash/wallpaper/wallpaper_controller_test_api.h"
  47. #include "ash/wm/tablet_mode/tablet_mode_controller.h"
  48. #include "ash/wm/window_state.h"
  49. #include "base/i18n/rtl.h"
  50. #include "base/memory/ptr_util.h"
  51. #include "base/numerics/safe_conversions.h"
  52. #include "base/run_loop.h"
  53. #include "base/strings/string_number_conversions.h"
  54. #include "base/strings/utf_string_conversions.h"
  55. #include "base/test/icu_test_util.h"
  56. #include "base/test/metrics/histogram_tester.h"
  57. #include "base/test/metrics/user_action_tester.h"
  58. #include "base/test/scoped_feature_list.h"
  59. #include "base/test/scoped_mock_time_message_loop_task_runner.h"
  60. #include "base/time/time.h"
  61. #include "components/prefs/pref_service.h"
  62. #include "testing/gmock/include/gmock/gmock.h"
  63. #include "testing/gtest/include/gtest/gtest.h"
  64. #include "ui/aura/test/aura_test_base.h"
  65. #include "ui/aura/window.h"
  66. #include "ui/aura/window_event_dispatcher.h"
  67. #include "ui/base/l10n/l10n_util.h"
  68. #include "ui/base/test/ui_controls.h"
  69. #include "ui/compositor/layer.h"
  70. #include "ui/compositor/scoped_animation_duration_scale_mode.h"
  71. #include "ui/display/display.h"
  72. #include "ui/display/screen.h"
  73. #include "ui/display/test/display_manager_test_api.h"
  74. #include "ui/events/event.h"
  75. #include "ui/events/event_constants.h"
  76. #include "ui/events/event_utils.h"
  77. #include "ui/events/test/event_generator.h"
  78. #include "ui/events/types/event_type.h"
  79. #include "ui/gfx/geometry/point.h"
  80. #include "ui/views/animation/bounds_animator.h"
  81. #include "ui/views/animation/ink_drop.h"
  82. #include "ui/views/animation/ink_drop_impl.h"
  83. #include "ui/views/animation/test/ink_drop_host_view_test_api.h"
  84. #include "ui/views/animation/test/ink_drop_impl_test_api.h"
  85. #include "ui/views/bubble/bubble_frame_view.h"
  86. #include "ui/views/view_model.h"
  87. #include "ui/views/widget/widget.h"
  88. #include "ui/views/widget/widget_delegate.h"
  89. #include "ui/wm/core/coordinate_conversion.h"
  90. #include "ui/wm/core/window_util.h"
  91. using testing::ElementsAre;
  92. using testing::IsEmpty;
  93. namespace ash {
  94. namespace {
  95. // Create a test 1x1 icon image with a given |color|.
  96. gfx::ImageSkia CreateImageSkiaIcon(SkColor color) {
  97. SkBitmap bitmap;
  98. bitmap.allocN32Pixels(1, 1);
  99. bitmap.eraseColor(color);
  100. return gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
  101. }
  102. int64_t GetPrimaryDisplayId() {
  103. return display::Screen::GetScreen()->GetPrimaryDisplay().id();
  104. }
  105. void ExpectFocused(views::View* view) {
  106. EXPECT_TRUE(view->GetWidget()->IsActive());
  107. EXPECT_TRUE(view->Contains(view->GetFocusManager()->GetFocusedView()));
  108. }
  109. void ExpectNotFocused(views::View* view) {
  110. EXPECT_FALSE(view->GetWidget()->IsActive());
  111. EXPECT_FALSE(view->Contains(view->GetFocusManager()->GetFocusedView()));
  112. }
  113. class TestShelfObserver : public ShelfObserver {
  114. public:
  115. explicit TestShelfObserver(Shelf* shelf) : shelf_(shelf) {
  116. shelf_->AddObserver(this);
  117. }
  118. TestShelfObserver(const TestShelfObserver&) = delete;
  119. TestShelfObserver& operator=(const TestShelfObserver&) = delete;
  120. ~TestShelfObserver() override { shelf_->RemoveObserver(this); }
  121. // ShelfObserver implementation.
  122. void OnShelfIconPositionsChanged() override {
  123. icon_positions_changed_ = true;
  124. icon_positions_animation_duration_ =
  125. ShelfViewTestAPI(shelf_->GetShelfViewForTesting())
  126. .GetAnimationDuration();
  127. }
  128. bool icon_positions_changed() const { return icon_positions_changed_; }
  129. void Reset() {
  130. icon_positions_changed_ = false;
  131. icon_positions_animation_duration_ = base::TimeDelta();
  132. }
  133. base::TimeDelta icon_positions_animation_duration() const {
  134. return icon_positions_animation_duration_;
  135. }
  136. private:
  137. Shelf* const shelf_;
  138. bool icon_positions_changed_ = false;
  139. base::TimeDelta icon_positions_animation_duration_;
  140. };
  141. // A ShelfItemDelegate that tracks the last context menu request, and exposes a
  142. // method tests can use to finish the tracked request.
  143. class AsyncContextMenuShelfItemDelegate : public ShelfItemDelegate {
  144. public:
  145. AsyncContextMenuShelfItemDelegate() : ShelfItemDelegate(ShelfID()) {}
  146. ~AsyncContextMenuShelfItemDelegate() override = default;
  147. bool RunPendingContextMenuCallback(
  148. std::unique_ptr<ui::SimpleMenuModel> model) {
  149. if (pending_context_menu_callback_.is_null())
  150. return false;
  151. std::move(pending_context_menu_callback_).Run(std::move(model));
  152. return true;
  153. }
  154. bool HasPendingContextMenuCallback() const {
  155. return !pending_context_menu_callback_.is_null();
  156. }
  157. private:
  158. // ShelfItemDelegate:
  159. void GetContextMenu(int64_t display_id,
  160. GetContextMenuCallback callback) override {
  161. ASSERT_TRUE(pending_context_menu_callback_.is_null());
  162. pending_context_menu_callback_ = std::move(callback);
  163. }
  164. void ExecuteCommand(bool, int64_t, int32_t, int64_t) override {}
  165. void Close() override {}
  166. GetContextMenuCallback pending_context_menu_callback_;
  167. };
  168. } // namespace
  169. ////////////////////////////////////////////////////////////////////////////////
  170. // ShelfObserver::OnShelfIconPositionsChanged tests.
  171. class ShelfObserverIconTest : public AshTestBase {
  172. public:
  173. ShelfObserverIconTest() = default;
  174. ShelfObserverIconTest(const ShelfObserverIconTest&) = delete;
  175. ShelfObserverIconTest& operator=(const ShelfObserverIconTest&) = delete;
  176. ~ShelfObserverIconTest() override = default;
  177. void SetUp() override {
  178. AshTestBase::SetUp();
  179. observer_ = std::make_unique<TestShelfObserver>(GetPrimaryShelf());
  180. shelf_view_test_ = std::make_unique<ShelfViewTestAPI>(
  181. GetPrimaryShelf()->GetShelfViewForTesting());
  182. shelf_view_test_->SetAnimationDuration(base::Milliseconds(1));
  183. }
  184. void TearDown() override {
  185. observer_.reset();
  186. AshTestBase::TearDown();
  187. }
  188. TestShelfObserver* observer() { return observer_.get(); }
  189. ShelfViewTestAPI* shelf_view_test() { return shelf_view_test_.get(); }
  190. private:
  191. std::unique_ptr<TestShelfObserver> observer_;
  192. std::unique_ptr<ShelfViewTestAPI> shelf_view_test_;
  193. };
  194. // A ShelfItemDelegate that tracks selections and reports a custom action.
  195. class ShelfItemSelectionTracker : public ShelfItemDelegate {
  196. public:
  197. ShelfItemSelectionTracker() : ShelfItemDelegate(ShelfID()) {}
  198. ShelfItemSelectionTracker(const ShelfItemSelectionTracker&) = delete;
  199. ShelfItemSelectionTracker& operator=(const ShelfItemSelectionTracker&) =
  200. delete;
  201. ~ShelfItemSelectionTracker() override = default;
  202. size_t item_selected_count() const { return item_selected_count_; }
  203. void set_item_selected_action(ShelfAction item_selected_action) {
  204. item_selected_action_ = item_selected_action;
  205. }
  206. // ShelfItemDelegate:
  207. void ItemSelected(std::unique_ptr<ui::Event> event,
  208. int64_t display_id,
  209. ShelfLaunchSource source,
  210. ItemSelectedCallback callback,
  211. const ItemFilterPredicate& filter_predicate) override {
  212. item_selected_count_++;
  213. std::move(callback).Run(item_selected_action_, {});
  214. }
  215. void ExecuteCommand(bool, int64_t, int32_t, int64_t) override {}
  216. void Close() override {}
  217. private:
  218. size_t item_selected_count_ = 0;
  219. ShelfAction item_selected_action_ = SHELF_ACTION_NONE;
  220. };
  221. // A ShelfItemDelegate to generate empty shelf context menu.
  222. class EmptyContextMenuBuilder : public ShelfItemDelegate {
  223. public:
  224. EmptyContextMenuBuilder() : ShelfItemDelegate(ShelfID()) {}
  225. EmptyContextMenuBuilder(const EmptyContextMenuBuilder&) = delete;
  226. EmptyContextMenuBuilder& operator=(const EmptyContextMenuBuilder&) = delete;
  227. ~EmptyContextMenuBuilder() override = default;
  228. // ShelfItemDelegate:
  229. void GetContextMenu(int64_t display_id,
  230. GetContextMenuCallback callback) override {
  231. std::move(callback).Run(std::make_unique<ui::SimpleMenuModel>(nullptr));
  232. }
  233. void ExecuteCommand(bool, int64_t, int32_t, int64_t) override {}
  234. void Close() override {}
  235. };
  236. TEST_F(ShelfObserverIconTest, AddRemove) {
  237. ShelfItem item;
  238. item.id = ShelfID("foo");
  239. item.type = TYPE_APP;
  240. EXPECT_FALSE(observer()->icon_positions_changed());
  241. const int shelf_item_index = ShelfModel::Get()->Add(
  242. item, std::make_unique<TestShelfItemDelegate>(item.id));
  243. shelf_view_test()->RunMessageLoopUntilAnimationsDone();
  244. EXPECT_TRUE(observer()->icon_positions_changed());
  245. observer()->Reset();
  246. EXPECT_FALSE(observer()->icon_positions_changed());
  247. ShelfModel::Get()->RemoveItemAt(shelf_item_index);
  248. shelf_view_test()->RunMessageLoopUntilAnimationsDone();
  249. EXPECT_TRUE(observer()->icon_positions_changed());
  250. observer()->Reset();
  251. }
  252. // Make sure creating/deleting an window on one displays notifies a
  253. // shelf on external display as well as one on primary.
  254. TEST_F(ShelfObserverIconTest, AddRemoveWithMultipleDisplays) {
  255. UpdateDisplay("500x400,500x400");
  256. observer()->Reset();
  257. Shelf* second_shelf = Shelf::ForWindow(Shell::GetAllRootWindows()[1]);
  258. TestShelfObserver second_observer(second_shelf);
  259. ShelfViewTestAPI second_shelf_test_api(
  260. second_shelf->GetShelfViewForTesting());
  261. ShelfItem item;
  262. item.id = ShelfID("foo");
  263. item.type = TYPE_APP;
  264. EXPECT_FALSE(observer()->icon_positions_changed());
  265. EXPECT_FALSE(second_observer.icon_positions_changed());
  266. // Add item and wait for all animations to finish.
  267. const int shelf_item_index = ShelfModel::Get()->Add(
  268. item, std::make_unique<TestShelfItemDelegate>(item.id));
  269. shelf_view_test()->RunMessageLoopUntilAnimationsDone();
  270. second_shelf_test_api.RunMessageLoopUntilAnimationsDone();
  271. EXPECT_TRUE(observer()->icon_positions_changed());
  272. EXPECT_TRUE(second_observer.icon_positions_changed());
  273. // Reset observer so they can track the next set of animations.
  274. observer()->Reset();
  275. ASSERT_FALSE(observer()->icon_positions_changed());
  276. second_observer.Reset();
  277. ASSERT_FALSE(second_observer.icon_positions_changed());
  278. // Remove the item, and wait for all the animations to complete.
  279. ShelfModel::Get()->RemoveItemAt(shelf_item_index);
  280. shelf_view_test()->RunMessageLoopUntilAnimationsDone();
  281. second_shelf_test_api.RunMessageLoopUntilAnimationsDone();
  282. EXPECT_TRUE(observer()->icon_positions_changed());
  283. EXPECT_TRUE(second_observer.icon_positions_changed());
  284. observer()->Reset();
  285. second_observer.Reset();
  286. }
  287. ////////////////////////////////////////////////////////////////////////////////
  288. // ShelfView tests.
  289. class ShelfViewTest : public AshTestBase {
  290. public:
  291. static const char*
  292. kTimeBetweenWindowMinimizedAndActivatedActionsHistogramName;
  293. template <typename... TaskEnvironmentTraits>
  294. explicit ShelfViewTest(TaskEnvironmentTraits&&... traits)
  295. : AshTestBase(std::forward<TaskEnvironmentTraits>(traits)...) {}
  296. ShelfViewTest(const ShelfViewTest&) = delete;
  297. ShelfViewTest& operator=(const ShelfViewTest&) = delete;
  298. ~ShelfViewTest() override = default;
  299. void SetUp() override {
  300. AshTestBase::SetUp();
  301. haptics_tracker_ = std::make_unique<HapticsTrackingTestInputController>();
  302. model_ = ShelfModel::Get();
  303. shelf_view_ = GetPrimaryShelf()->GetShelfViewForTesting();
  304. navigation_view_ = GetPrimaryShelf()
  305. ->shelf_widget()
  306. ->navigation_widget()
  307. ->GetContentsView();
  308. gfx::NativeWindow window = shelf_view_->shelf_widget()->GetNativeWindow();
  309. status_area_ = RootWindowController::ForWindow(window)
  310. ->GetStatusAreaWidget()
  311. ->GetContentsView();
  312. // The bounds should be big enough for 4 buttons.
  313. ASSERT_GE(GetPrimaryShelf()
  314. ->shelf_widget()
  315. ->hotseat_widget()
  316. ->GetWindowBoundsInScreen()
  317. .width(),
  318. 500);
  319. test_api_ = std::make_unique<ShelfViewTestAPI>(shelf_view_);
  320. test_api_->SetAnimationDuration(base::Milliseconds(1));
  321. // Add a browser shortcut shelf item, as chrome does, for testing.
  322. AddItem(TYPE_BROWSER_SHORTCUT, true);
  323. }
  324. void TearDown() override {
  325. test_api_.reset();
  326. haptics_tracker_.reset();
  327. AshTestBase::TearDown();
  328. }
  329. std::string GetNextAppId() const { return base::NumberToString(id_); }
  330. protected:
  331. // Add shelf items of various types, and optionally wait for animations.
  332. ShelfID AddItem(ShelfItemType type, bool wait_for_animations) {
  333. ShelfItem item = ShelfTestUtil::AddAppShortcutWithIcon(
  334. base::NumberToString(id_++), type, CreateImageSkiaIcon(SK_ColorRED));
  335. // Set a delegate; some tests require one to select the item.
  336. model_->ReplaceShelfItemDelegate(
  337. item.id, std::make_unique<ShelfItemSelectionTracker>());
  338. if (wait_for_animations)
  339. test_api_->RunMessageLoopUntilAnimationsDone();
  340. return item.id;
  341. }
  342. ShelfID AddAppShortcut() { return AddItem(TYPE_PINNED_APP, true); }
  343. ShelfID AddAppNoWait() { return AddItem(TYPE_APP, false); }
  344. ShelfID AddApp() { return AddItem(TYPE_APP, true); }
  345. void SetShelfItemTypeToAppShortcut(const ShelfID& id) {
  346. int index = model_->ItemIndexByID(id);
  347. DCHECK_GE(index, 0);
  348. ShelfItem item = model_->items()[index];
  349. if (item.type == TYPE_APP) {
  350. item.type = TYPE_PINNED_APP;
  351. model_->Set(index, item);
  352. }
  353. test_api_->RunMessageLoopUntilAnimationsDone();
  354. }
  355. void RemoveByID(const ShelfID& id) {
  356. model_->RemoveItemAt(model_->ItemIndexByID(id));
  357. test_api_->RunMessageLoopUntilAnimationsDone();
  358. }
  359. ShelfAppButton* GetButtonByID(const ShelfID& id) {
  360. return test_api_->GetButton(model_->ItemIndexByID(id));
  361. }
  362. ShelfItem GetItemByID(const ShelfID& id) { return *model_->ItemByID(id); }
  363. bool IsAppPinned(const ShelfID& id) const {
  364. return model_->IsAppPinned(id.app_id);
  365. }
  366. void CheckModelIDs(
  367. const std::vector<std::pair<ShelfID, views::View*>>& id_map) {
  368. size_t map_index = 0;
  369. for (size_t model_index = 0; model_index < model_->items().size();
  370. ++model_index) {
  371. ShelfItem item = model_->items()[model_index];
  372. ShelfID id = item.id;
  373. EXPECT_EQ(id_map[map_index].first, id);
  374. ++map_index;
  375. }
  376. ASSERT_EQ(map_index, id_map.size());
  377. }
  378. void VerifyShelfItemBoundsAreValid() {
  379. for (int i : shelf_view_->visible_views_indices()) {
  380. if (test_api_->GetButton(i)) {
  381. gfx::Rect shelf_view_bounds = shelf_view_->GetLocalBounds();
  382. gfx::Rect item_bounds = test_api_->GetBoundsByIndex(i);
  383. EXPECT_GE(item_bounds.x(), 0);
  384. EXPECT_GE(item_bounds.y(), 0);
  385. EXPECT_LE(item_bounds.right(), shelf_view_bounds.width());
  386. EXPECT_LE(item_bounds.bottom(), shelf_view_bounds.height());
  387. }
  388. }
  389. }
  390. // Simulate a mouse press event on the shelf's view at |view_index|.
  391. views::View* SimulateViewPressed(ShelfView::Pointer pointer, int view_index) {
  392. views::View* view = test_api_->GetViewAt(view_index);
  393. ui::MouseEvent pressed_event(ui::ET_MOUSE_PRESSED, gfx::Point(),
  394. view->GetBoundsInScreen().origin(),
  395. ui::EventTimeForNow(), 0, 0);
  396. shelf_view_->PointerPressedOnButton(view, pointer, pressed_event);
  397. return view;
  398. }
  399. // Similar to SimulateViewPressed, but the index must not be for the app list,
  400. // since the home button is not a ShelfAppButton.
  401. ShelfAppButton* SimulateButtonPressed(ShelfView::Pointer pointer,
  402. int button_index) {
  403. ShelfAppButton* button = test_api_->GetButton(button_index);
  404. EXPECT_EQ(button, SimulateViewPressed(pointer, button_index));
  405. return button;
  406. }
  407. // Simulates a single mouse click.
  408. void SimulateClick(int button_index) {
  409. ShelfAppButton* button =
  410. SimulateButtonPressed(ShelfView::MOUSE, button_index);
  411. ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, gfx::Point(),
  412. button->GetBoundsInScreen().origin(),
  413. ui::EventTimeForNow(), 0, 0);
  414. button->NotifyClick(release_event);
  415. button->OnMouseReleased(release_event);
  416. }
  417. // Simulates the second click of a double click.
  418. void SimulateDoubleClick(int button_index) {
  419. ShelfAppButton* button =
  420. SimulateButtonPressed(ShelfView::MOUSE, button_index);
  421. ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, gfx::Point(),
  422. button->GetBoundsInScreen().origin(),
  423. ui::EventTimeForNow(), ui::EF_IS_DOUBLE_CLICK,
  424. 0);
  425. button->NotifyClick(release_event);
  426. button->OnMouseReleased(release_event);
  427. }
  428. void DoDrag(int dist_x,
  429. int dist_y,
  430. views::View* button,
  431. ShelfView::Pointer pointer,
  432. views::View* to) {
  433. ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED, gfx::Point(dist_x, dist_y),
  434. to->GetBoundsInScreen().origin(),
  435. ui::EventTimeForNow(), 0, 0);
  436. shelf_view_->PointerDraggedOnButton(button, pointer, drag_event);
  437. }
  438. /*
  439. * Trigger ContinueDrag of the shelf
  440. * The argument progressively means whether to simulate the drag progress (a
  441. * series of changes of the posistion of dragged item), like the normal user
  442. * drag behavior.
  443. */
  444. void ContinueDrag(views::View* button,
  445. ShelfView::Pointer pointer,
  446. int from_index,
  447. int to_index,
  448. bool progressively) {
  449. views::View* to = test_api_->GetViewAt(to_index);
  450. views::View* from = test_api_->GetViewAt(from_index);
  451. gfx::Rect to_rect = to->GetMirroredBounds();
  452. gfx::Rect from_rect = from->GetMirroredBounds();
  453. int dist_x = to_rect.x() - from_rect.x();
  454. int dist_y = to_rect.y() - from_rect.y();
  455. if (progressively) {
  456. int sgn = dist_x > 0 ? 1 : -1;
  457. dist_x = abs(dist_x);
  458. for (; dist_x; dist_x -= std::min(10, dist_x))
  459. DoDrag(sgn * std::min(10, abs(dist_x)), 0, button, pointer, to);
  460. } else {
  461. DoDrag(dist_x, dist_y, button, pointer, to);
  462. }
  463. }
  464. /*
  465. * Simulate drag operation.
  466. * Argument progressively means whether to simulate the drag progress (a
  467. * series of changes of the posistion of dragged item) like the behavior of
  468. * user drags.
  469. */
  470. views::View* SimulateDrag(ShelfView::Pointer pointer,
  471. int button_index,
  472. int destination_index,
  473. bool progressively) {
  474. views::View* button = SimulateViewPressed(pointer, button_index);
  475. if (!progressively) {
  476. ContinueDrag(button, pointer, button_index, destination_index, false);
  477. } else if (button_index < destination_index) {
  478. for (int cur_index = button_index + 1; cur_index <= destination_index;
  479. cur_index++)
  480. ContinueDrag(button, pointer, cur_index - 1, cur_index, true);
  481. } else if (button_index > destination_index) {
  482. for (int cur_index = button_index - 1; cur_index >= destination_index;
  483. cur_index--)
  484. ContinueDrag(button, pointer, cur_index + 1, cur_index, true);
  485. }
  486. return button;
  487. }
  488. void DragAndVerify(
  489. int from,
  490. int to,
  491. ShelfView* shelf_view,
  492. const std::vector<std::pair<ShelfID, views::View*>>& expected_id_map) {
  493. views::View* dragged_button =
  494. SimulateDrag(ShelfView::MOUSE, from, to, true);
  495. shelf_view->PointerReleasedOnButton(dragged_button, ShelfView::MOUSE,
  496. false);
  497. test_api_->RunMessageLoopUntilAnimationsDone();
  498. ASSERT_NO_FATAL_FAILURE(CheckModelIDs(expected_id_map));
  499. }
  500. void SetupForDragTest(std::vector<std::pair<ShelfID, views::View*>>* id_map) {
  501. // Initialize |id_map| with the automatically-created shelf buttons.
  502. for (size_t i = 0; i < model_->items().size(); ++i) {
  503. ShelfAppButton* button = test_api_->GetButton(i);
  504. id_map->push_back(std::make_pair(model_->items()[i].id, button));
  505. }
  506. ASSERT_NO_FATAL_FAILURE(CheckModelIDs(*id_map));
  507. // Add 5 app shelf buttons for testing.
  508. for (int i = 0; i < 5; ++i) {
  509. ShelfID id = AddAppShortcut();
  510. // The browser shortcut is located at index 0. So we should start to add
  511. // app shortcuts at index 1.
  512. id_map->insert(id_map->begin() + i + 1,
  513. std::make_pair(id, GetButtonByID(id)));
  514. }
  515. ASSERT_NO_FATAL_FAILURE(CheckModelIDs(*id_map));
  516. }
  517. // Returns the item's ShelfID at |index|.
  518. ShelfID GetItemId(int index) const {
  519. DCHECK_GE(index, 0);
  520. return model_->items()[index].id;
  521. }
  522. // Returns the center point of a button. Helper function for event generators.
  523. gfx::Point GetButtonCenter(const ShelfID& button_id) {
  524. return GetButtonCenter(GetButtonByID(button_id));
  525. }
  526. gfx::Point GetButtonCenter(const ShelfAppButton* button) {
  527. return button->GetBoundsInScreen().CenterPoint();
  528. }
  529. int GetHapticTickEventsCount() const {
  530. return haptics_tracker_->GetSentHapticCount(
  531. ui::HapticTouchpadEffect::kTick,
  532. ui::HapticTouchpadEffectStrength::kMedium);
  533. }
  534. ShelfModel* model_ = nullptr;
  535. ShelfView* shelf_view_ = nullptr;
  536. views::View* navigation_view_ = nullptr;
  537. views::View* status_area_ = nullptr;
  538. int id_ = 0;
  539. // Used to track haptics events sent during drag.
  540. std::unique_ptr<HapticsTrackingTestInputController> haptics_tracker_;
  541. std::unique_ptr<ShelfViewTestAPI> test_api_;
  542. };
  543. class LtrRtlShelfViewTest : public ShelfViewTest,
  544. public testing::WithParamInterface<bool> {
  545. public:
  546. LtrRtlShelfViewTest() : scoped_locale_(GetParam() ? "he" : "") {}
  547. LtrRtlShelfViewTest(const LtrRtlShelfViewTest&) = delete;
  548. LtrRtlShelfViewTest& operator=(const LtrRtlShelfViewTest&) = delete;
  549. ~LtrRtlShelfViewTest() override = default;
  550. bool IsRtlEnabled() const { return GetParam(); }
  551. private:
  552. // Restores locale to the default when destructor is called.
  553. base::test::ScopedRestoreICUDefaultLocale scoped_locale_;
  554. };
  555. INSTANTIATE_TEST_SUITE_P(All, LtrRtlShelfViewTest, testing::Bool());
  556. const char*
  557. ShelfViewTest::kTimeBetweenWindowMinimizedAndActivatedActionsHistogramName =
  558. ShelfButtonPressedMetricTracker::
  559. kTimeBetweenWindowMinimizedAndActivatedActionsHistogramName;
  560. class ShelfViewDragToPinTest : public ShelfViewTest {
  561. public:
  562. ShelfViewDragToPinTest() {
  563. feature_list_.InitAndEnableFeature(features::kDragUnpinnedAppToPin);
  564. }
  565. ~ShelfViewDragToPinTest() override = default;
  566. private:
  567. base::test::ScopedFeatureList feature_list_;
  568. };
  569. TEST_P(LtrRtlShelfViewTest, VisibleShelfItemsBounds) {
  570. // Add 3 pinned apps, and a normal app.
  571. AddAppShortcut();
  572. AddAppShortcut();
  573. AddAppShortcut();
  574. const auto app_id = AddApp();
  575. EXPECT_EQ(static_cast<size_t>(model_->item_count()),
  576. shelf_view_->number_of_visible_apps());
  577. const gfx::Rect visible_items_bounds =
  578. test_api_->visible_shelf_item_bounds_union();
  579. // Pin the app with `app_id` and expect that the visible items bounds union
  580. // remains the same.
  581. SetShelfItemTypeToAppShortcut(app_id);
  582. EXPECT_EQ(static_cast<size_t>(model_->item_count()),
  583. shelf_view_->number_of_visible_apps());
  584. EXPECT_EQ(visible_items_bounds, test_api_->visible_shelf_item_bounds_union());
  585. }
  586. // Checks that shelf view contents are considered in the correct drag group.
  587. TEST_P(LtrRtlShelfViewTest, EnforceDragType) {
  588. EXPECT_TRUE(test_api_->SameDragType(TYPE_APP, TYPE_APP));
  589. EXPECT_FALSE(test_api_->SameDragType(TYPE_APP, TYPE_PINNED_APP));
  590. EXPECT_FALSE(test_api_->SameDragType(TYPE_APP, TYPE_BROWSER_SHORTCUT));
  591. EXPECT_FALSE(
  592. test_api_->SameDragType(TYPE_APP, TYPE_UNPINNED_BROWSER_SHORTCUT));
  593. EXPECT_TRUE(test_api_->SameDragType(TYPE_PINNED_APP, TYPE_PINNED_APP));
  594. EXPECT_TRUE(test_api_->SameDragType(TYPE_PINNED_APP, TYPE_BROWSER_SHORTCUT));
  595. EXPECT_FALSE(
  596. test_api_->SameDragType(TYPE_PINNED_APP, TYPE_UNPINNED_BROWSER_SHORTCUT));
  597. EXPECT_TRUE(
  598. test_api_->SameDragType(TYPE_BROWSER_SHORTCUT, TYPE_BROWSER_SHORTCUT));
  599. EXPECT_TRUE(test_api_->SameDragType(TYPE_UNPINNED_BROWSER_SHORTCUT,
  600. TYPE_UNPINNED_BROWSER_SHORTCUT));
  601. // No test for TYPE_BROWSER_SHORTCUT and TYPE_UNPINNED_BROWSER_SHORTCUT,
  602. // because they should be mutually exclusive.
  603. }
  604. // Check that model changes are handled correctly while a shelf icon is being
  605. // dragged.
  606. TEST_P(LtrRtlShelfViewTest, ModelChangesWhileDragging) {
  607. std::vector<std::pair<ShelfID, views::View*>> id_map;
  608. SetupForDragTest(&id_map);
  609. // Dragging browser shortcut at index 1.
  610. EXPECT_TRUE(model_->items()[0].type == TYPE_BROWSER_SHORTCUT);
  611. views::View* dragged_button = SimulateDrag(ShelfView::MOUSE, 0, 2, false);
  612. EXPECT_EQ(1, GetHapticTickEventsCount());
  613. std::rotate(id_map.begin(), id_map.begin() + 1, id_map.begin() + 3);
  614. ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
  615. shelf_view_->PointerReleasedOnButton(dragged_button, ShelfView::MOUSE, false);
  616. EXPECT_EQ(1, GetHapticTickEventsCount());
  617. EXPECT_TRUE(model_->items()[2].type == TYPE_BROWSER_SHORTCUT);
  618. test_api_->RunMessageLoopUntilAnimationsDone();
  619. // Dragging changes model order.
  620. dragged_button = SimulateDrag(ShelfView::MOUSE, 0, 2, false);
  621. EXPECT_EQ(2, GetHapticTickEventsCount());
  622. std::rotate(id_map.begin(), id_map.begin() + 1, id_map.begin() + 3);
  623. ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
  624. // Cancelling the drag operation restores previous order.
  625. shelf_view_->PointerReleasedOnButton(dragged_button, ShelfView::MOUSE, true);
  626. test_api_->RunMessageLoopUntilAnimationsDone();
  627. EXPECT_EQ(2, GetHapticTickEventsCount());
  628. std::rotate(id_map.begin(), id_map.begin() + 2, id_map.begin() + 3);
  629. ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
  630. // Deleting an item keeps the remaining intact.
  631. dragged_button = SimulateDrag(ShelfView::MOUSE, 0, 2, false);
  632. EXPECT_EQ(3, GetHapticTickEventsCount());
  633. // The dragged view has been moved to index 2 during drag.
  634. std::rotate(id_map.begin(), id_map.begin() + 1, id_map.begin() + 3);
  635. ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
  636. model_->RemoveItemAt(2);
  637. id_map.erase(id_map.begin() + 2);
  638. ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
  639. shelf_view_->PointerReleasedOnButton(dragged_button, ShelfView::MOUSE, false);
  640. // Waits until app removal animation finishes.
  641. test_api_->RunMessageLoopUntilAnimationsDone();
  642. // Adding a shelf item cancels the drag and respects the order.
  643. dragged_button = SimulateDrag(ShelfView::MOUSE, 0, 2, false);
  644. EXPECT_EQ(4, GetHapticTickEventsCount());
  645. ShelfID new_id = AddAppShortcut();
  646. id_map.insert(id_map.begin() + 5,
  647. std::make_pair(new_id, GetButtonByID(new_id)));
  648. ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
  649. shelf_view_->PointerReleasedOnButton(dragged_button, ShelfView::MOUSE, false);
  650. EXPECT_EQ(4, GetHapticTickEventsCount());
  651. }
  652. // Check that 2nd drag from the other pointer would be ignored.
  653. TEST_P(LtrRtlShelfViewTest, SimultaneousDrag) {
  654. std::vector<std::pair<ShelfID, views::View*>> id_map;
  655. SetupForDragTest(&id_map);
  656. // Start a mouse drag.
  657. views::View* dragged_button_mouse =
  658. SimulateDrag(ShelfView::MOUSE, 2, 4, false);
  659. EXPECT_EQ(1, GetHapticTickEventsCount());
  660. std::rotate(id_map.begin() + 2, id_map.begin() + 3, id_map.begin() + 5);
  661. ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
  662. // Attempt a touch drag before the mouse drag finishes.
  663. views::View* dragged_button_touch =
  664. SimulateDrag(ShelfView::TOUCH, 5, 3, false);
  665. EXPECT_EQ(1, GetHapticTickEventsCount());
  666. // Nothing changes since 2nd drag is ignored.
  667. ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
  668. // Finish the mouse drag.
  669. shelf_view_->PointerReleasedOnButton(dragged_button_mouse, ShelfView::MOUSE,
  670. false);
  671. ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
  672. // Now start a touch drag.
  673. dragged_button_touch = SimulateDrag(ShelfView::TOUCH, 5, 3, false);
  674. std::rotate(id_map.begin() + 4, id_map.begin() + 5, id_map.begin() + 6);
  675. ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
  676. // And attempt a mouse drag before the touch drag finishes.
  677. dragged_button_mouse = SimulateDrag(ShelfView::MOUSE, 2, 3, false);
  678. // Nothing changes since 2nd drag is ignored.
  679. ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
  680. shelf_view_->PointerReleasedOnButton(dragged_button_touch, ShelfView::TOUCH,
  681. false);
  682. ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
  683. EXPECT_EQ(1, GetHapticTickEventsCount());
  684. }
  685. // Ensure that the behavior of pinning and unpinning by dragging works as
  686. // expected.
  687. TEST_F(ShelfViewDragToPinTest, DragAppsToPinAndUnpin) {
  688. std::vector<std::pair<ShelfID, views::View*>> id_map;
  689. SetupForDragTest(&id_map);
  690. size_t pinned_apps_size = id_map.size();
  691. const ShelfID open_app_id = AddApp();
  692. id_map.emplace_back(open_app_id, GetButtonByID(open_app_id));
  693. // Run the pinned app at index 1.
  694. ShelfItem item = model_->items()[1];
  695. item.status = STATUS_RUNNING;
  696. model_->Set(1, item);
  697. id_map[1].second = GetButtonByID(item.id);
  698. ASSERT_TRUE(static_cast<ShelfAppButton*>(id_map[1].second)->state() &
  699. ShelfAppButton::STATE_RUNNING);
  700. ASSERT_FALSE(static_cast<ShelfAppButton*>(id_map[2].second)->state() &
  701. ShelfAppButton::STATE_RUNNING);
  702. ASSERT_TRUE(GetButtonByID(open_app_id)->state() &
  703. ShelfAppButton::STATE_RUNNING);
  704. EXPECT_EQ(test_api_->GetSeparatorIndex(), pinned_apps_size - 1);
  705. // Drag the app at index 1 and move it to the end of the shelf. With separator
  706. // available and the app is dragged to the unpinned app side, the dragged open
  707. // app should be unpinned and moved to the released position.
  708. views::View* dragged_button =
  709. SimulateDrag(ShelfView::MOUSE, 1, id_map.size() - 1, false);
  710. EXPECT_EQ(1, GetHapticTickEventsCount());
  711. std::rotate(id_map.begin() + 1, id_map.begin() + 2, id_map.end());
  712. ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
  713. shelf_view_->PointerReleasedOnButton(dragged_button, ShelfView::MOUSE, false);
  714. test_api_->RunMessageLoopUntilAnimationsDone();
  715. EXPECT_EQ(1, GetHapticTickEventsCount());
  716. EXPECT_FALSE(IsAppPinned(id_map.back().first));
  717. --pinned_apps_size;
  718. // Drag the app at index 2 and move it to the end of the shelf. With separator
  719. // available and the app is dragged to the unpinned app side, the dragged app
  720. // with no running instance should be unpinned and removed from shelf.
  721. dragged_button = SimulateDrag(ShelfView::MOUSE, 2, id_map.size() - 1, false);
  722. EXPECT_EQ(2, GetHapticTickEventsCount());
  723. shelf_view_->PointerReleasedOnButton(dragged_button, ShelfView::MOUSE, false);
  724. id_map.erase(id_map.begin() + 2);
  725. EXPECT_EQ(id_map.size(), model_->items().size());
  726. ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
  727. --pinned_apps_size;
  728. EXPECT_EQ(2, GetHapticTickEventsCount());
  729. // Drag an app in unpinned app side and move it to the beginning of the shelf.
  730. // With separator available and the app is dragged to the pinned app side, the
  731. // dragged app should be pinned and moved to the released position.
  732. dragged_button = SimulateDrag(ShelfView::MOUSE, id_map.size() - 2, 0, false);
  733. EXPECT_EQ(3, GetHapticTickEventsCount());
  734. std::rotate(id_map.rbegin() + 1, id_map.rbegin() + 2, id_map.rend());
  735. ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
  736. shelf_view_->PointerReleasedOnButton(dragged_button, ShelfView::MOUSE, false);
  737. test_api_->RunMessageLoopUntilAnimationsDone();
  738. EXPECT_EQ(3, GetHapticTickEventsCount());
  739. EXPECT_TRUE(IsAppPinned(id_map[0].first));
  740. ++pinned_apps_size;
  741. EXPECT_EQ(test_api_->GetSeparatorIndex(), pinned_apps_size - 1);
  742. }
  743. // Check that the Browser Shortcut will not be dragged to the unpinned app side
  744. // or be unpinned by dragging.
  745. TEST_F(ShelfViewDragToPinTest, BlockBrowserShortcutFromUnpinningByDragging) {
  746. std::vector<std::pair<ShelfID, views::View*>> id_map;
  747. SetupForDragTest(&id_map);
  748. const size_t pinned_apps_size = id_map.size();
  749. const ShelfID open_app_id = AddApp();
  750. id_map.emplace_back(open_app_id, GetButtonByID(open_app_id));
  751. EXPECT_TRUE(model_->items()[0].type == TYPE_BROWSER_SHORTCUT);
  752. EXPECT_EQ(test_api_->GetSeparatorIndex(), pinned_apps_size - 1);
  753. // Dragging the browser shortcut to the unpinned app side should not make it
  754. // unpinned.
  755. views::View* dragged_button =
  756. SimulateDrag(ShelfView::MOUSE, 0, id_map.size() - 1, false);
  757. EXPECT_EQ(1, GetHapticTickEventsCount());
  758. std::rotate(id_map.begin(), id_map.begin() + 1,
  759. id_map.begin() + pinned_apps_size);
  760. ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
  761. // When drag pointer released, the browser shortcut should be the last app in
  762. // the pinned app side.
  763. shelf_view_->PointerReleasedOnButton(dragged_button, ShelfView::MOUSE, false);
  764. EXPECT_EQ(model_->items()[pinned_apps_size - 1].type, TYPE_BROWSER_SHORTCUT);
  765. EXPECT_EQ(1, GetHapticTickEventsCount());
  766. }
  767. // Check that separator index updates as expected when a drag view is dragged
  768. // over it.
  769. TEST_F(ShelfViewDragToPinTest, DragAppAroundSeparator) {
  770. std::vector<std::pair<ShelfID, views::View*>> id_map;
  771. SetupForDragTest(&id_map);
  772. const size_t pinned_apps_size = id_map.size();
  773. const ShelfID open_app_id = AddApp();
  774. id_map.emplace_back(open_app_id, GetButtonByID(open_app_id));
  775. EXPECT_EQ(test_api_->GetSeparatorIndex(), pinned_apps_size - 1);
  776. const int button_width =
  777. GetButtonByID(open_app_id)->GetBoundsInScreen().width();
  778. ui::test::EventGenerator* generator = GetEventGenerator();
  779. // The test makes some assumptions that the shelf is bottom aligned.
  780. ASSERT_EQ(shelf_view_->shelf()->alignment(), ShelfAlignment::kBottom);
  781. // Drag an unpinned open app that is beside the separator around and check
  782. // that the separator is correctly placed.
  783. ASSERT_EQ(static_cast<size_t>(model_->ItemIndexByID(open_app_id)),
  784. test_api_->GetSeparatorIndex().value() + 1);
  785. gfx::Point unpinned_app_location =
  786. GetButtonCenter(GetButtonByID(open_app_id));
  787. generator->set_current_screen_location(unpinned_app_location);
  788. generator->PressLeftButton();
  789. // Drag the mouse slightly to the left. The dragged app will stay at the same
  790. // index but the separator will move to the right.
  791. generator->MoveMouseBy(-button_width / 4, 0);
  792. EXPECT_EQ(1, GetHapticTickEventsCount());
  793. // In this case, the separator is moved to the end of the shelf so it is set
  794. // invisible and the |separator_index_| will be updated to nullopt.
  795. EXPECT_FALSE(test_api_->IsSeparatorVisible());
  796. EXPECT_FALSE(test_api_->GetSeparatorIndex().has_value());
  797. // Drag the mouse slightly to the right where the dragged app will stay at the
  798. // same index.
  799. generator->MoveMouseBy(button_width / 2, 0);
  800. // In this case, because the dragged app is not released or pinned yet,
  801. // dragging it back to its original place will show the separator again.
  802. EXPECT_EQ(test_api_->GetSeparatorIndex(), pinned_apps_size - 1);
  803. generator->ReleaseLeftButton();
  804. EXPECT_EQ(1, GetHapticTickEventsCount());
  805. // Drag an pinned app that is beside the separator around and check that the
  806. // separator is correctly placed. Check that the dragged app is not a browser
  807. // shortcut, which can not be dragged across the separator.
  808. ASSERT_NE(model_->items()[pinned_apps_size - 1].type, TYPE_BROWSER_SHORTCUT);
  809. ASSERT_EQ(static_cast<size_t>(
  810. model_->ItemIndexByID(id_map[pinned_apps_size - 1].first)),
  811. test_api_->GetSeparatorIndex());
  812. gfx::Point pinned_app_location =
  813. GetButtonCenter(id_map[pinned_apps_size - 1].first);
  814. generator->set_current_screen_location(pinned_app_location);
  815. generator->PressLeftButton();
  816. // Drag the mouse slightly to the right. The dragged app will stay at the same
  817. // index but the separator will move to the left.
  818. generator->MoveMouseBy(button_width / 4, 0);
  819. EXPECT_EQ(2, GetHapticTickEventsCount());
  820. EXPECT_EQ(test_api_->GetSeparatorIndex(), pinned_apps_size - 2);
  821. // Drag the mouse slightly to the left. The dragged app will stay at the same
  822. // index but the separator will move to the right.
  823. generator->MoveMouseBy(-button_width / 2, 0);
  824. EXPECT_EQ(test_api_->GetSeparatorIndex(), pinned_apps_size - 1);
  825. generator->ReleaseLeftButton();
  826. EXPECT_EQ(2, GetHapticTickEventsCount());
  827. }
  828. // Ensure that clicking on one item and then dragging another works as expected.
  829. TEST_P(LtrRtlShelfViewTest, ClickOneDragAnother) {
  830. std::vector<std::pair<ShelfID, views::View*>> id_map;
  831. SetupForDragTest(&id_map);
  832. // A click on the item at index 1 is simulated.
  833. SimulateClick(1);
  834. // Dragging the browser item at index 0 should change the model order.
  835. EXPECT_TRUE(model_->items()[0].type == TYPE_BROWSER_SHORTCUT);
  836. views::View* dragged_button = SimulateDrag(ShelfView::MOUSE, 0, 2, false);
  837. EXPECT_EQ(1, GetHapticTickEventsCount());
  838. std::rotate(id_map.begin(), id_map.begin() + 1, id_map.begin() + 3);
  839. ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
  840. shelf_view_->PointerReleasedOnButton(dragged_button, ShelfView::MOUSE, false);
  841. EXPECT_TRUE(model_->items()[2].type == TYPE_BROWSER_SHORTCUT);
  842. EXPECT_EQ(1, GetHapticTickEventsCount());
  843. }
  844. // Tests that double-clicking an item does not activate it twice.
  845. TEST_P(LtrRtlShelfViewTest, ClickingTwiceActivatesOnce) {
  846. // Watch for selection of the browser shortcut.
  847. ShelfItemSelectionTracker* selection_tracker = new ShelfItemSelectionTracker;
  848. model_->ReplaceShelfItemDelegate(model_->items()[0].id,
  849. base::WrapUnique(selection_tracker));
  850. // A single click selects the item, but a double-click does not.
  851. EXPECT_EQ(0u, selection_tracker->item_selected_count());
  852. SimulateClick(0);
  853. EXPECT_EQ(1u, selection_tracker->item_selected_count());
  854. SimulateDoubleClick(0);
  855. EXPECT_EQ(1u, selection_tracker->item_selected_count());
  856. EXPECT_EQ(0, GetHapticTickEventsCount());
  857. }
  858. // Check that very small mouse drags do not prevent shelf item selection.
  859. TEST_P(LtrRtlShelfViewTest, ClickAndMoveSlightly) {
  860. std::vector<std::pair<ShelfID, views::View*>> id_map;
  861. SetupForDragTest(&id_map);
  862. ShelfID shelf_id = (id_map.begin() + 2)->first;
  863. views::View* button = (id_map.begin() + 2)->second;
  864. // Install a ShelfItemDelegate that tracks when the shelf item is selected.
  865. ShelfItemSelectionTracker* selection_tracker = new ShelfItemSelectionTracker;
  866. model_->ReplaceShelfItemDelegate(
  867. shelf_id, base::WrapUnique<ShelfItemSelectionTracker>(selection_tracker));
  868. gfx::Vector2d press_offset(5, 30);
  869. gfx::Point press_location = gfx::Point() + press_offset;
  870. gfx::Point press_location_in_screen =
  871. button->GetBoundsInScreen().origin() + press_offset;
  872. ui::MouseEvent click_event(ui::ET_MOUSE_PRESSED, press_location,
  873. press_location_in_screen, ui::EventTimeForNow(),
  874. ui::EF_LEFT_MOUSE_BUTTON, 0);
  875. button->OnMousePressed(click_event);
  876. EXPECT_EQ(0u, selection_tracker->item_selected_count());
  877. ui::MouseEvent drag_event1(
  878. ui::ET_MOUSE_DRAGGED, press_location + gfx::Vector2d(0, 1),
  879. press_location_in_screen + gfx::Vector2d(0, 1), ui::EventTimeForNow(),
  880. ui::EF_LEFT_MOUSE_BUTTON, 0);
  881. button->OnMouseDragged(drag_event1);
  882. ui::MouseEvent drag_event2(
  883. ui::ET_MOUSE_DRAGGED, press_location + gfx::Vector2d(-1, 0),
  884. press_location_in_screen + gfx::Vector2d(-1, 0), ui::EventTimeForNow(),
  885. ui::EF_LEFT_MOUSE_BUTTON, 0);
  886. button->OnMouseDragged(drag_event2);
  887. EXPECT_EQ(0u, selection_tracker->item_selected_count());
  888. ui::MouseEvent release_event(
  889. ui::ET_MOUSE_RELEASED, press_location + gfx::Vector2d(-1, 0),
  890. press_location_in_screen + gfx::Vector2d(-1, 0), ui::EventTimeForNow(),
  891. ui::EF_LEFT_MOUSE_BUTTON, 0);
  892. button->OnMouseReleased(release_event);
  893. EXPECT_EQ(1u, selection_tracker->item_selected_count());
  894. EXPECT_EQ(0, GetHapticTickEventsCount());
  895. }
  896. // Confirm that item status changes are reflected in the buttons.
  897. TEST_P(LtrRtlShelfViewTest, ShelfItemStatus) {
  898. // All buttons should be visible.
  899. ASSERT_EQ(test_api_->GetButtonCount(), shelf_view_->number_of_visible_apps());
  900. // Add platform app button.
  901. ShelfID last_added = AddApp();
  902. ShelfItem item = GetItemByID(last_added);
  903. int index = model_->ItemIndexByID(last_added);
  904. ShelfAppButton* button = GetButtonByID(last_added);
  905. ASSERT_EQ(ShelfAppButton::STATE_RUNNING, button->state());
  906. item.status = STATUS_ATTENTION;
  907. model_->Set(index, item);
  908. ASSERT_EQ(ShelfAppButton::STATE_ATTENTION, button->state());
  909. }
  910. // Test what drag movements will rip an item off the shelf.
  911. TEST_P(LtrRtlShelfViewTest, ShelfRipOff) {
  912. ui::test::EventGenerator* generator = GetEventGenerator();
  913. // The test makes some assumptions that the shelf is bottom aligned.
  914. ASSERT_EQ(shelf_view_->shelf()->alignment(), ShelfAlignment::kBottom);
  915. // The rip off threshold. Taken from |kRipOffDistance| in shelf_view.cc.
  916. const int kRipOffDistance = 48;
  917. // Add two apps on the main shelf.
  918. ShelfID first_app_id = AddAppShortcut();
  919. ShelfID second_app_id = AddAppShortcut();
  920. // Verify that dragging an app off the shelf will trigger the app getting
  921. // ripped off, unless the distance is less than |kRipOffDistance|.
  922. gfx::Point first_app_location = GetButtonCenter(GetButtonByID(first_app_id));
  923. generator->set_current_screen_location(first_app_location);
  924. generator->PressLeftButton();
  925. // Drag the mouse to just off the shelf.
  926. generator->MoveMouseBy(0, -ShelfConfig::Get()->shelf_size() / 2 - 1);
  927. EXPECT_EQ(1, GetHapticTickEventsCount());
  928. EXPECT_FALSE(test_api_->IsRippedOffFromShelf());
  929. // Drag the mouse past the rip off threshold.
  930. generator->MoveMouseBy(0, -kRipOffDistance);
  931. EXPECT_TRUE(test_api_->IsRippedOffFromShelf());
  932. // Drag the mouse back to the original position, so that the app does not get
  933. // deleted.
  934. generator->MoveMouseTo(first_app_location);
  935. generator->ReleaseLeftButton();
  936. EXPECT_EQ(1, GetHapticTickEventsCount());
  937. EXPECT_FALSE(test_api_->IsRippedOffFromShelf());
  938. }
  939. // Test that rip off drag can gracefully be canceled.
  940. TEST_P(LtrRtlShelfViewTest, ShelfRipOffCancel) {
  941. // The test makes some assumptions that the shelf is bottom aligned.
  942. ASSERT_EQ(shelf_view_->shelf()->alignment(), ShelfAlignment::kBottom);
  943. // The rip off threshold. Taken from |kRipOffDistance| in shelf_view.cc.
  944. constexpr int kRipOffDistance = 48;
  945. // Add two apps on the main shelf.
  946. ShelfID first_app_id = AddAppShortcut();
  947. AddAppShortcut();
  948. // Cache the shelf state - test will verify that shelf items are not changed
  949. // if rip off drag gets canceled.
  950. std::vector<std::pair<ShelfID, views::View*>> id_map;
  951. for (size_t i = 0; i < model_->items().size(); ++i) {
  952. ShelfAppButton* button = test_api_->GetButton(i);
  953. id_map.emplace_back(model_->items()[i].id, button);
  954. }
  955. // Verify that dragging an app off the shelf will trigger the app getting
  956. // ripped off, unless the distance is less than |kRipOffDistance|.
  957. const ShelfAppButton* dragged_button = GetButtonByID(first_app_id);
  958. gfx::Point first_app_location = GetButtonCenter(dragged_button);
  959. ui::test::EventGenerator* generator = GetEventGenerator();
  960. generator->set_current_screen_location(first_app_location);
  961. generator->PressLeftButton();
  962. // Drag the mouse to just off the shelf.
  963. generator->MoveMouseBy(0, -ShelfConfig::Get()->shelf_size() / 2 - 1);
  964. EXPECT_EQ(1, GetHapticTickEventsCount());
  965. EXPECT_FALSE(test_api_->IsRippedOffFromShelf());
  966. // Drag the mouse past the rip off threshold.
  967. generator->MoveMouseBy(0, -kRipOffDistance);
  968. EXPECT_TRUE(test_api_->IsRippedOffFromShelf());
  969. shelf_view_->PointerReleasedOnButton(dragged_button, ShelfView::MOUSE, true);
  970. generator->ReleaseLeftButton();
  971. EXPECT_EQ(1, GetHapticTickEventsCount());
  972. EXPECT_FALSE(test_api_->IsRippedOffFromShelf());
  973. ASSERT_NO_FATAL_FAILURE(CheckModelIDs(id_map));
  974. }
  975. // Tests that drag and drop a pinned running app will unpin it.
  976. TEST_P(LtrRtlShelfViewTest, DragAndDropPinnedRunningApp) {
  977. ui::test::EventGenerator* generator = GetEventGenerator();
  978. // The test makes some assumptions that the shelf is bottom aligned.
  979. ASSERT_EQ(shelf_view_->shelf()->alignment(), ShelfAlignment::kBottom);
  980. // The rip off threshold. Taken from |kRipOffDistance| in shelf_view.cc.
  981. constexpr int kRipOffDistance = 48;
  982. const ShelfID id = AddApp();
  983. // Added only one app here, the index of the app will not change after drag
  984. // and drop.
  985. int index = model_->ItemIndexByID(id);
  986. ShelfItem item = GetItemByID(id);
  987. EXPECT_EQ(STATUS_RUNNING, item.status);
  988. model_->PinExistingItemWithID(id.app_id);
  989. EXPECT_TRUE(IsAppPinned(GetItemId(index)));
  990. gfx::Point app_location = GetButtonCenter(GetButtonByID(id));
  991. generator->set_current_screen_location(app_location);
  992. generator->PressLeftButton();
  993. generator->MoveMouseBy(0, -ShelfConfig::Get()->shelf_size() / 2 - 1);
  994. EXPECT_EQ(1, GetHapticTickEventsCount());
  995. EXPECT_FALSE(test_api_->IsRippedOffFromShelf());
  996. generator->MoveMouseBy(0, -kRipOffDistance);
  997. EXPECT_TRUE(test_api_->IsRippedOffFromShelf());
  998. generator->ReleaseLeftButton();
  999. EXPECT_FALSE(IsAppPinned(GetItemId(index)));
  1000. EXPECT_EQ(1, GetHapticTickEventsCount());
  1001. }
  1002. // Double click an app while animating drag icon drop.
  1003. TEST_P(LtrRtlShelfViewTest, ActivateAppButtonDuringDropAnimation) {
  1004. ui::test::EventGenerator* generator = GetEventGenerator();
  1005. // Enable animations, as the test verifies behavior while a drop animation is
  1006. // in progress.
  1007. ui::ScopedAnimationDurationScaleMode regular_animations(
  1008. ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
  1009. // The test makes some assumptions that the shelf is bottom aligned.
  1010. ASSERT_EQ(shelf_view_->shelf()->alignment(), ShelfAlignment::kBottom);
  1011. const ShelfID drag_item_id = AddApp();
  1012. const ShelfID activated_item_id = AddApp();
  1013. // Watch for selection of the browser shortcut.
  1014. auto owned_selection_tracker = std::make_unique<ShelfItemSelectionTracker>();
  1015. ShelfItemSelectionTracker* selection_tracker = owned_selection_tracker.get();
  1016. model_->ReplaceShelfItemDelegate(activated_item_id,
  1017. std::move(owned_selection_tracker));
  1018. generator->set_current_screen_location(
  1019. GetButtonCenter(GetButtonByID(drag_item_id)));
  1020. generator->PressLeftButton();
  1021. generator->MoveMouseBy(0, -ShelfConfig::Get()->shelf_size() / 2 - 1);
  1022. EXPECT_EQ(1, GetHapticTickEventsCount());
  1023. generator->ReleaseLeftButton();
  1024. EXPECT_EQ(1, GetHapticTickEventsCount());
  1025. generator->set_current_screen_location(
  1026. GetButtonCenter(GetButtonByID(activated_item_id)));
  1027. generator->DoubleClickLeftButton();
  1028. EXPECT_EQ(1, GetHapticTickEventsCount());
  1029. EXPECT_EQ(1u, selection_tracker->item_selected_count());
  1030. VerifyShelfItemBoundsAreValid();
  1031. }
  1032. // Confirm that item status changes are reflected in the buttons
  1033. // for platform apps.
  1034. TEST_P(LtrRtlShelfViewTest, ShelfItemStatusPlatformApp) {
  1035. // All buttons should be visible.
  1036. ASSERT_EQ(test_api_->GetButtonCount(), shelf_view_->number_of_visible_apps());
  1037. // Add platform app button.
  1038. ShelfID last_added = AddApp();
  1039. ShelfItem item = GetItemByID(last_added);
  1040. int index = model_->ItemIndexByID(last_added);
  1041. ShelfAppButton* button = GetButtonByID(last_added);
  1042. ASSERT_EQ(ShelfAppButton::STATE_RUNNING, button->state());
  1043. item.status = STATUS_ATTENTION;
  1044. model_->Set(index, item);
  1045. ASSERT_EQ(ShelfAppButton::STATE_ATTENTION, button->state());
  1046. }
  1047. // Confirm that shelf item bounds are correctly updated on shelf changes.
  1048. TEST_P(LtrRtlShelfViewTest, ShelfItemBoundsCheck) {
  1049. VerifyShelfItemBoundsAreValid();
  1050. shelf_view_->shelf()->SetAutoHideBehavior(ShelfAutoHideBehavior::kAlways);
  1051. test_api_->RunMessageLoopUntilAnimationsDone();
  1052. VerifyShelfItemBoundsAreValid();
  1053. shelf_view_->shelf()->SetAutoHideBehavior(ShelfAutoHideBehavior::kNever);
  1054. test_api_->RunMessageLoopUntilAnimationsDone();
  1055. VerifyShelfItemBoundsAreValid();
  1056. }
  1057. TEST_P(LtrRtlShelfViewTest, ShelfTooltipTest) {
  1058. ASSERT_EQ(shelf_view_->number_of_visible_apps(), test_api_->GetButtonCount());
  1059. // Prepare some items to the shelf.
  1060. ShelfID app_button_id = AddAppShortcut();
  1061. ShelfID platform_button_id = AddApp();
  1062. ShelfAppButton* app_button = GetButtonByID(app_button_id);
  1063. ShelfAppButton* platform_button = GetButtonByID(platform_button_id);
  1064. ShelfTooltipManager* tooltip_manager = test_api_->tooltip_manager();
  1065. EXPECT_TRUE(shelf_view_->GetWidget()->GetNativeWindow());
  1066. ui::test::EventGenerator* generator = GetEventGenerator();
  1067. generator->MoveMouseTo(app_button->GetBoundsInScreen().CenterPoint());
  1068. // There's a delay to show the tooltip, so it's not visible yet.
  1069. EXPECT_FALSE(tooltip_manager->IsVisible());
  1070. EXPECT_EQ(nullptr, tooltip_manager->GetCurrentAnchorView());
  1071. tooltip_manager->ShowTooltip(app_button);
  1072. EXPECT_TRUE(tooltip_manager->IsVisible());
  1073. EXPECT_EQ(app_button, tooltip_manager->GetCurrentAnchorView());
  1074. // The tooltip will continue showing while the cursor moves between buttons.
  1075. const gfx::Point midpoint =
  1076. gfx::UnionRects(app_button->GetBoundsInScreen(),
  1077. platform_button->GetBoundsInScreen())
  1078. .CenterPoint();
  1079. generator->MoveMouseTo(midpoint);
  1080. EXPECT_TRUE(tooltip_manager->IsVisible());
  1081. EXPECT_EQ(app_button, tooltip_manager->GetCurrentAnchorView());
  1082. // When the cursor moves over another item, its tooltip shows immediately.
  1083. generator->MoveMouseTo(platform_button->GetBoundsInScreen().CenterPoint());
  1084. EXPECT_TRUE(tooltip_manager->IsVisible());
  1085. EXPECT_EQ(platform_button, tooltip_manager->GetCurrentAnchorView());
  1086. tooltip_manager->Close();
  1087. // Now cursor over the app_button and move immediately to the platform_button.
  1088. generator->MoveMouseTo(app_button->GetBoundsInScreen().CenterPoint());
  1089. generator->MoveMouseTo(midpoint);
  1090. generator->MoveMouseTo(platform_button->GetBoundsInScreen().CenterPoint());
  1091. EXPECT_FALSE(tooltip_manager->IsVisible());
  1092. EXPECT_EQ(nullptr, tooltip_manager->GetCurrentAnchorView());
  1093. }
  1094. // Verify a fix for crash caused by a tooltip update for a deleted shelf
  1095. // button, see crbug.com/288838.
  1096. TEST_P(LtrRtlShelfViewTest, RemovingItemClosesTooltip) {
  1097. ShelfTooltipManager* tooltip_manager = test_api_->tooltip_manager();
  1098. // Add an item to the shelf.
  1099. ShelfID app_button_id = AddAppShortcut();
  1100. ShelfAppButton* app_button = GetButtonByID(app_button_id);
  1101. // Spawn a tooltip on that item.
  1102. tooltip_manager->ShowTooltip(app_button);
  1103. EXPECT_TRUE(tooltip_manager->IsVisible());
  1104. // Remove the app shortcut while the tooltip is open. The tooltip should be
  1105. // closed.
  1106. RemoveByID(app_button_id);
  1107. EXPECT_FALSE(tooltip_manager->IsVisible());
  1108. // Change the shelf layout. This should not crash.
  1109. GetPrimaryShelf()->SetAlignment(ShelfAlignment::kLeft);
  1110. }
  1111. // Changing the shelf alignment closes any open tooltip.
  1112. TEST_P(LtrRtlShelfViewTest, ShelfAlignmentClosesTooltip) {
  1113. ShelfTooltipManager* tooltip_manager = test_api_->tooltip_manager();
  1114. // Add an item to the shelf.
  1115. ShelfID app_button_id = AddAppShortcut();
  1116. ShelfAppButton* app_button = GetButtonByID(app_button_id);
  1117. // Spawn a tooltip on the item.
  1118. tooltip_manager->ShowTooltip(app_button);
  1119. EXPECT_TRUE(tooltip_manager->IsVisible());
  1120. // Changing shelf alignment hides the tooltip.
  1121. GetPrimaryShelf()->SetAlignment(ShelfAlignment::kLeft);
  1122. EXPECT_FALSE(tooltip_manager->IsVisible());
  1123. }
  1124. // Verifies that the time of button press is recorded correctly in clamshell.
  1125. TEST_P(LtrRtlShelfViewTest, HomeButtonMetricsInClamshell) {
  1126. const HomeButton* home_button =
  1127. GetPrimaryShelf()->navigation_widget()->GetHomeButton();
  1128. // Make sure we're not showing the app list.
  1129. EXPECT_FALSE(home_button->IsShowingAppList());
  1130. base::UserActionTester user_action_tester;
  1131. ASSERT_EQ(0, user_action_tester.GetActionCount(
  1132. "AppList_HomeButtonPressedClamshell"));
  1133. GetEventGenerator()->GestureTapAt(
  1134. home_button->GetBoundsInScreen().CenterPoint());
  1135. ASSERT_EQ(1, user_action_tester.GetActionCount(
  1136. "AppList_HomeButtonPressedClamshell"));
  1137. EXPECT_TRUE(home_button->IsShowingAppList());
  1138. }
  1139. // Verifies that the time of button press is recorded correctly in tablet.
  1140. TEST_P(LtrRtlShelfViewTest, HomeButtonMetricsInTablet) {
  1141. // Enable accessibility feature that forces home button to be shown even with
  1142. // kHideShelfControlsInTabletMode enabled.
  1143. Shell::Get()
  1144. ->accessibility_controller()
  1145. ->SetTabletModeShelfNavigationButtonsEnabled(true);
  1146. Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
  1147. const HomeButton* home_button =
  1148. GetPrimaryShelf()->navigation_widget()->GetHomeButton();
  1149. // Make sure we're not showing the app list.
  1150. std::unique_ptr<aura::Window> window = CreateTestWindow();
  1151. wm::ActivateWindow(window.get());
  1152. EXPECT_FALSE(home_button->IsShowingAppList());
  1153. base::UserActionTester user_action_tester;
  1154. ASSERT_EQ(
  1155. 0, user_action_tester.GetActionCount("AppList_HomeButtonPressedTablet"));
  1156. GetEventGenerator()->GestureTapAt(
  1157. home_button->GetBoundsInScreen().CenterPoint());
  1158. ASSERT_EQ(
  1159. 1, user_action_tester.GetActionCount("AppList_HomeButtonPressedTablet"));
  1160. EXPECT_TRUE(home_button->IsShowingAppList());
  1161. }
  1162. TEST_P(LtrRtlShelfViewTest, ShouldHideTooltipTest) {
  1163. // Set a screen size large enough to have space between the home button and
  1164. // app buttons.
  1165. UpdateDisplay("2000x600");
  1166. ShelfID app_button_id = AddAppShortcut();
  1167. ShelfID platform_button_id = AddApp();
  1168. // TODO(manucornet): It should not be necessary to call this manually. The
  1169. // |AddItem| call seems to sometimes be missing some re-layout steps. We
  1170. // should find out what's going on there.
  1171. shelf_view_->UpdateVisibleShelfItemBoundsUnion();
  1172. const HomeButton* home_button =
  1173. GetPrimaryShelf()->navigation_widget()->GetHomeButton();
  1174. // Make sure we're not showing the app list.
  1175. EXPECT_FALSE(home_button->IsShowingAppList())
  1176. << "We should not be showing the app list";
  1177. // The tooltip shouldn't hide if the mouse is on normal buttons.
  1178. for (size_t i = 0; i < test_api_->GetButtonCount(); i++) {
  1179. ShelfAppButton* button = test_api_->GetButton(i);
  1180. if (!button)
  1181. continue;
  1182. EXPECT_FALSE(shelf_view_->ShouldHideTooltip(
  1183. button->GetMirroredBounds().CenterPoint()))
  1184. << "ShelfView tries to hide on button " << i;
  1185. }
  1186. // The tooltip should hide if placed in between the home button and the
  1187. // first shelf button.
  1188. const int left = home_button->GetBoundsInScreen().right();
  1189. // Find the first shelf button that's to the right of the home button.
  1190. int right = 0;
  1191. for (size_t i = 0; i < test_api_->GetButtonCount(); ++i) {
  1192. ShelfAppButton* button = test_api_->GetButton(i);
  1193. if (!button)
  1194. continue;
  1195. right = button->GetBoundsInScreen().x();
  1196. if (right > left)
  1197. break;
  1198. }
  1199. gfx::Point test_point(left + (right - left) / 2,
  1200. home_button->GetBoundsInScreen().y());
  1201. views::View::ConvertPointFromScreen(shelf_view_, &test_point);
  1202. EXPECT_TRUE(shelf_view_->ShouldHideTooltip(gfx::Point(
  1203. shelf_view_->GetMirroredXInView(test_point.x()), test_point.y())))
  1204. << "Tooltip should hide between home button and first shelf item";
  1205. // The tooltip shouldn't hide if the mouse is in the gap between two buttons.
  1206. gfx::Rect app_button_rect = GetButtonByID(app_button_id)->GetMirroredBounds();
  1207. gfx::Rect platform_button_rect =
  1208. GetButtonByID(platform_button_id)->GetMirroredBounds();
  1209. ASSERT_FALSE(app_button_rect.Intersects(platform_button_rect));
  1210. EXPECT_FALSE(shelf_view_->ShouldHideTooltip(
  1211. gfx::UnionRects(app_button_rect, platform_button_rect).CenterPoint()));
  1212. // The tooltip should hide if it's outside of all buttons.
  1213. gfx::Rect all_area;
  1214. for (size_t i = 0; i < test_api_->GetButtonCount(); i++) {
  1215. ShelfAppButton* button = test_api_->GetButton(i);
  1216. if (!button)
  1217. continue;
  1218. all_area.Union(button->GetMirroredBounds());
  1219. }
  1220. EXPECT_FALSE(shelf_view_->ShouldHideTooltip(
  1221. gfx::Point(all_area.right() - 1, all_area.bottom() - 1)));
  1222. EXPECT_TRUE(shelf_view_->ShouldHideTooltip(
  1223. gfx::Point(all_area.right(), all_area.y())));
  1224. EXPECT_TRUE(shelf_view_->ShouldHideTooltip(
  1225. gfx::Point(all_area.x() - 1, all_area.y())));
  1226. EXPECT_TRUE(shelf_view_->ShouldHideTooltip(
  1227. gfx::Point(all_area.x(), all_area.y() - 1)));
  1228. EXPECT_TRUE(shelf_view_->ShouldHideTooltip(
  1229. gfx::Point(all_area.x(), all_area.bottom())));
  1230. }
  1231. // Test that shelf button tooltips show (except app list) with an open app list.
  1232. TEST_P(LtrRtlShelfViewTest, ShouldHideTooltipWithAppListWindowTest) {
  1233. GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
  1234. // The tooltip shouldn't hide if the mouse is on normal buttons.
  1235. for (size_t i = 2; i < test_api_->GetButtonCount(); i++) {
  1236. ShelfAppButton* button = test_api_->GetButton(i);
  1237. if (!button)
  1238. continue;
  1239. EXPECT_FALSE(shelf_view_->ShouldHideTooltip(
  1240. button->GetMirroredBounds().CenterPoint()))
  1241. << "ShelfView tries to hide on button " << i;
  1242. }
  1243. // The tooltip should hide on the home button if the app list is visible.
  1244. HomeButton* home_button =
  1245. GetPrimaryShelf()->navigation_widget()->GetHomeButton();
  1246. gfx::Point center_point = home_button->GetBoundsInScreen().CenterPoint();
  1247. views::View::ConvertPointFromScreen(shelf_view_, &center_point);
  1248. EXPECT_TRUE(shelf_view_->ShouldHideTooltip(gfx::Point(
  1249. shelf_view_->GetMirroredXInView(center_point.x()), center_point.y())));
  1250. }
  1251. // Test that by moving the mouse cursor off the button onto the bubble it closes
  1252. // the bubble.
  1253. TEST_P(LtrRtlShelfViewTest, ShouldHideTooltipWhenHoveringOnTooltip) {
  1254. ShelfTooltipManager* tooltip_manager = test_api_->tooltip_manager();
  1255. tooltip_manager->set_timer_delay_for_test(0);
  1256. ui::test::EventGenerator* generator = GetEventGenerator();
  1257. // Move the mouse off any item and check that no tooltip is shown.
  1258. generator->MoveMouseTo(gfx::Point(0, 0));
  1259. EXPECT_FALSE(tooltip_manager->IsVisible());
  1260. // Move the mouse over the button and check that it is visible.
  1261. views::View* button = shelf_view_->first_visible_button_for_testing();
  1262. gfx::Rect bounds = button->GetBoundsInScreen();
  1263. generator->MoveMouseTo(bounds.CenterPoint());
  1264. // Wait for the timer to go off.
  1265. base::RunLoop().RunUntilIdle();
  1266. EXPECT_TRUE(tooltip_manager->IsVisible());
  1267. // Move the mouse cursor slightly to the right of the item. The tooltip should
  1268. // now close.
  1269. generator->MoveMouseBy(bounds.width() / 2 + 5, 0);
  1270. base::RunLoop().RunUntilIdle();
  1271. EXPECT_FALSE(tooltip_manager->IsVisible());
  1272. // Move back - it should appear again.
  1273. generator->MoveMouseBy(-(bounds.width() / 2 + 5), 0);
  1274. // Make sure there is no delayed close.
  1275. base::RunLoop().RunUntilIdle();
  1276. EXPECT_TRUE(tooltip_manager->IsVisible());
  1277. // Now move the mouse cursor slightly above the item - so that it is over the
  1278. // tooltip bubble. Now it should disappear.
  1279. generator->MoveMouseBy(0, -(bounds.height() / 2 + 5));
  1280. // Wait until the delayed close kicked in.
  1281. base::RunLoop().RunUntilIdle();
  1282. EXPECT_FALSE(tooltip_manager->IsVisible());
  1283. }
  1284. // Checks the rip an item off from left aligned shelf in secondary monitor.
  1285. TEST_P(LtrRtlShelfViewTest, CheckRipOffFromLeftShelfAlignmentWithMultiMonitor) {
  1286. UpdateDisplay("800x600,800x600");
  1287. ASSERT_EQ(2U, Shell::GetAllRootWindows().size());
  1288. aura::Window* root_window = Shell::GetAllRootWindows()[1];
  1289. Shelf* secondary_shelf = Shelf::ForWindow(root_window);
  1290. secondary_shelf->SetAlignment(ShelfAlignment::kLeft);
  1291. ASSERT_EQ(ShelfAlignment::kLeft, secondary_shelf->alignment());
  1292. ShelfView* shelf_view_for_secondary =
  1293. secondary_shelf->GetShelfViewForTesting();
  1294. AddAppShortcut();
  1295. ShelfViewTestAPI test_api_for_secondary_shelf_view(shelf_view_for_secondary);
  1296. ShelfAppButton* button = test_api_for_secondary_shelf_view.GetButton(0);
  1297. // Fetch the start point of dragging.
  1298. gfx::Point start_point = button->GetBoundsInScreen().CenterPoint();
  1299. gfx::Point end_point = start_point + gfx::Vector2d(400, 0);
  1300. ::wm::ConvertPointFromScreen(root_window, &start_point);
  1301. ui::test::EventGenerator generator(root_window, start_point);
  1302. // Rip off the browser item.
  1303. generator.PressLeftButton();
  1304. generator.MoveMouseTo(end_point);
  1305. test_api_for_secondary_shelf_view.RunMessageLoopUntilAnimationsDone();
  1306. EXPECT_TRUE(test_api_for_secondary_shelf_view.IsRippedOffFromShelf());
  1307. // Release the button to prevent crash in test destructor (releasing the
  1308. // button triggers animating shelf to ideal bounds during shell destruction).
  1309. generator.ReleaseLeftButton();
  1310. }
  1311. // Verifies that Launcher_ButtonPressed_* UMA user actions are recorded when an
  1312. // item is selected.
  1313. TEST_P(LtrRtlShelfViewTest,
  1314. Launcher_ButtonPressedUserActionsRecordedWhenItemSelected) {
  1315. base::UserActionTester user_action_tester;
  1316. ShelfItemSelectionTracker* selection_tracker = new ShelfItemSelectionTracker;
  1317. model_->ReplaceShelfItemDelegate(
  1318. model_->items()[0].id,
  1319. base::WrapUnique<ShelfItemSelectionTracker>(selection_tracker));
  1320. SimulateClick(0);
  1321. EXPECT_EQ(1,
  1322. user_action_tester.GetActionCount("Launcher_ButtonPressed_Mouse"));
  1323. }
  1324. // Verifies that Launcher_*Task UMA user actions are recorded when an item is
  1325. // selected.
  1326. TEST_P(LtrRtlShelfViewTest, Launcher_TaskUserActionsRecordedWhenItemSelected) {
  1327. base::UserActionTester user_action_tester;
  1328. ShelfItemSelectionTracker* selection_tracker = new ShelfItemSelectionTracker;
  1329. selection_tracker->set_item_selected_action(SHELF_ACTION_NEW_WINDOW_CREATED);
  1330. model_->ReplaceShelfItemDelegate(
  1331. model_->items()[0].id,
  1332. base::WrapUnique<ShelfItemSelectionTracker>(selection_tracker));
  1333. SimulateClick(0);
  1334. EXPECT_EQ(1, user_action_tester.GetActionCount("Launcher_LaunchTask"));
  1335. }
  1336. // Verifies that metrics are recorded when an item is minimized and subsequently
  1337. // activated.
  1338. TEST_P(LtrRtlShelfViewTest,
  1339. VerifyMetricsAreRecordedWhenAnItemIsMinimizedAndActivated) {
  1340. base::HistogramTester histogram_tester;
  1341. ShelfItemSelectionTracker* selection_tracker = new ShelfItemSelectionTracker;
  1342. model_->ReplaceShelfItemDelegate(
  1343. model_->items()[0].id,
  1344. base::WrapUnique<ShelfItemSelectionTracker>(selection_tracker));
  1345. selection_tracker->set_item_selected_action(SHELF_ACTION_WINDOW_MINIMIZED);
  1346. SimulateClick(0);
  1347. selection_tracker->set_item_selected_action(SHELF_ACTION_WINDOW_ACTIVATED);
  1348. SimulateClick(0);
  1349. histogram_tester.ExpectTotalCount(
  1350. kTimeBetweenWindowMinimizedAndActivatedActionsHistogramName, 1);
  1351. }
  1352. // Verify the animations of the shelf items are as long as expected.
  1353. TEST_P(LtrRtlShelfViewTest, TestShelfItemsAnimations) {
  1354. TestShelfObserver observer(shelf_view_->shelf());
  1355. ui::test::EventGenerator* generator = GetEventGenerator();
  1356. ShelfID first_app_id = AddAppShortcut();
  1357. ShelfID second_app_id = AddAppShortcut();
  1358. // Set the animation duration for shelf items.
  1359. test_api_->SetAnimationDuration(base::Milliseconds(100));
  1360. // The shelf items should animate if they are moved within the shelf, either
  1361. // by swapping or if the items need to be rearranged due to an item getting
  1362. // ripped off.
  1363. generator->set_current_screen_location(GetButtonCenter(first_app_id));
  1364. generator->DragMouseTo(GetButtonCenter(second_app_id));
  1365. generator->DragMouseBy(0, 50);
  1366. test_api_->RunMessageLoopUntilAnimationsDone();
  1367. EXPECT_EQ(100, observer.icon_positions_animation_duration().InMilliseconds());
  1368. // The shelf items should not animate when the whole shelf and its contents
  1369. // have to move.
  1370. observer.Reset();
  1371. shelf_view_->shelf()->SetAlignment(ShelfAlignment::kLeft);
  1372. test_api_->RunMessageLoopUntilAnimationsDone();
  1373. EXPECT_EQ(1, observer.icon_positions_animation_duration().InMilliseconds());
  1374. // The shelf items should animate if we are entering or exiting tablet mode,
  1375. // and the shelf alignment is bottom aligned, and scrollable shelf is not
  1376. // enabled.
  1377. PrefService* prefs =
  1378. Shell::Get()->session_controller()->GetLastActiveUserPrefService();
  1379. const int64_t id = GetPrimaryDisplay().id();
  1380. shelf_view_->shelf()->SetAlignment(ShelfAlignment::kBottom);
  1381. SetShelfAlignmentPref(prefs, id, ShelfAlignment::kBottom);
  1382. observer.Reset();
  1383. Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
  1384. test_api_->RunMessageLoopUntilAnimationsDone();
  1385. EXPECT_EQ(1, observer.icon_positions_animation_duration().InMilliseconds());
  1386. observer.Reset();
  1387. Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false);
  1388. test_api_->RunMessageLoopUntilAnimationsDone();
  1389. EXPECT_EQ(1, observer.icon_positions_animation_duration().InMilliseconds());
  1390. // The shelf items should not animate if we are entering or exiting tablet
  1391. // mode, and the shelf alignment is not bottom aligned.
  1392. shelf_view_->shelf()->SetAlignment(ShelfAlignment::kLeft);
  1393. SetShelfAlignmentPref(prefs, id, ShelfAlignment::kLeft);
  1394. observer.Reset();
  1395. Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
  1396. test_api_->RunMessageLoopUntilAnimationsDone();
  1397. EXPECT_EQ(1, observer.icon_positions_animation_duration().InMilliseconds());
  1398. observer.Reset();
  1399. Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false);
  1400. test_api_->RunMessageLoopUntilAnimationsDone();
  1401. EXPECT_EQ(1, observer.icon_positions_animation_duration().InMilliseconds());
  1402. }
  1403. // Tests that the blank shelf view area shows a context menu on right click.
  1404. TEST_P(LtrRtlShelfViewTest, ShelfViewShowsContextMenu) {
  1405. ui::test::EventGenerator* generator = GetEventGenerator();
  1406. generator->MoveMouseTo(shelf_view_->GetBoundsInScreen().CenterPoint());
  1407. generator->PressRightButton();
  1408. generator->ReleaseRightButton();
  1409. EXPECT_TRUE(test_api_->CloseMenu());
  1410. }
  1411. TEST_P(LtrRtlShelfViewTest, TabletModeStartAndEndClosesContextMenu) {
  1412. // Show a context menu on the shelf
  1413. ui::test::EventGenerator* generator = GetEventGenerator();
  1414. generator->MoveMouseTo(shelf_view_->GetBoundsInScreen().CenterPoint());
  1415. generator->PressRightButton();
  1416. // Start tablet mode, which should close the menu.
  1417. shelf_view_->OnTabletModeStarted();
  1418. // Attempt to close the menu, which should already be closed.
  1419. EXPECT_FALSE(test_api_->CloseMenu());
  1420. // Show another context menu on the shelf.
  1421. generator->MoveMouseTo(shelf_view_->GetBoundsInScreen().CenterPoint());
  1422. generator->PressRightButton();
  1423. // End tablet mode, which should close the menu.
  1424. shelf_view_->OnTabletModeEnded();
  1425. // Attempt to close the menu, which should already be closed.
  1426. EXPECT_FALSE(test_api_->CloseMenu());
  1427. }
  1428. // Tests that ShelfWindowWatcher buttons show a context menu on right click.
  1429. TEST_P(LtrRtlShelfViewTest, ShelfWindowWatcherButtonShowsContextMenu) {
  1430. ui::test::EventGenerator* generator = GetEventGenerator();
  1431. std::unique_ptr<views::Widget> widget = CreateTestWidget();
  1432. widget->Show();
  1433. aura::Window* window = widget->GetNativeWindow();
  1434. ShelfID shelf_id("123");
  1435. window->SetProperty(kShelfIDKey, shelf_id.Serialize());
  1436. window->SetProperty(kShelfItemTypeKey, static_cast<int32_t>(TYPE_DIALOG));
  1437. ShelfAppButton* button = GetButtonByID(shelf_id);
  1438. ASSERT_TRUE(button);
  1439. generator->MoveMouseTo(button->GetBoundsInScreen().CenterPoint());
  1440. generator->PressRightButton();
  1441. EXPECT_TRUE(test_api_->CloseMenu());
  1442. }
  1443. // Tests that the drag view is set on left click and not set on right click.
  1444. TEST_P(LtrRtlShelfViewTest, ShelfDragViewAndContextMenu) {
  1445. ui::test::EventGenerator* generator = GetEventGenerator();
  1446. std::unique_ptr<views::Widget> widget = CreateTestWidget();
  1447. widget->Show();
  1448. aura::Window* window = widget->GetNativeWindow();
  1449. ShelfID shelf_id("123");
  1450. window->SetProperty(kShelfIDKey, shelf_id.Serialize());
  1451. window->SetProperty(kShelfItemTypeKey, static_cast<int32_t>(TYPE_DIALOG));
  1452. // Waits for the bounds animation triggered by window property setting to
  1453. // finish.
  1454. test_api_->RunMessageLoopUntilAnimationsDone();
  1455. ShelfAppButton* button = GetButtonByID(shelf_id);
  1456. ASSERT_TRUE(button);
  1457. // Context menu is shown on right button press and no drag view is set.
  1458. EXPECT_FALSE(shelf_view_->IsShowingMenu());
  1459. EXPECT_FALSE(shelf_view_->drag_view());
  1460. generator->MoveMouseTo(button->GetBoundsInScreen().CenterPoint());
  1461. generator->PressRightButton();
  1462. EXPECT_TRUE(shelf_view_->IsShowingMenu());
  1463. EXPECT_FALSE(shelf_view_->drag_view());
  1464. // Press left button. Menu should close.
  1465. generator->PressLeftButton();
  1466. generator->ReleaseLeftButton();
  1467. EXPECT_FALSE(shelf_view_->IsShowingMenu());
  1468. // Press left button. Drag view is set to |button|.
  1469. generator->PressLeftButton();
  1470. base::RunLoop().RunUntilIdle();
  1471. EXPECT_EQ(shelf_view_->drag_view(), button);
  1472. generator->ReleaseLeftButton();
  1473. EXPECT_FALSE(shelf_view_->drag_view());
  1474. EXPECT_EQ(0, GetHapticTickEventsCount());
  1475. }
  1476. // Tests that context menu show is cancelled if item drag starts during context
  1477. // menu show (while constructing the item menu model).
  1478. TEST_P(LtrRtlShelfViewTest, InProgressItemDragPreventsContextMenuShow) {
  1479. const ShelfID app_id = AddAppShortcut();
  1480. auto item_delegate_owned =
  1481. std::make_unique<AsyncContextMenuShelfItemDelegate>();
  1482. AsyncContextMenuShelfItemDelegate* item_delegate = item_delegate_owned.get();
  1483. model_->ReplaceShelfItemDelegate(app_id, std::move(item_delegate_owned));
  1484. ShelfAppButton* button = GetButtonByID(app_id);
  1485. ASSERT_TRUE(button);
  1486. const gfx::Point location = button->GetBoundsInScreen().CenterPoint();
  1487. ui::test::EventGenerator* generator = GetEventGenerator();
  1488. generator->set_current_screen_location(location);
  1489. generator->PressTouch();
  1490. // Generate long press, which should start context menu request.
  1491. ui::GestureEventDetails event_details(ui::ET_GESTURE_LONG_PRESS);
  1492. ui::GestureEvent long_press(location.x(), location.y(), 0,
  1493. ui::EventTimeForNow(), event_details);
  1494. generator->Dispatch(&long_press);
  1495. EXPECT_FALSE(shelf_view_->IsShowingMenu());
  1496. EXPECT_FALSE(shelf_view_->drag_view());
  1497. EXPECT_FALSE(button->state() & ShelfAppButton::STATE_DRAGGING);
  1498. EXPECT_TRUE(item_delegate->HasPendingContextMenuCallback());
  1499. // Drag the app icon while context menu callback is pending..
  1500. ASSERT_TRUE(button->FireDragTimerForTest());
  1501. generator->MoveTouchBy(0, -10);
  1502. EXPECT_FALSE(shelf_view_->IsShowingMenu());
  1503. EXPECT_FALSE(shelf_view_->GetShelfItemViewWithContextMenu());
  1504. EXPECT_TRUE(shelf_view_->drag_view());
  1505. EXPECT_TRUE(button->state() & ShelfAppButton::STATE_DRAGGING);
  1506. // Return the context menu model.
  1507. auto menu_model = std::make_unique<ui::SimpleMenuModel>(nullptr);
  1508. menu_model->AddItem(203, u"item");
  1509. ASSERT_TRUE(
  1510. item_delegate->RunPendingContextMenuCallback(std::move(menu_model)));
  1511. // The context menu show is expected to be canceled by the item drag.
  1512. EXPECT_FALSE(shelf_view_->IsShowingMenu());
  1513. EXPECT_FALSE(shelf_view_->GetShelfItemViewWithContextMenu());
  1514. EXPECT_TRUE(shelf_view_->drag_view());
  1515. EXPECT_TRUE(button->state() & ShelfAppButton::STATE_DRAGGING);
  1516. // Drag state should be cleared when the drag ends.
  1517. generator->ReleaseTouch();
  1518. EXPECT_FALSE(shelf_view_->IsShowingMenu());
  1519. EXPECT_FALSE(shelf_view_->GetShelfItemViewWithContextMenu());
  1520. EXPECT_FALSE(shelf_view_->drag_view());
  1521. EXPECT_FALSE(button->state() & ShelfAppButton::STATE_DRAGGING);
  1522. // Another long press starts context menu request.
  1523. generator->set_current_screen_location(location);
  1524. generator->PressTouch();
  1525. ui::GestureEventDetails second_press_event_details(ui::ET_GESTURE_LONG_PRESS);
  1526. ui::GestureEvent second_long_press(location.x(), location.y(), 0,
  1527. ui::EventTimeForNow(),
  1528. second_press_event_details);
  1529. generator->Dispatch(&second_long_press);
  1530. EXPECT_TRUE(item_delegate->HasPendingContextMenuCallback());
  1531. }
  1532. // Tests that context menu show is cancelled if item drag starts and ends during
  1533. // context menu show (while constructing the item menu model).
  1534. TEST_P(LtrRtlShelfViewTest, CompletedItemDragPreventsContextMenuShow) {
  1535. const ShelfID app_id = AddAppShortcut();
  1536. auto item_delegate_owned =
  1537. std::make_unique<AsyncContextMenuShelfItemDelegate>();
  1538. AsyncContextMenuShelfItemDelegate* item_delegate = item_delegate_owned.get();
  1539. model_->ReplaceShelfItemDelegate(app_id, std::move(item_delegate_owned));
  1540. ShelfAppButton* button = GetButtonByID(app_id);
  1541. ASSERT_TRUE(button);
  1542. const gfx::Point location = button->GetBoundsInScreen().CenterPoint();
  1543. ui::test::EventGenerator* generator = GetEventGenerator();
  1544. generator->set_current_screen_location(location);
  1545. generator->PressTouch();
  1546. // Generate long press, which should start context menu request.
  1547. ui::GestureEventDetails event_details(ui::ET_GESTURE_LONG_PRESS);
  1548. ui::GestureEvent long_press(location.x(), location.y(), 0,
  1549. ui::EventTimeForNow(), event_details);
  1550. generator->Dispatch(&long_press);
  1551. EXPECT_FALSE(shelf_view_->IsShowingMenu());
  1552. EXPECT_FALSE(shelf_view_->GetShelfItemViewWithContextMenu());
  1553. EXPECT_FALSE(shelf_view_->drag_view());
  1554. EXPECT_FALSE(button->state() & ShelfAppButton::STATE_DRAGGING);
  1555. EXPECT_TRUE(item_delegate->HasPendingContextMenuCallback());
  1556. // Drag the app icon while context menu callback is pending.
  1557. ASSERT_TRUE(button->FireDragTimerForTest());
  1558. generator->MoveTouchBy(0, -10);
  1559. EXPECT_FALSE(shelf_view_->IsShowingMenu());
  1560. EXPECT_FALSE(shelf_view_->GetShelfItemViewWithContextMenu());
  1561. EXPECT_TRUE(shelf_view_->drag_view());
  1562. EXPECT_TRUE(button->state() & ShelfAppButton::STATE_DRAGGING);
  1563. // Drag state should be cleared when the drag ends.
  1564. generator->ReleaseTouch();
  1565. EXPECT_FALSE(shelf_view_->IsShowingMenu());
  1566. EXPECT_FALSE(shelf_view_->GetShelfItemViewWithContextMenu());
  1567. EXPECT_FALSE(shelf_view_->drag_view());
  1568. EXPECT_FALSE(button->state() & ShelfAppButton::STATE_DRAGGING);
  1569. // Return the context menu model.
  1570. auto menu_model = std::make_unique<ui::SimpleMenuModel>(nullptr);
  1571. menu_model->AddItem(203, u"item");
  1572. ASSERT_TRUE(
  1573. item_delegate->RunPendingContextMenuCallback(std::move(menu_model)));
  1574. // The context menu show is expected to be canceled by the item drag, so it
  1575. // should not be shown even though there is no in-progress drag when the
  1576. // context menu model is received.
  1577. EXPECT_FALSE(shelf_view_->IsShowingMenu());
  1578. EXPECT_FALSE(shelf_view_->GetShelfItemViewWithContextMenu());
  1579. EXPECT_FALSE(shelf_view_->drag_view());
  1580. EXPECT_FALSE(button->state() & ShelfAppButton::STATE_DRAGGING);
  1581. // Another long press starts context menu request.
  1582. generator->set_current_screen_location(location);
  1583. generator->PressTouch();
  1584. ui::GestureEventDetails second_press_event_details(ui::ET_GESTURE_LONG_PRESS);
  1585. ui::GestureEvent second_long_press(location.x(), location.y(), 0,
  1586. ui::EventTimeForNow(),
  1587. second_press_event_details);
  1588. generator->Dispatch(&second_long_press);
  1589. EXPECT_TRUE(item_delegate->HasPendingContextMenuCallback());
  1590. }
  1591. // Tests that shelf items in always shown shelf can be dragged through gesture
  1592. // events after context menu is shown.
  1593. TEST_P(LtrRtlShelfViewTest, DragAppAfterContextMenuIsShownInAlwaysShownShelf) {
  1594. ASSERT_EQ(SHELF_VISIBLE, GetPrimaryShelf()->GetVisibilityState());
  1595. ui::test::EventGenerator* generator = GetEventGenerator();
  1596. const ShelfID first_app_id = AddAppShortcut();
  1597. const ShelfID second_app_id = AddAppShortcut();
  1598. const int last_index = model_->items().size() - 1;
  1599. ASSERT_TRUE(last_index >= 0);
  1600. ShelfAppButton* button = GetButtonByID(first_app_id);
  1601. ASSERT_TRUE(button);
  1602. auto item_delegate_owned =
  1603. std::make_unique<AsyncContextMenuShelfItemDelegate>();
  1604. AsyncContextMenuShelfItemDelegate* item_delegate = item_delegate_owned.get();
  1605. model_->ReplaceShelfItemDelegate(first_app_id,
  1606. std::move(item_delegate_owned));
  1607. const gfx::Point start = GetButtonCenter(first_app_id);
  1608. // Drag the app long enough to ensure the drag can be triggered.
  1609. const gfx::Point end(start.x() + (IsRtlEnabled() ? -100 : 100), start.y());
  1610. generator->set_current_screen_location(start);
  1611. generator->PressTouch();
  1612. ASSERT_TRUE(button->FireDragTimerForTest());
  1613. button->FireRippleActivationTimerForTest();
  1614. // Generate long press, which should start context menu request.
  1615. ui::GestureEventDetails event_details(ui::ET_GESTURE_LONG_PRESS);
  1616. ui::GestureEvent long_press(start.x(), start.y(), 0, ui::EventTimeForNow(),
  1617. event_details);
  1618. generator->Dispatch(&long_press);
  1619. EXPECT_FALSE(shelf_view_->IsShowingMenu());
  1620. EXPECT_FALSE(shelf_view_->GetShelfItemViewWithContextMenu());
  1621. EXPECT_FALSE(shelf_view_->drag_view());
  1622. EXPECT_TRUE(button->state() & ShelfAppButton::STATE_DRAGGING);
  1623. EXPECT_TRUE(item_delegate->HasPendingContextMenuCallback());
  1624. auto menu_model = std::make_unique<ui::SimpleMenuModel>(nullptr);
  1625. menu_model->AddItem(203, u"item");
  1626. ASSERT_TRUE(
  1627. item_delegate->RunPendingContextMenuCallback(std::move(menu_model)));
  1628. EXPECT_TRUE(shelf_view_->IsShowingMenu());
  1629. EXPECT_EQ(button, shelf_view_->GetShelfItemViewWithContextMenu());
  1630. EXPECT_FALSE(shelf_view_->drag_view());
  1631. EXPECT_TRUE(button->state() & ShelfAppButton::STATE_DRAGGING);
  1632. generator->GestureScrollSequence(start, end, base::Milliseconds(100), 3);
  1633. generator->ReleaseTouch();
  1634. EXPECT_EQ(0, GetHapticTickEventsCount());
  1635. // |first_add_id| has been moved to the end of the items in the shelf.
  1636. EXPECT_EQ(first_app_id, model_->items()[last_index].id);
  1637. EXPECT_FALSE(shelf_view_->IsShowingMenu());
  1638. EXPECT_FALSE(shelf_view_->GetShelfItemViewWithContextMenu());
  1639. EXPECT_FALSE(shelf_view_->drag_view());
  1640. EXPECT_FALSE(button->state() & ShelfAppButton::STATE_DRAGGING);
  1641. EXPECT_FALSE(item_delegate->HasPendingContextMenuCallback());
  1642. }
  1643. // Tests that shelf items in AUTO_HIDE_SHOWN shelf can be dragged through
  1644. // gesture events after context menu is shown.
  1645. TEST_P(LtrRtlShelfViewTest, DragAppAfterContextMenuIsShownInAutoHideShelf) {
  1646. ui::test::EventGenerator* generator = GetEventGenerator();
  1647. const ShelfID first_app_id = AddAppShortcut();
  1648. const ShelfID second_app_id = AddAppShortcut();
  1649. const int last_index = model_->items().size() - 1;
  1650. Shelf* shelf = GetPrimaryShelf();
  1651. std::unique_ptr<views::Widget> widget = CreateTestWidget();
  1652. widget->Show();
  1653. shelf->SetAutoHideBehavior(ShelfAutoHideBehavior::kAlways);
  1654. EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState());
  1655. EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->GetAutoHideState());
  1656. shelf->shelf_widget()->GetFocusCycler()->RotateFocus(FocusCycler::FORWARD);
  1657. EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState());
  1658. ShelfAppButton* button = GetButtonByID(first_app_id);
  1659. ASSERT_TRUE(button);
  1660. auto item_delegate_owned =
  1661. std::make_unique<AsyncContextMenuShelfItemDelegate>();
  1662. AsyncContextMenuShelfItemDelegate* item_delegate = item_delegate_owned.get();
  1663. model_->ReplaceShelfItemDelegate(first_app_id,
  1664. std::move(item_delegate_owned));
  1665. const gfx::Point start = GetButtonCenter(first_app_id);
  1666. // Drag the app long enough to ensure the drag can be triggered.
  1667. const gfx::Point end(start.x() + (IsRtlEnabled() ? -100 : 100), start.y());
  1668. generator->set_current_screen_location(start);
  1669. generator->PressTouch();
  1670. ASSERT_TRUE(button->FireDragTimerForTest());
  1671. button->FireRippleActivationTimerForTest();
  1672. // Generate long press, which should start context menu request.
  1673. ui::GestureEventDetails event_details(ui::ET_GESTURE_LONG_PRESS);
  1674. ui::GestureEvent long_press(start.x(), start.y(), 0, ui::EventTimeForNow(),
  1675. event_details);
  1676. generator->Dispatch(&long_press);
  1677. EXPECT_FALSE(shelf_view_->IsShowingMenu());
  1678. EXPECT_FALSE(shelf_view_->GetShelfItemViewWithContextMenu());
  1679. EXPECT_FALSE(shelf_view_->drag_view());
  1680. EXPECT_TRUE(button->state() & ShelfAppButton::STATE_DRAGGING);
  1681. EXPECT_TRUE(item_delegate->HasPendingContextMenuCallback());
  1682. auto menu_model = std::make_unique<ui::SimpleMenuModel>(nullptr);
  1683. menu_model->AddItem(203, u"item");
  1684. ASSERT_TRUE(
  1685. item_delegate->RunPendingContextMenuCallback(std::move(menu_model)));
  1686. EXPECT_TRUE(shelf_view_->IsShowingMenu());
  1687. EXPECT_EQ(button, shelf_view_->GetShelfItemViewWithContextMenu());
  1688. EXPECT_FALSE(shelf_view_->drag_view());
  1689. EXPECT_TRUE(button->state() & ShelfAppButton::STATE_DRAGGING);
  1690. generator->GestureScrollSequence(start, end, base::Milliseconds(100), 3);
  1691. generator->ReleaseTouch();
  1692. EXPECT_EQ(0, GetHapticTickEventsCount());
  1693. // |first_add_id| has been moved to the end of the items in the shelf.
  1694. EXPECT_EQ(first_app_id, model_->items()[last_index].id);
  1695. EXPECT_FALSE(shelf_view_->IsShowingMenu());
  1696. EXPECT_FALSE(shelf_view_->GetShelfItemViewWithContextMenu());
  1697. EXPECT_FALSE(shelf_view_->drag_view());
  1698. EXPECT_FALSE(button->state() & ShelfAppButton::STATE_DRAGGING);
  1699. EXPECT_FALSE(item_delegate->HasPendingContextMenuCallback());
  1700. }
  1701. // Tests that the app button returns to normal state after context menu is
  1702. // hidden and touch is released, even if another touch point is added while the
  1703. // context menu is shown.
  1704. TEST_P(LtrRtlShelfViewTest,
  1705. DragStateIsClearedIfAnotherTouchIsAddedWithContextMenu) {
  1706. ASSERT_EQ(SHELF_VISIBLE, GetPrimaryShelf()->GetVisibilityState());
  1707. ui::test::EventGenerator* generator = GetEventGenerator();
  1708. const ShelfID app_id = AddAppShortcut();
  1709. ShelfAppButton* button = GetButtonByID(app_id);
  1710. ASSERT_TRUE(button);
  1711. auto item_delegate_owned =
  1712. std::make_unique<AsyncContextMenuShelfItemDelegate>();
  1713. AsyncContextMenuShelfItemDelegate* item_delegate = item_delegate_owned.get();
  1714. model_->ReplaceShelfItemDelegate(app_id, std::move(item_delegate_owned));
  1715. const gfx::Point location = GetButtonCenter(app_id);
  1716. generator->set_current_screen_location(location);
  1717. generator->PressTouch();
  1718. ASSERT_TRUE(button->FireDragTimerForTest());
  1719. button->FireRippleActivationTimerForTest();
  1720. // Generate long press, which should start context menu request.
  1721. ui::GestureEventDetails event_details(ui::ET_GESTURE_LONG_PRESS);
  1722. ui::GestureEvent long_press(location.x(), location.y(), 0,
  1723. ui::EventTimeForNow(), event_details);
  1724. generator->Dispatch(&long_press);
  1725. EXPECT_FALSE(shelf_view_->IsShowingMenu());
  1726. EXPECT_FALSE(shelf_view_->GetShelfItemViewWithContextMenu());
  1727. EXPECT_TRUE(button->state() & ShelfAppButton::STATE_DRAGGING);
  1728. EXPECT_TRUE(item_delegate->HasPendingContextMenuCallback());
  1729. auto menu_model = std::make_unique<ui::SimpleMenuModel>(nullptr);
  1730. menu_model->AddItem(203, u"item");
  1731. ASSERT_TRUE(
  1732. item_delegate->RunPendingContextMenuCallback(std::move(menu_model)));
  1733. EXPECT_TRUE(shelf_view_->IsShowingMenu());
  1734. EXPECT_EQ(button, shelf_view_->GetShelfItemViewWithContextMenu());
  1735. EXPECT_TRUE(button->state() & ShelfAppButton::STATE_DRAGGING);
  1736. // Add and release touch.
  1737. generator->PressTouchId(2, gfx::Point(10, 10));
  1738. generator->ReleaseTouchId(2);
  1739. EXPECT_TRUE(shelf_view_->IsShowingMenu());
  1740. EXPECT_EQ(button, shelf_view_->GetShelfItemViewWithContextMenu());
  1741. // Another tap to hide the context menu.
  1742. generator->PressTouchId(2, gfx::Point(10, 10));
  1743. generator->ReleaseTouchId(2);
  1744. EXPECT_FALSE(shelf_view_->IsShowingMenu());
  1745. EXPECT_FALSE(shelf_view_->GetShelfItemViewWithContextMenu());
  1746. EXPECT_FALSE(button->state() & ShelfAppButton::STATE_DRAGGING);
  1747. // Releasing the original touch should not show another menu.
  1748. generator->ReleaseTouch();
  1749. EXPECT_EQ(0, GetHapticTickEventsCount());
  1750. EXPECT_FALSE(shelf_view_->IsShowingMenu());
  1751. EXPECT_FALSE(shelf_view_->GetShelfItemViewWithContextMenu());
  1752. EXPECT_FALSE(shelf_view_->drag_view());
  1753. EXPECT_FALSE(button->state() & ShelfAppButton::STATE_DRAGGING);
  1754. EXPECT_FALSE(item_delegate->HasPendingContextMenuCallback());
  1755. }
  1756. // Tests that app button context menu remains shown if touch moves slightly
  1757. // outside the button bounds while the menu is shown (not enough to initiate
  1758. // drag gesture).
  1759. TEST_P(LtrRtlShelfViewTest,
  1760. AppContextMenuRemainsShowingAfterTouchSlightlyMovesOutsideTheButton) {
  1761. ASSERT_EQ(SHELF_VISIBLE, GetPrimaryShelf()->GetVisibilityState());
  1762. ui::test::EventGenerator* generator = GetEventGenerator();
  1763. const ShelfID app_id = AddAppShortcut();
  1764. ShelfAppButton* button = GetButtonByID(app_id);
  1765. ASSERT_TRUE(button);
  1766. auto item_delegate_owned =
  1767. std::make_unique<AsyncContextMenuShelfItemDelegate>();
  1768. AsyncContextMenuShelfItemDelegate* item_delegate = item_delegate_owned.get();
  1769. model_->ReplaceShelfItemDelegate(app_id, std::move(item_delegate_owned));
  1770. // Start near the button edge.
  1771. const gfx::Point location =
  1772. button->GetBoundsInScreen().bottom_right() + gfx::Vector2d(-1, -1);
  1773. generator->set_current_screen_location(location);
  1774. generator->PressTouch();
  1775. ASSERT_TRUE(button->FireDragTimerForTest());
  1776. button->FireRippleActivationTimerForTest();
  1777. // Generate long press, which should start context menu request.
  1778. ui::GestureEventDetails event_details(ui::ET_GESTURE_LONG_PRESS);
  1779. ui::GestureEvent long_press(location.x(), location.y(), 0,
  1780. ui::EventTimeForNow(), event_details);
  1781. generator->Dispatch(&long_press);
  1782. EXPECT_FALSE(shelf_view_->IsShowingMenu());
  1783. EXPECT_FALSE(shelf_view_->GetShelfItemViewWithContextMenu());
  1784. EXPECT_TRUE(button->state() & ShelfAppButton::STATE_DRAGGING);
  1785. EXPECT_TRUE(item_delegate->HasPendingContextMenuCallback());
  1786. auto menu_model = std::make_unique<ui::SimpleMenuModel>(nullptr);
  1787. menu_model->AddItem(203, u"item");
  1788. ASSERT_TRUE(
  1789. item_delegate->RunPendingContextMenuCallback(std::move(menu_model)));
  1790. EXPECT_TRUE(shelf_view_->IsShowingMenu());
  1791. EXPECT_EQ(button, shelf_view_->GetShelfItemViewWithContextMenu());
  1792. EXPECT_TRUE(button->state() & ShelfAppButton::STATE_DRAGGING);
  1793. // Move touch outside the button bounds using small distance - not enough to
  1794. // start a drag operation that would hide the context menu.
  1795. generator->MoveTouch(button->GetBoundsInScreen().bottom_right() +
  1796. gfx::Vector2d(1, -1));
  1797. EXPECT_TRUE(shelf_view_->IsShowingMenu());
  1798. EXPECT_FALSE(shelf_view_->drag_view());
  1799. EXPECT_EQ(button, shelf_view_->GetShelfItemViewWithContextMenu());
  1800. EXPECT_TRUE(button->state() & ShelfAppButton::STATE_DRAGGING);
  1801. // Release touch - button drag state should be cleared, and the context menu
  1802. // remain visible.
  1803. generator->ReleaseTouch();
  1804. EXPECT_TRUE(shelf_view_->IsShowingMenu());
  1805. EXPECT_TRUE(shelf_view_->GetShelfItemViewWithContextMenu());
  1806. EXPECT_FALSE(shelf_view_->drag_view());
  1807. EXPECT_FALSE(button->state() & ShelfAppButton::STATE_DRAGGING);
  1808. EXPECT_FALSE(item_delegate->HasPendingContextMenuCallback());
  1809. // Tap to close the context menu.
  1810. generator->GestureTapAt(gfx::Point(10, 10));
  1811. EXPECT_FALSE(shelf_view_->IsShowingMenu());
  1812. EXPECT_FALSE(shelf_view_->GetShelfItemViewWithContextMenu());
  1813. EXPECT_FALSE(shelf_view_->drag_view());
  1814. EXPECT_FALSE(button->state() & ShelfAppButton::STATE_DRAGGING);
  1815. EXPECT_FALSE(item_delegate->HasPendingContextMenuCallback());
  1816. }
  1817. // Tests that the home button does shows a context menu on right click.
  1818. TEST_P(LtrRtlShelfViewTest, HomeButtonDoesShowContextMenu) {
  1819. ui::test::EventGenerator* generator = GetEventGenerator();
  1820. const HomeButton* home_button =
  1821. GetPrimaryShelf()->navigation_widget()->GetHomeButton();
  1822. generator->MoveMouseTo(home_button->GetBoundsInScreen().CenterPoint());
  1823. generator->PressRightButton();
  1824. EXPECT_TRUE(test_api_->CloseMenu());
  1825. }
  1826. void ExpectWithinOnePixel(int a, int b) {
  1827. EXPECT_TRUE(abs(a - b) <= 1) << "Values " << a << " and " << b
  1828. << " should have a difference no greater than 1";
  1829. }
  1830. TEST_P(LtrRtlShelfViewTest, IconCenteringTest) {
  1831. const display::Display display =
  1832. display::Screen::GetScreen()->GetPrimaryDisplay();
  1833. const int screen_width = display.bounds().width();
  1834. const int screen_center = screen_width / 2;
  1835. // Show the IME panel, to introduce for asymettry with a larger status area.
  1836. Shell::Get()->ime_controller()->ShowImeMenuOnShelf(true);
  1837. // At the start, we have exactly one app icon for the browser. That should
  1838. // be centered on the screen.
  1839. const ShelfAppButton* button1 = GetButtonByID(model_->items()[0].id);
  1840. ExpectWithinOnePixel(screen_center,
  1841. button1->GetBoundsInScreen().CenterPoint().x());
  1842. // Also check that the distance between the icon edge and the screen edge is
  1843. // the same on both sides.
  1844. ExpectWithinOnePixel(button1->GetBoundsInScreen().x(),
  1845. screen_width - button1->GetBoundsInScreen().right());
  1846. const int apps_that_can_easily_fit_at_center_of_screen = 3;
  1847. std::vector<ShelfAppButton*> app_buttons;
  1848. // Start with just the browser app button.
  1849. app_buttons.push_back(GetButtonByID(model_->items()[0].id));
  1850. int n_buttons = 1;
  1851. // Now repeat the same process by adding apps until they can't fit at the
  1852. // center of the screen.
  1853. for (int i = 1; i < apps_that_can_easily_fit_at_center_of_screen; ++i) {
  1854. // Add a new app and add its button to our list.
  1855. app_buttons.push_back(GetButtonByID(AddApp()));
  1856. n_buttons = app_buttons.size();
  1857. if (n_buttons % 2 == 1) {
  1858. // Odd number of apps. Check that the middle app is exactly at the center
  1859. // of the screen.
  1860. ExpectWithinOnePixel(
  1861. screen_center,
  1862. app_buttons[n_buttons / 2]->GetBoundsInScreen().CenterPoint().x());
  1863. }
  1864. // Also check that the first icon is at the same distance from the left
  1865. // screen edge as the last icon is from the right screen edge.
  1866. ExpectWithinOnePixel(
  1867. app_buttons[0]->GetBoundsInScreen().x(),
  1868. screen_width - app_buttons[n_buttons - 1]->GetBoundsInScreen().right());
  1869. }
  1870. }
  1871. TEST_P(LtrRtlShelfViewTest, FirstAndLastVisibleIndex) {
  1872. // At the start, the only visible app on the shelf is the browser app button
  1873. // (index 0).
  1874. ASSERT_EQ(1u, shelf_view_->visible_views_indices().size());
  1875. EXPECT_EQ(0u, shelf_view_->visible_views_indices()[0]);
  1876. // By enabling tablet mode, the back button (index 0) should become visible,
  1877. // but that does not change the first and last visible indices.
  1878. Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
  1879. ASSERT_EQ(1u, shelf_view_->visible_views_indices().size());
  1880. EXPECT_EQ(0u, shelf_view_->visible_views_indices()[0]);
  1881. // Turn tablet mode off again.
  1882. Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false);
  1883. ASSERT_EQ(1u, shelf_view_->visible_views_indices().size());
  1884. EXPECT_EQ(0u, shelf_view_->visible_views_indices()[0]);
  1885. }
  1886. TEST_P(LtrRtlShelfViewTest, ReplacingDelegateCancelsContextMenu) {
  1887. ui::test::EventGenerator* generator = GetEventGenerator();
  1888. ShelfID app_button_id = AddAppShortcut();
  1889. generator->MoveMouseTo(GetButtonCenter(GetButtonByID(app_button_id)));
  1890. // Right click should open the context menu.
  1891. generator->PressRightButton();
  1892. generator->ReleaseRightButton();
  1893. EXPECT_TRUE(shelf_view_->IsShowingMenu());
  1894. // Replacing the item delegate should close the context menu.
  1895. model_->ReplaceShelfItemDelegate(
  1896. app_button_id, std::make_unique<ShelfItemSelectionTracker>());
  1897. EXPECT_FALSE(shelf_view_->IsShowingMenu());
  1898. }
  1899. // Verifies that shelf is shown with the app list in fullscreen mode, and that
  1900. // shelf app buttons are clickable.
  1901. TEST_P(LtrRtlShelfViewTest, ClickItemInFullscreen) {
  1902. ShelfID app_button_id = AddAppShortcut();
  1903. auto selection_tracker_owned = std::make_unique<ShelfItemSelectionTracker>();
  1904. ShelfItemSelectionTracker* selection_tracker = selection_tracker_owned.get();
  1905. model_->ReplaceShelfItemDelegate(app_button_id,
  1906. std::move(selection_tracker_owned));
  1907. // Create a fullscreen widget.
  1908. std::unique_ptr<views::Widget> widget = CreateTestWidget();
  1909. widget->SetFullscreen(true);
  1910. WindowState* window_state = WindowState::Get(widget->GetNativeWindow());
  1911. window_state->SetHideShelfWhenFullscreen(true);
  1912. Shelf* const shelf = Shelf::ForWindow(Shell::GetPrimaryRootWindow());
  1913. EXPECT_EQ(SHELF_HIDDEN, shelf->GetVisibilityState());
  1914. // Show app list, which will bring the shelf up.
  1915. GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
  1916. EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState());
  1917. EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState());
  1918. // Verify that clicking a shelf button activates it.
  1919. EXPECT_EQ(0u, selection_tracker->item_selected_count());
  1920. const ShelfAppButton* shelf_button = GetButtonByID(app_button_id);
  1921. GetEventGenerator()->MoveMouseTo(
  1922. shelf_button->GetBoundsInScreen().CenterPoint());
  1923. GetEventGenerator()->ClickLeftButton();
  1924. EXPECT_EQ(1u, selection_tracker->item_selected_count());
  1925. // Shelf gets hidden when the app list is dismissed.
  1926. GetAppListTestHelper()->DismissAndRunLoop();
  1927. EXPECT_EQ(SHELF_HIDDEN, shelf->GetVisibilityState());
  1928. EXPECT_EQ(0, GetHapticTickEventsCount());
  1929. }
  1930. // Verifies that shelf is shown with the app list in fullscreen mode, and that
  1931. // shelf app buttons are tappable.
  1932. TEST_P(LtrRtlShelfViewTest, TapInFullscreen) {
  1933. ShelfID app_button_id = AddAppShortcut();
  1934. auto selection_tracker_owned = std::make_unique<ShelfItemSelectionTracker>();
  1935. ShelfItemSelectionTracker* selection_tracker = selection_tracker_owned.get();
  1936. model_->ReplaceShelfItemDelegate(app_button_id,
  1937. std::move(selection_tracker_owned));
  1938. // Create a fullscreen widget.
  1939. std::unique_ptr<views::Widget> widget = CreateTestWidget();
  1940. widget->SetFullscreen(true);
  1941. WindowState* window_state = WindowState::Get(widget->GetNativeWindow());
  1942. window_state->SetHideShelfWhenFullscreen(true);
  1943. Shelf* const shelf = Shelf::ForWindow(Shell::GetPrimaryRootWindow());
  1944. EXPECT_EQ(SHELF_HIDDEN, shelf->GetVisibilityState());
  1945. // Show app list, which will bring the shelf up.
  1946. GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
  1947. EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState());
  1948. EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState());
  1949. // Verify that tapping a shelf button activates it.
  1950. EXPECT_EQ(0u, selection_tracker->item_selected_count());
  1951. const ShelfAppButton* shelf_button = GetButtonByID(app_button_id);
  1952. GetEventGenerator()->GestureTapAt(
  1953. shelf_button->GetBoundsInScreen().CenterPoint());
  1954. EXPECT_EQ(1u, selection_tracker->item_selected_count());
  1955. // Shelf gets hidden when the app list is dismissed.
  1956. GetAppListTestHelper()->DismissAndRunLoop();
  1957. EXPECT_EQ(SHELF_HIDDEN, shelf->GetVisibilityState());
  1958. EXPECT_EQ(0, GetHapticTickEventsCount());
  1959. }
  1960. // Verifies that partying items are hidden from the shelf.
  1961. TEST_P(LtrRtlShelfViewTest, PartyingItemsHiddenFromShelf) {
  1962. AddAppShortcut();
  1963. AddAppShortcut();
  1964. AddApp();
  1965. ShelfItem item = model_->items()[1u];
  1966. item.status = STATUS_RUNNING;
  1967. model_->Set(1, item);
  1968. const gfx::Rect initial_bounds0 = test_api_->GetBoundsByIndex(0);
  1969. const gfx::Rect initial_bounds2 = test_api_->GetBoundsByIndex(2);
  1970. // Start shelf party.
  1971. model_->ToggleShelfParty();
  1972. {
  1973. const std::vector<size_t> not_partying = {1, 3};
  1974. EXPECT_EQ(not_partying, shelf_view_->visible_views_indices());
  1975. }
  1976. test_api_->RunMessageLoopUntilAnimationsDone();
  1977. EXPECT_TRUE(test_api_->GetBoundsByIndex(0).IsEmpty());
  1978. EXPECT_TRUE(test_api_->GetBoundsByIndex(2).IsEmpty());
  1979. // End shelf party.
  1980. model_->ToggleShelfParty();
  1981. {
  1982. const std::vector<size_t> not_partying = {0, 1, 2, 3};
  1983. EXPECT_EQ(not_partying, shelf_view_->visible_views_indices());
  1984. }
  1985. test_api_->RunMessageLoopUntilAnimationsDone();
  1986. EXPECT_EQ(initial_bounds0, test_api_->GetBoundsByIndex(0));
  1987. EXPECT_EQ(initial_bounds2, test_api_->GetBoundsByIndex(2));
  1988. }
  1989. // Test class that tests both context and application menus.
  1990. class ShelfViewMenuTest : public ShelfViewTest,
  1991. public testing::WithParamInterface<bool> {
  1992. public:
  1993. ShelfViewMenuTest() = default;
  1994. ShelfViewMenuTest(const ShelfViewMenuTest&) = delete;
  1995. ShelfViewMenuTest& operator=(const ShelfViewMenuTest&) = delete;
  1996. ~ShelfViewMenuTest() override = default;
  1997. };
  1998. INSTANTIATE_TEST_SUITE_P(All, ShelfViewMenuTest, testing::Bool());
  1999. // Tests that menu anchor points are aligned with the shelf button bounds.
  2000. TEST_P(ShelfViewMenuTest, ShelfViewMenuAnchorPoint) {
  2001. const ShelfAppButton* shelf_button = GetButtonByID(AddApp());
  2002. const bool context_menu = GetParam();
  2003. EXPECT_EQ(ShelfAlignment::kBottom, GetPrimaryShelf()->alignment());
  2004. // Test for bottom shelf.
  2005. EXPECT_EQ(
  2006. shelf_button->GetBoundsInScreen().y(),
  2007. test_api_->GetMenuAnchorRect(*shelf_button, gfx::Point(), context_menu)
  2008. .y());
  2009. // Test for left shelf.
  2010. GetPrimaryShelf()->SetAlignment(ShelfAlignment::kLeft);
  2011. EXPECT_EQ(
  2012. shelf_button->GetBoundsInScreen().x(),
  2013. test_api_->GetMenuAnchorRect(*shelf_button, gfx::Point(), context_menu)
  2014. .x());
  2015. // Test for right shelf.
  2016. GetPrimaryShelf()->SetAlignment(ShelfAlignment::kRight);
  2017. EXPECT_EQ(
  2018. shelf_button->GetBoundsInScreen().x(),
  2019. test_api_->GetMenuAnchorRect(*shelf_button, gfx::Point(), context_menu)
  2020. .x());
  2021. }
  2022. // Tests that an item has a notification badge indicator when the notification
  2023. // is added and removed.
  2024. TEST_F(ShelfViewTest, ItemHasCorrectNotificationBadgeIndicator) {
  2025. const ShelfID item_id = AddApp();
  2026. const ShelfAppButton* shelf_app_button = GetButtonByID(item_id);
  2027. EXPECT_FALSE(GetItemByID(item_id).has_notification);
  2028. EXPECT_FALSE(shelf_app_button->state() & ShelfAppButton::STATE_NOTIFICATION);
  2029. // Add a notification for the new shelf item.
  2030. model_->UpdateItemNotification(item_id.app_id, true /* has_badge */);
  2031. EXPECT_TRUE(GetItemByID(item_id).has_notification);
  2032. EXPECT_TRUE(shelf_app_button->state() & ShelfAppButton::STATE_NOTIFICATION);
  2033. // Remove notification.
  2034. model_->UpdateItemNotification(item_id.app_id, false /* has_badge */);
  2035. EXPECT_FALSE(GetItemByID(item_id).has_notification);
  2036. EXPECT_FALSE(shelf_app_button->state() & ShelfAppButton::STATE_NOTIFICATION);
  2037. }
  2038. class GhostImageShelfViewTest : public ShelfViewTest {
  2039. public:
  2040. GhostImageShelfViewTest() = default;
  2041. GhostImageShelfViewTest(const GhostImageShelfViewTest&) = delete;
  2042. GhostImageShelfViewTest& operator=(const GhostImageShelfViewTest&) = delete;
  2043. ~GhostImageShelfViewTest() override = default;
  2044. void StartDrag(ShelfAppButton* dragged) {
  2045. ASSERT_TRUE(dragged);
  2046. ui::test::EventGenerator* generator = GetEventGenerator();
  2047. generator->set_current_screen_location(
  2048. dragged->GetBoundsInScreen().CenterPoint());
  2049. generator->PressTouch();
  2050. ASSERT_TRUE(dragged->FireDragTimerForTest());
  2051. }
  2052. };
  2053. // Tests that the ghost image shows during a drag operation.
  2054. TEST_F(GhostImageShelfViewTest, ShowGhostImageOnDrag) {
  2055. std::vector<std::pair<ShelfID, views::View*>> id_map;
  2056. SetupForDragTest(&id_map);
  2057. ShelfAppButton* first_app = GetButtonByID(id_map[0].first);
  2058. StartDrag(first_app);
  2059. EXPECT_EQ(0, GetHapticTickEventsCount());
  2060. EXPECT_TRUE(first_app->state() & ShelfAppButton::STATE_DRAGGING);
  2061. EXPECT_FALSE(shelf_view_->drag_view());
  2062. EXPECT_FALSE(shelf_view_->current_ghost_view_index().has_value());
  2063. ShelfID second_app_id = id_map[1].first;
  2064. GetEventGenerator()->MoveTouch(GetButtonCenter(second_app_id));
  2065. EXPECT_TRUE(first_app->state() & ShelfAppButton::STATE_DRAGGING);
  2066. EXPECT_TRUE(shelf_view_->drag_view());
  2067. EXPECT_EQ(1u, shelf_view_->current_ghost_view_index());
  2068. GetEventGenerator()->ReleaseTouch();
  2069. EXPECT_EQ(0, GetHapticTickEventsCount());
  2070. EXPECT_FALSE(first_app->state() & ShelfAppButton::STATE_DRAGGING);
  2071. EXPECT_FALSE(shelf_view_->drag_view());
  2072. EXPECT_FALSE(shelf_view_->current_ghost_view_index().has_value());
  2073. }
  2074. // Tests that the ghost image is removed if the app is dragged outide of the
  2075. // bounds of the shelf.
  2076. TEST_F(GhostImageShelfViewTest, RemoveGhostImageForRipOffDrag) {
  2077. std::vector<std::pair<ShelfID, views::View*>> id_map;
  2078. SetupForDragTest(&id_map);
  2079. ShelfAppButton* first_app = GetButtonByID(id_map[0].first);
  2080. StartDrag(first_app);
  2081. EXPECT_EQ(0, GetHapticTickEventsCount());
  2082. EXPECT_TRUE(first_app->state() & ShelfAppButton::STATE_DRAGGING);
  2083. EXPECT_FALSE(shelf_view_->drag_view());
  2084. EXPECT_FALSE(shelf_view_->current_ghost_view_index().has_value());
  2085. ShelfID second_app_id = id_map[1].first;
  2086. GetEventGenerator()->MoveTouch(GetButtonCenter(second_app_id));
  2087. EXPECT_TRUE(first_app->state() & ShelfAppButton::STATE_DRAGGING);
  2088. EXPECT_TRUE(shelf_view_->drag_view());
  2089. EXPECT_EQ(1u, shelf_view_->current_ghost_view_index());
  2090. // The rip off threshold. Taken from |kRipOffDistance| in shelf_view.cc.
  2091. constexpr int kRipOffDistance = 48;
  2092. // Drag off the shelf to trigger rip off drag.
  2093. GetEventGenerator()->MoveTouch(shelf_view_->GetBoundsInScreen().top_center());
  2094. GetEventGenerator()->MoveTouchBy(0, -kRipOffDistance - 1);
  2095. EXPECT_TRUE(first_app->state() & ShelfAppButton::STATE_DRAGGING);
  2096. EXPECT_TRUE(shelf_view_->drag_view());
  2097. EXPECT_FALSE(shelf_view_->current_ghost_view_index().has_value());
  2098. GetEventGenerator()->ReleaseTouch();
  2099. EXPECT_EQ(0, GetHapticTickEventsCount());
  2100. EXPECT_FALSE(first_app->state() & ShelfAppButton::STATE_DRAGGING);
  2101. EXPECT_FALSE(shelf_view_->drag_view());
  2102. EXPECT_FALSE(shelf_view_->current_ghost_view_index().has_value());
  2103. }
  2104. // Tests that the ghost image is reinserted if the app is dragged within the
  2105. // bounds of the shelf after a rip off.
  2106. TEST_F(GhostImageShelfViewTest, ReinsertGhostImageAfterRipOffDrag) {
  2107. std::vector<std::pair<ShelfID, views::View*>> id_map;
  2108. SetupForDragTest(&id_map);
  2109. ShelfAppButton* first_app = GetButtonByID(id_map[0].first);
  2110. StartDrag(first_app);
  2111. EXPECT_EQ(0, GetHapticTickEventsCount());
  2112. EXPECT_TRUE(first_app->state() & ShelfAppButton::STATE_DRAGGING);
  2113. EXPECT_FALSE(shelf_view_->drag_view());
  2114. EXPECT_FALSE(shelf_view_->current_ghost_view_index().has_value());
  2115. ShelfID second_app_id = id_map[1].first;
  2116. GetEventGenerator()->MoveTouch(GetButtonCenter(second_app_id));
  2117. EXPECT_TRUE(first_app->state() & ShelfAppButton::STATE_DRAGGING);
  2118. EXPECT_TRUE(shelf_view_->drag_view());
  2119. EXPECT_EQ(1u, shelf_view_->current_ghost_view_index());
  2120. // The rip off threshold. Taken from |kRipOffDistance| in shelf_view.cc.
  2121. constexpr int kRipOffDistance = 48;
  2122. // Drag off the shelf to trigger rip off drag.
  2123. GetEventGenerator()->MoveTouch(shelf_view_->GetBoundsInScreen().top_center());
  2124. GetEventGenerator()->MoveTouchBy(0, -kRipOffDistance - 1);
  2125. EXPECT_TRUE(first_app->state() & ShelfAppButton::STATE_DRAGGING);
  2126. EXPECT_TRUE(shelf_view_->drag_view());
  2127. EXPECT_FALSE(shelf_view_->current_ghost_view_index().has_value());
  2128. GetEventGenerator()->MoveTouch(GetButtonCenter(second_app_id));
  2129. EXPECT_TRUE(first_app->state() & ShelfAppButton::STATE_DRAGGING);
  2130. EXPECT_TRUE(shelf_view_->drag_view());
  2131. EXPECT_EQ(1u, shelf_view_->current_ghost_view_index());
  2132. GetEventGenerator()->ReleaseTouch();
  2133. EXPECT_EQ(0, GetHapticTickEventsCount());
  2134. EXPECT_FALSE(first_app->state() & ShelfAppButton::STATE_DRAGGING);
  2135. EXPECT_FALSE(shelf_view_->drag_view());
  2136. EXPECT_FALSE(shelf_view_->current_ghost_view_index().has_value());
  2137. }
  2138. class ShelfViewVisibleBoundsTest : public ShelfViewTest,
  2139. public testing::WithParamInterface<bool> {
  2140. public:
  2141. ShelfViewVisibleBoundsTest() : scoped_locale_(GetParam() ? "he" : "") {}
  2142. ShelfViewVisibleBoundsTest(const ShelfViewVisibleBoundsTest&) = delete;
  2143. ShelfViewVisibleBoundsTest& operator=(const ShelfViewVisibleBoundsTest&) =
  2144. delete;
  2145. void CheckAllItemsAreInBounds() {
  2146. gfx::Rect visible_bounds = shelf_view_->GetVisibleItemsBoundsInScreen();
  2147. gfx::Rect shelf_bounds = shelf_view_->GetBoundsInScreen();
  2148. EXPECT_TRUE(shelf_bounds.Contains(visible_bounds));
  2149. for (size_t i = 0; i < test_api_->GetButtonCount(); ++i)
  2150. if (ShelfAppButton* button = test_api_->GetButton(i)) {
  2151. if (button->GetVisible())
  2152. EXPECT_TRUE(visible_bounds.Contains(button->GetBoundsInScreen()));
  2153. }
  2154. }
  2155. private:
  2156. // Restores locale to the default when destructor is called.
  2157. base::test::ScopedRestoreICUDefaultLocale scoped_locale_;
  2158. };
  2159. TEST_P(ShelfViewVisibleBoundsTest, ItemsAreInBounds) {
  2160. // Adding elements leaving some empty space.
  2161. for (int i = 0; i < 3; i++) {
  2162. AddAppShortcut();
  2163. }
  2164. test_api_->RunMessageLoopUntilAnimationsDone();
  2165. CheckAllItemsAreInBounds();
  2166. }
  2167. INSTANTIATE_TEST_SUITE_P(LtrRtl, ShelfViewVisibleBoundsTest, testing::Bool());
  2168. namespace {
  2169. // An InkDrop implementation that wraps another InkDrop instance to keep track
  2170. // of state changes requested on it. Note that this will only track transitions
  2171. // routed through AnimateToState() and not the ones performed directly on the
  2172. // ripple inside the contained |ink_drop|.
  2173. class InkDropSpy : public views::InkDrop {
  2174. public:
  2175. explicit InkDropSpy(std::unique_ptr<views::InkDrop> ink_drop)
  2176. : ink_drop_(std::move(ink_drop)) {}
  2177. InkDropSpy(const InkDropSpy&) = delete;
  2178. InkDropSpy& operator=(const InkDropSpy&) = delete;
  2179. ~InkDropSpy() override = default;
  2180. std::vector<views::InkDropState> GetAndResetRequestedStates() {
  2181. std::vector<views::InkDropState> requested_states;
  2182. requested_states.swap(requested_states_);
  2183. return requested_states;
  2184. }
  2185. // views::InkDrop:
  2186. void HostSizeChanged(const gfx::Size& new_size) override {
  2187. ink_drop_->HostSizeChanged(new_size);
  2188. }
  2189. void HostTransformChanged(const gfx::Transform& new_transform) override {
  2190. ink_drop_->HostTransformChanged(new_transform);
  2191. }
  2192. views::InkDropState GetTargetInkDropState() const override {
  2193. return ink_drop_->GetTargetInkDropState();
  2194. }
  2195. void AnimateToState(views::InkDropState ink_drop_state) override {
  2196. requested_states_.push_back(ink_drop_state);
  2197. ink_drop_->AnimateToState(ink_drop_state);
  2198. }
  2199. void SetHoverHighlightFadeDuration(base::TimeDelta duration) override {
  2200. ink_drop_->SetHoverHighlightFadeDuration(duration);
  2201. }
  2202. void UseDefaultHoverHighlightFadeDuration() override {
  2203. ink_drop_->UseDefaultHoverHighlightFadeDuration();
  2204. }
  2205. void SnapToActivated() override { ink_drop_->SnapToActivated(); }
  2206. void SnapToHidden() override { ink_drop_->SnapToHidden(); }
  2207. void SetHovered(bool is_hovered) override {
  2208. ink_drop_->SetHovered(is_hovered);
  2209. }
  2210. void SetFocused(bool is_focused) override {
  2211. ink_drop_->SetFocused(is_focused);
  2212. }
  2213. bool IsHighlightFadingInOrVisible() const override {
  2214. return ink_drop_->IsHighlightFadingInOrVisible();
  2215. }
  2216. void SetShowHighlightOnHover(bool show_highlight_on_hover) override {
  2217. ink_drop_->SetShowHighlightOnHover(show_highlight_on_hover);
  2218. }
  2219. void SetShowHighlightOnFocus(bool show_highlight_on_focus) override {
  2220. ink_drop_->SetShowHighlightOnFocus(show_highlight_on_focus);
  2221. }
  2222. std::unique_ptr<views::InkDrop> ink_drop_;
  2223. std::vector<views::InkDropState> requested_states_;
  2224. };
  2225. // A ShelfItemDelegate that returns a menu for the shelf item.
  2226. class ListMenuShelfItemDelegate : public ShelfItemDelegate {
  2227. public:
  2228. ListMenuShelfItemDelegate() : ShelfItemDelegate(ShelfID()) {}
  2229. ListMenuShelfItemDelegate(const ListMenuShelfItemDelegate&) = delete;
  2230. ListMenuShelfItemDelegate& operator=(const ListMenuShelfItemDelegate&) =
  2231. delete;
  2232. ~ListMenuShelfItemDelegate() override = default;
  2233. private:
  2234. // ShelfItemDelegate:
  2235. void ItemSelected(std::unique_ptr<ui::Event> event,
  2236. int64_t display_id,
  2237. ShelfLaunchSource source,
  2238. ItemSelectedCallback callback,
  2239. const ItemFilterPredicate& filter_predicate) override {
  2240. // Two items are needed to show a menu; the data in the items is not tested.
  2241. std::move(callback).Run(SHELF_ACTION_NONE, {{}, {}});
  2242. }
  2243. void ExecuteCommand(bool, int64_t, int32_t, int64_t) override {}
  2244. void Close() override {}
  2245. };
  2246. } // namespace
  2247. // Test fixture for testing material design ink drop ripples on shelf.
  2248. class ShelfViewInkDropTest : public ShelfViewTest {
  2249. public:
  2250. ShelfViewInkDropTest() = default;
  2251. ShelfViewInkDropTest(const ShelfViewInkDropTest&) = delete;
  2252. ShelfViewInkDropTest& operator=(const ShelfViewInkDropTest&) = delete;
  2253. ~ShelfViewInkDropTest() override = default;
  2254. protected:
  2255. void InitHomeButtonInkDrop() {
  2256. home_button_ = GetPrimaryShelf()->navigation_widget()->GetHomeButton();
  2257. auto home_button_ink_drop = std::make_unique<InkDropSpy>(
  2258. views::InkDrop::CreateInkDropWithoutAutoHighlight(
  2259. views::InkDrop::Get(home_button_)));
  2260. home_button_ink_drop_ = home_button_ink_drop.get();
  2261. views::test::InkDropHostTestApi(views::InkDrop::Get(home_button_))
  2262. .SetInkDrop(std::move(home_button_ink_drop), false);
  2263. }
  2264. void InitBrowserButtonInkDrop() {
  2265. browser_button_ = test_api_->GetButton(0);
  2266. auto ink_drop_impl = std::make_unique<views::InkDropImpl>(
  2267. views::InkDrop::Get(browser_button_), browser_button_->size(),
  2268. views::InkDropImpl::AutoHighlightMode::NONE);
  2269. browser_button_ink_drop_impl_ = ink_drop_impl.get();
  2270. auto browser_button_ink_drop =
  2271. std::make_unique<InkDropSpy>(std::move(ink_drop_impl));
  2272. browser_button_ink_drop_ = browser_button_ink_drop.get();
  2273. views::test::InkDropHostTestApi(views::InkDrop::Get(browser_button_))
  2274. .SetInkDrop(std::move(browser_button_ink_drop));
  2275. }
  2276. HomeButton* home_button_ = nullptr;
  2277. InkDropSpy* home_button_ink_drop_ = nullptr;
  2278. ShelfAppButton* browser_button_ = nullptr;
  2279. InkDropSpy* browser_button_ink_drop_ = nullptr;
  2280. views::InkDropImpl* browser_button_ink_drop_impl_ = nullptr;
  2281. };
  2282. // Tests that changing visibility of the app list transitions home button's
  2283. // ink drop states correctly.
  2284. TEST_F(ShelfViewInkDropTest, HomeButtonWhenVisibilityChanges) {
  2285. InitHomeButtonInkDrop();
  2286. GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
  2287. EXPECT_EQ(views::InkDropState::ACTIVATED,
  2288. home_button_ink_drop_->GetTargetInkDropState());
  2289. EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(),
  2290. ElementsAre(views::InkDropState::ACTIVATED));
  2291. GetAppListTestHelper()->DismissAndRunLoop();
  2292. EXPECT_EQ(views::InkDropState::HIDDEN,
  2293. home_button_ink_drop_->GetTargetInkDropState());
  2294. EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(),
  2295. ElementsAre(views::InkDropState::DEACTIVATED));
  2296. }
  2297. // Tests that when the app list is hidden, mouse press on the home button,
  2298. // which shows the app list, transitions ink drop states correctly. Also, tests
  2299. // that mouse drag and mouse release does not affect the ink drop state.
  2300. TEST_F(ShelfViewInkDropTest, HomeButtonMouseEventsWhenHidden) {
  2301. InitHomeButtonInkDrop();
  2302. ui::test::EventGenerator* generator = GetEventGenerator();
  2303. generator->MoveMouseTo(home_button_->GetBoundsInScreen().CenterPoint());
  2304. // Mouse press on the button, which shows the app list, should end up in the
  2305. // activated state.
  2306. generator->PressLeftButton();
  2307. GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
  2308. EXPECT_EQ(views::InkDropState::ACTIVATED,
  2309. home_button_ink_drop_->GetTargetInkDropState());
  2310. EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(),
  2311. ElementsAre(views::InkDropState::ACTION_PENDING,
  2312. views::InkDropState::ACTIVATED));
  2313. // Dragging mouse out and back and releasing the button should not change the
  2314. // ink drop state.
  2315. generator->MoveMouseBy(home_button_->width(), 0);
  2316. generator->MoveMouseBy(-home_button_->width(), 0);
  2317. generator->ReleaseLeftButton();
  2318. EXPECT_EQ(views::InkDropState::ACTIVATED,
  2319. home_button_ink_drop_->GetTargetInkDropState());
  2320. EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(), IsEmpty());
  2321. }
  2322. // Tests that when the app list is visible, mouse press on the home button,
  2323. // which dismisses the app list, transitions ink drop states correctly. Also,
  2324. // tests that mouse drag and mouse release does not affect the ink drop state.
  2325. TEST_F(ShelfViewInkDropTest, HomeButtonMouseEventsWhenVisible) {
  2326. InitHomeButtonInkDrop();
  2327. GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
  2328. EXPECT_EQ(views::InkDropState::ACTIVATED,
  2329. home_button_ink_drop_->GetTargetInkDropState());
  2330. EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(),
  2331. ElementsAre(views::InkDropState::ACTIVATED));
  2332. // Mouse press on the button, which dismisses the app list, should end up in
  2333. // the hidden state.
  2334. ui::test::EventGenerator* generator = GetEventGenerator();
  2335. generator->MoveMouseTo(home_button_->GetBoundsInScreen().CenterPoint());
  2336. generator->PressLeftButton();
  2337. base::RunLoop().RunUntilIdle();
  2338. EXPECT_EQ(views::InkDropState::HIDDEN,
  2339. home_button_ink_drop_->GetTargetInkDropState());
  2340. EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(),
  2341. ElementsAre(views::InkDropState::ACTION_PENDING,
  2342. views::InkDropState::DEACTIVATED));
  2343. // Dragging mouse out and back and releasing the button should not change the
  2344. // ink drop state.
  2345. generator->MoveMouseBy(home_button_->width(), 0);
  2346. generator->MoveMouseBy(-home_button_->width(), 0);
  2347. generator->ReleaseLeftButton();
  2348. EXPECT_EQ(views::InkDropState::HIDDEN,
  2349. home_button_ink_drop_->GetTargetInkDropState());
  2350. EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(), IsEmpty());
  2351. }
  2352. // Tests that when the app list is hidden, tapping on the home button
  2353. // transitions ink drop states correctly.
  2354. TEST_F(ShelfViewInkDropTest, HomeButtonGestureTapWhenHidden) {
  2355. InitHomeButtonInkDrop();
  2356. ui::test::EventGenerator* generator = GetEventGenerator();
  2357. generator->MoveMouseTo(home_button_->GetBoundsInScreen().CenterPoint());
  2358. // Touch press on the button should end up in the pending state.
  2359. generator->PressTouch();
  2360. EXPECT_EQ(views::InkDropState::ACTION_PENDING,
  2361. home_button_ink_drop_->GetTargetInkDropState());
  2362. EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(),
  2363. ElementsAre(views::InkDropState::ACTION_PENDING));
  2364. // Touch release on the button, which shows the app list, should end up in the
  2365. // activated state.
  2366. generator->ReleaseTouch();
  2367. GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
  2368. EXPECT_EQ(views::InkDropState::ACTIVATED,
  2369. home_button_ink_drop_->GetTargetInkDropState());
  2370. EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(),
  2371. ElementsAre(views::InkDropState::ACTION_TRIGGERED,
  2372. views::InkDropState::ACTIVATED));
  2373. }
  2374. // Tests that when the app list is visible, tapping on the home button
  2375. // transitions ink drop states correctly.
  2376. TEST_F(ShelfViewInkDropTest, HomeButtonGestureTapWhenVisible) {
  2377. InitHomeButtonInkDrop();
  2378. GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
  2379. EXPECT_EQ(views::InkDropState::ACTIVATED,
  2380. home_button_ink_drop_->GetTargetInkDropState());
  2381. EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(),
  2382. ElementsAre(views::InkDropState::ACTIVATED));
  2383. // Touch press and release on the button, which dismisses the app list, should
  2384. // end up in the hidden state.
  2385. ui::test::EventGenerator* generator = GetEventGenerator();
  2386. generator->MoveMouseTo(home_button_->GetBoundsInScreen().CenterPoint());
  2387. generator->PressTouch();
  2388. generator->ReleaseTouch();
  2389. GetAppListTestHelper()->WaitUntilIdle();
  2390. EXPECT_EQ(views::InkDropState::HIDDEN,
  2391. home_button_ink_drop_->GetTargetInkDropState());
  2392. EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(),
  2393. ElementsAre(views::InkDropState::DEACTIVATED));
  2394. }
  2395. // Tests that when the app list is hidden, tapping down on the home button
  2396. // and dragging the touch point transitions ink drop states correctly.
  2397. TEST_F(ShelfViewInkDropTest, HomeButtonGestureTapDragWhenHidden) {
  2398. InitHomeButtonInkDrop();
  2399. ui::test::EventGenerator* generator = GetEventGenerator();
  2400. gfx::Point touch_location = home_button_->GetBoundsInScreen().CenterPoint();
  2401. generator->MoveMouseTo(touch_location);
  2402. // Touch press on the button should end up in the pending state.
  2403. generator->PressTouch();
  2404. EXPECT_EQ(views::InkDropState::ACTION_PENDING,
  2405. home_button_ink_drop_->GetTargetInkDropState());
  2406. EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(),
  2407. ElementsAre(views::InkDropState::ACTION_PENDING));
  2408. // Dragging the touch point should hide the pending ink drop.
  2409. touch_location.Offset(home_button_->width(), 0);
  2410. generator->MoveTouch(touch_location);
  2411. EXPECT_EQ(views::InkDropState::HIDDEN,
  2412. home_button_ink_drop_->GetTargetInkDropState());
  2413. EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(),
  2414. ElementsAre(views::InkDropState::ACTION_TRIGGERED));
  2415. // Touch release should not change the ink drop state.
  2416. generator->ReleaseTouch();
  2417. EXPECT_EQ(views::InkDropState::HIDDEN,
  2418. home_button_ink_drop_->GetTargetInkDropState());
  2419. EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(), IsEmpty());
  2420. }
  2421. // Tests that when the app list is visible, tapping down on the home button
  2422. // and dragging the touch point will not change ink drop states.
  2423. TEST_F(ShelfViewInkDropTest, HomeButtonGestureTapDragWhenVisible) {
  2424. InitHomeButtonInkDrop();
  2425. GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
  2426. EXPECT_EQ(views::InkDropState::ACTIVATED,
  2427. home_button_ink_drop_->GetTargetInkDropState());
  2428. EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(),
  2429. ElementsAre(views::InkDropState::ACTIVATED));
  2430. // Touch press on the button, dragging the touch point, and releasing, which
  2431. // will not dismisses the app list, should end up in the |ACTIVATED| state.
  2432. ui::test::EventGenerator* generator = GetEventGenerator();
  2433. gfx::Point touch_location = home_button_->GetBoundsInScreen().CenterPoint();
  2434. generator->MoveMouseTo(touch_location);
  2435. // Touch press on the button should not change the ink drop state.
  2436. generator->PressTouch();
  2437. EXPECT_EQ(views::InkDropState::ACTIVATED,
  2438. home_button_ink_drop_->GetTargetInkDropState());
  2439. EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(), IsEmpty());
  2440. // Dragging the touch point should not hide the pending ink drop.
  2441. touch_location.Offset(home_button_->width(), 0);
  2442. generator->MoveTouch(touch_location);
  2443. EXPECT_EQ(views::InkDropState::ACTIVATED,
  2444. home_button_ink_drop_->GetTargetInkDropState());
  2445. EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(), IsEmpty());
  2446. // Touch release should not change the ink drop state.
  2447. generator->ReleaseTouch();
  2448. EXPECT_EQ(views::InkDropState::ACTIVATED,
  2449. home_button_ink_drop_->GetTargetInkDropState());
  2450. EXPECT_THAT(home_button_ink_drop_->GetAndResetRequestedStates(), IsEmpty());
  2451. }
  2452. // Tests that clicking on a shelf item that does not show a menu transitions ink
  2453. // drop states correctly.
  2454. TEST_F(ShelfViewInkDropTest, ShelfButtonWithoutMenuPressRelease) {
  2455. InitBrowserButtonInkDrop();
  2456. views::Button* button = browser_button_;
  2457. gfx::Point mouse_location = button->GetLocalBounds().CenterPoint();
  2458. ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED, mouse_location,
  2459. mouse_location, ui::EventTimeForNow(),
  2460. ui::EF_LEFT_MOUSE_BUTTON, 0);
  2461. button->OnMousePressed(press_event);
  2462. EXPECT_EQ(views::InkDropState::ACTION_PENDING,
  2463. browser_button_ink_drop_->GetTargetInkDropState());
  2464. EXPECT_THAT(browser_button_ink_drop_->GetAndResetRequestedStates(),
  2465. ElementsAre(views::InkDropState::ACTION_PENDING));
  2466. ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, mouse_location,
  2467. mouse_location, ui::EventTimeForNow(),
  2468. ui::EF_LEFT_MOUSE_BUTTON, 0);
  2469. button->OnMouseReleased(release_event);
  2470. EXPECT_EQ(views::InkDropState::HIDDEN,
  2471. browser_button_ink_drop_->GetTargetInkDropState());
  2472. EXPECT_THAT(browser_button_ink_drop_->GetAndResetRequestedStates(),
  2473. ElementsAre(views::InkDropState::ACTION_TRIGGERED));
  2474. }
  2475. // Verifies the shelf view's behavior when opening an empty context menu.
  2476. TEST_F(ShelfViewInkDropTest, ShowEmptyShelfAppContextMenu) {
  2477. InitBrowserButtonInkDrop();
  2478. // Set the shelf item delegate to generate an empty context menu. Note that if
  2479. // we do not specify the shelf item delegate, a default context menu which is
  2480. // non-empty is created.
  2481. const int browser_shortcut_index =
  2482. ShelfModel::Get()->GetItemIndexForType(TYPE_BROWSER_SHORTCUT);
  2483. model_->ReplaceShelfItemDelegate(model_->items()[browser_shortcut_index].id,
  2484. std::make_unique<EmptyContextMenuBuilder>());
  2485. // Right mouse click at the browser button.
  2486. const gfx::Rect button_bounds_in_screen =
  2487. browser_button_->GetBoundsInScreen();
  2488. const gfx::Point button_bounds_center = button_bounds_in_screen.CenterPoint();
  2489. GetEventGenerator()->MoveMouseTo(button_bounds_center);
  2490. GetEventGenerator()->ClickRightButton();
  2491. // Verify that the context menu does not show and the inkdrop is hidden.
  2492. auto* shelf_view = GetPrimaryShelf()->GetShelfViewForTesting();
  2493. EXPECT_FALSE(shelf_view->IsShowingMenu());
  2494. EXPECT_EQ(views::InkDropState::HIDDEN,
  2495. browser_button_ink_drop_->GetTargetInkDropState());
  2496. // Press a point outside of the browser button. Then move the mouse without
  2497. // release.
  2498. gfx::Point press_point(button_bounds_in_screen.right() + 5,
  2499. button_bounds_center.y());
  2500. GetEventGenerator()->MoveMouseTo(press_point);
  2501. GetEventGenerator()->PressLeftButton();
  2502. press_point.Offset(/*x_delta=*/0, /*y_delta=*/-10);
  2503. GetEventGenerator()->MoveMouseTo(press_point);
  2504. // Verify that the browser button is not the mouse handler, which means that
  2505. // the browser button is not under drag.
  2506. EXPECT_FALSE(shelf_view->IsDraggedView(browser_button_));
  2507. }
  2508. // Tests that dragging outside of a shelf item transitions ink drop states
  2509. // correctly.
  2510. TEST_F(ShelfViewInkDropTest, ShelfButtonWithoutMenuPressDragReleaseOutside) {
  2511. InitBrowserButtonInkDrop();
  2512. views::Button* button = browser_button_;
  2513. gfx::Point mouse_location = button->GetLocalBounds().CenterPoint();
  2514. ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED, mouse_location,
  2515. mouse_location, ui::EventTimeForNow(),
  2516. ui::EF_LEFT_MOUSE_BUTTON, 0);
  2517. button->OnMousePressed(press_event);
  2518. EXPECT_EQ(views::InkDropState::ACTION_PENDING,
  2519. browser_button_ink_drop_->GetTargetInkDropState());
  2520. EXPECT_THAT(browser_button_ink_drop_->GetAndResetRequestedStates(),
  2521. ElementsAre(views::InkDropState::ACTION_PENDING));
  2522. mouse_location.Offset(test_api_->GetMinimumDragDistance() / 2, 0);
  2523. ui::MouseEvent drag_event_small(ui::ET_MOUSE_DRAGGED, mouse_location,
  2524. mouse_location, ui::EventTimeForNow(),
  2525. ui::EF_LEFT_MOUSE_BUTTON, 0);
  2526. button->OnMouseDragged(drag_event_small);
  2527. EXPECT_EQ(views::InkDropState::ACTION_PENDING,
  2528. browser_button_ink_drop_->GetTargetInkDropState());
  2529. EXPECT_THAT(browser_button_ink_drop_->GetAndResetRequestedStates(),
  2530. IsEmpty());
  2531. mouse_location.Offset(test_api_->GetMinimumDragDistance(), 0);
  2532. ui::MouseEvent drag_event_large(ui::ET_MOUSE_DRAGGED, mouse_location,
  2533. mouse_location, ui::EventTimeForNow(),
  2534. ui::EF_LEFT_MOUSE_BUTTON, 0);
  2535. button->OnMouseDragged(drag_event_large);
  2536. EXPECT_EQ(views::InkDropState::HIDDEN,
  2537. browser_button_ink_drop_->GetTargetInkDropState());
  2538. EXPECT_THAT(browser_button_ink_drop_->GetAndResetRequestedStates(),
  2539. ElementsAre(views::InkDropState::HIDDEN));
  2540. ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, mouse_location,
  2541. mouse_location, ui::EventTimeForNow(),
  2542. ui::EF_LEFT_MOUSE_BUTTON, 0);
  2543. button->OnMouseReleased(release_event);
  2544. EXPECT_EQ(views::InkDropState::HIDDEN,
  2545. browser_button_ink_drop_->GetTargetInkDropState());
  2546. EXPECT_THAT(browser_button_ink_drop_->GetAndResetRequestedStates(),
  2547. IsEmpty());
  2548. }
  2549. // Tests that dragging outside of a shelf item and back transitions ink drop
  2550. // states correctly.
  2551. TEST_F(ShelfViewInkDropTest, ShelfButtonWithoutMenuPressDragReleaseInside) {
  2552. InitBrowserButtonInkDrop();
  2553. views::Button* button = browser_button_;
  2554. gfx::Point mouse_location = button->GetLocalBounds().CenterPoint();
  2555. ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED, mouse_location,
  2556. mouse_location, ui::EventTimeForNow(),
  2557. ui::EF_LEFT_MOUSE_BUTTON, 0);
  2558. button->OnMousePressed(press_event);
  2559. EXPECT_EQ(views::InkDropState::ACTION_PENDING,
  2560. browser_button_ink_drop_->GetTargetInkDropState());
  2561. EXPECT_THAT(browser_button_ink_drop_->GetAndResetRequestedStates(),
  2562. ElementsAre(views::InkDropState::ACTION_PENDING));
  2563. mouse_location.Offset(test_api_->GetMinimumDragDistance() * 2, 0);
  2564. ui::MouseEvent drag_event_outside(ui::ET_MOUSE_DRAGGED, mouse_location,
  2565. mouse_location, ui::EventTimeForNow(),
  2566. ui::EF_LEFT_MOUSE_BUTTON, 0);
  2567. button->OnMouseDragged(drag_event_outside);
  2568. EXPECT_EQ(views::InkDropState::HIDDEN,
  2569. browser_button_ink_drop_->GetTargetInkDropState());
  2570. EXPECT_THAT(browser_button_ink_drop_->GetAndResetRequestedStates(),
  2571. ElementsAre(views::InkDropState::HIDDEN));
  2572. mouse_location.Offset(-test_api_->GetMinimumDragDistance() * 2, 0);
  2573. ui::MouseEvent drag_event_inside(ui::ET_MOUSE_DRAGGED, mouse_location,
  2574. mouse_location, ui::EventTimeForNow(),
  2575. ui::EF_LEFT_MOUSE_BUTTON, 0);
  2576. button->OnMouseDragged(drag_event_inside);
  2577. EXPECT_EQ(views::InkDropState::HIDDEN,
  2578. browser_button_ink_drop_->GetTargetInkDropState());
  2579. EXPECT_THAT(browser_button_ink_drop_->GetAndResetRequestedStates(),
  2580. IsEmpty());
  2581. ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, mouse_location,
  2582. mouse_location, ui::EventTimeForNow(),
  2583. ui::EF_LEFT_MOUSE_BUTTON, 0);
  2584. button->OnMouseReleased(release_event);
  2585. EXPECT_EQ(views::InkDropState::HIDDEN,
  2586. browser_button_ink_drop_->GetTargetInkDropState());
  2587. EXPECT_THAT(browser_button_ink_drop_->GetAndResetRequestedStates(),
  2588. IsEmpty());
  2589. }
  2590. // Tests that clicking on a shelf item that shows an app list menu transitions
  2591. // ink drop state correctly.
  2592. TEST_F(ShelfViewInkDropTest, ShelfButtonWithMenuPressRelease) {
  2593. InitBrowserButtonInkDrop();
  2594. // Set a delegate for the shelf item that returns an app list menu.
  2595. model_->ReplaceShelfItemDelegate(
  2596. model_->items()[0].id, std::make_unique<ListMenuShelfItemDelegate>());
  2597. views::Button* button = browser_button_;
  2598. gfx::Point mouse_location = button->GetLocalBounds().CenterPoint();
  2599. ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED, mouse_location,
  2600. mouse_location, ui::EventTimeForNow(),
  2601. ui::EF_LEFT_MOUSE_BUTTON, 0);
  2602. button->OnMousePressed(press_event);
  2603. EXPECT_EQ(views::InkDropState::ACTION_PENDING,
  2604. browser_button_ink_drop_->GetTargetInkDropState());
  2605. EXPECT_THAT(browser_button_ink_drop_->GetAndResetRequestedStates(),
  2606. ElementsAre(views::InkDropState::ACTION_PENDING));
  2607. // Mouse release will spawn a menu which we will then close.
  2608. ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED, mouse_location,
  2609. mouse_location, ui::EventTimeForNow(),
  2610. ui::EF_LEFT_MOUSE_BUTTON, 0);
  2611. button->OnMouseReleased(release_event);
  2612. EXPECT_EQ(views::InkDropState::ACTIVATED,
  2613. browser_button_ink_drop_->GetTargetInkDropState());
  2614. EXPECT_TRUE(test_api_->CloseMenu());
  2615. EXPECT_EQ(views::InkDropState::HIDDEN,
  2616. browser_button_ink_drop_->GetTargetInkDropState());
  2617. EXPECT_THAT(browser_button_ink_drop_->GetAndResetRequestedStates(),
  2618. ElementsAre(views::InkDropState::ACTIVATED,
  2619. views::InkDropState::DEACTIVATED));
  2620. }
  2621. TEST_F(ShelfViewInkDropTest, DismissingMenuWithDoubleClickDoesntShowInkDrop) {
  2622. ui::test::EventGenerator* generator = GetEventGenerator();
  2623. InitBrowserButtonInkDrop();
  2624. views::Button* button = browser_button_;
  2625. // Show a context menu on the home button.
  2626. generator->MoveMouseTo(shelf_view_->shelf_widget()
  2627. ->navigation_widget()
  2628. ->GetHomeButton()
  2629. ->GetBoundsInScreen()
  2630. .CenterPoint());
  2631. generator->PressRightButton();
  2632. generator->ReleaseRightButton();
  2633. EXPECT_TRUE(shelf_view_->IsShowingMenu());
  2634. // Now check that double-clicking on the browser button dismisses the context
  2635. // menu, and does not show an ink drop.
  2636. EXPECT_EQ(views::InkDropState::HIDDEN,
  2637. browser_button_ink_drop_->GetTargetInkDropState());
  2638. generator->MoveMouseTo(button->GetBoundsInScreen().CenterPoint());
  2639. generator->DoubleClickLeftButton();
  2640. EXPECT_FALSE(shelf_view_->IsShowingMenu());
  2641. EXPECT_EQ(views::InkDropState::HIDDEN,
  2642. browser_button_ink_drop_->GetTargetInkDropState());
  2643. }
  2644. // Tests that the shelf ink drop transforms when the host transforms. Regression
  2645. // test for https://crbug.com/1097044.
  2646. TEST_F(ShelfViewInkDropTest, ShelfButtonTransformed) {
  2647. InitBrowserButtonInkDrop();
  2648. ASSERT_TRUE(browser_button_ink_drop_impl_);
  2649. auto ink_drop_impl_test_api =
  2650. std::make_unique<views::test::InkDropImplTestApi>(
  2651. browser_button_ink_drop_impl_);
  2652. views::Button* button = browser_button_;
  2653. gfx::Transform transform;
  2654. transform.Translate(10, 0);
  2655. button->SetTransform(transform);
  2656. EXPECT_EQ(transform, ink_drop_impl_test_api->GetRootLayer()->transform());
  2657. button->SetTransform(gfx::Transform());
  2658. EXPECT_EQ(gfx::Transform(),
  2659. ink_drop_impl_test_api->GetRootLayer()->transform());
  2660. }
  2661. class ShelfViewFocusTest : public ShelfViewTest {
  2662. public:
  2663. ShelfViewFocusTest() = default;
  2664. ShelfViewFocusTest(const ShelfViewFocusTest&) = delete;
  2665. ShelfViewFocusTest& operator=(const ShelfViewFocusTest&) = delete;
  2666. ~ShelfViewFocusTest() override = default;
  2667. // AshTestBase:
  2668. void SetUp() override {
  2669. ShelfViewTest::SetUp();
  2670. // Add two app shortcuts for testing.
  2671. AddAppShortcut();
  2672. AddAppShortcut();
  2673. // Focus the home button.
  2674. Shelf* shelf = Shelf::ForWindow(Shell::GetPrimaryRootWindow());
  2675. shelf->shelf_focus_cycler()->FocusNavigation(false /* last_element */);
  2676. }
  2677. void DoTab() {
  2678. ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
  2679. generator.PressKey(ui::KeyboardCode::VKEY_TAB, ui::EF_NONE);
  2680. }
  2681. void DoShiftTab() {
  2682. ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
  2683. generator.PressKey(ui::KeyboardCode::VKEY_TAB, ui::EF_SHIFT_DOWN);
  2684. }
  2685. void DoEnter() {
  2686. ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
  2687. generator.PressKey(ui::KeyboardCode::VKEY_RETURN, ui::EF_NONE);
  2688. }
  2689. };
  2690. // Tests that the number of buttons is as expected and the shelf's widget
  2691. // intially has focus.
  2692. TEST_F(ShelfViewFocusTest, Basic) {
  2693. // There are five buttons, including 3 app buttons. The back button and
  2694. // launcher are always there, the browser shortcut is added in
  2695. // ShelfViewTest and the two test apps added in ShelfViewFocusTest.
  2696. EXPECT_EQ(3u, test_api_->GetButtonCount());
  2697. EXPECT_TRUE(GetPrimaryShelf()->navigation_widget()->IsActive());
  2698. // The home button is focused initially because the back button is only
  2699. // visible in tablet mode.
  2700. EXPECT_TRUE(shelf_view_->shelf_widget()
  2701. ->navigation_widget()
  2702. ->GetHomeButton()
  2703. ->HasFocus());
  2704. }
  2705. // Tests that the expected views have focus when cycling through shelf items
  2706. // with tab.
  2707. TEST_F(ShelfViewFocusTest, ForwardCycling) {
  2708. // Pressing tab once should advance focus to the next element after the
  2709. // home button, which is the first app.
  2710. DoTab();
  2711. EXPECT_TRUE(test_api_->GetViewAt(0)->HasFocus());
  2712. DoTab();
  2713. DoTab();
  2714. EXPECT_TRUE(test_api_->GetViewAt(2)->HasFocus());
  2715. }
  2716. // Tests that the expected views have focus when cycling backwards through shelf
  2717. // items with shift tab.
  2718. TEST_F(ShelfViewFocusTest, BackwardCycling) {
  2719. // The first element is currently focused. Let's advance to the last element
  2720. // first.
  2721. EXPECT_TRUE(shelf_view_->shelf_widget()
  2722. ->navigation_widget()
  2723. ->GetHomeButton()
  2724. ->HasFocus());
  2725. DoTab();
  2726. DoTab();
  2727. DoTab();
  2728. EXPECT_TRUE(test_api_->GetViewAt(2)->HasFocus());
  2729. // Pressing shift tab once should advance focus to the previous element.
  2730. DoShiftTab();
  2731. EXPECT_TRUE(test_api_->GetViewAt(1)->HasFocus());
  2732. }
  2733. // Verifies that focus moves as expected between the shelf and the status area.
  2734. TEST_F(ShelfViewFocusTest, FocusCyclingBetweenShelfAndStatusWidget) {
  2735. // The first element of the shelf (the home button) is focused at start.
  2736. EXPECT_TRUE(shelf_view_->shelf_widget()
  2737. ->navigation_widget()
  2738. ->GetHomeButton()
  2739. ->HasFocus());
  2740. // Focus the next few elements.
  2741. DoTab();
  2742. EXPECT_TRUE(test_api_->GetViewAt(0)->HasFocus());
  2743. DoTab();
  2744. EXPECT_TRUE(test_api_->GetViewAt(1)->HasFocus());
  2745. DoTab();
  2746. EXPECT_TRUE(test_api_->GetViewAt(2)->HasFocus());
  2747. // This is the last element. Tabbing once more should go into the status
  2748. // area. If calendar view is enabled it is focusing on the date tray.
  2749. DoTab();
  2750. ExpectNotFocused(shelf_view_);
  2751. ExpectFocused(status_area_);
  2752. // Shift-tab: we should be back at the last element in the shelf.
  2753. DoShiftTab();
  2754. EXPECT_TRUE(test_api_->GetViewAt(2)->HasFocus());
  2755. ExpectNotFocused(status_area_);
  2756. // Go into the status area again.
  2757. DoTab();
  2758. ExpectNotFocused(shelf_view_);
  2759. ExpectFocused(status_area_);
  2760. // If calendar view is enabled, move the focusing ring from the date tray to
  2761. // the unified tray.
  2762. if (features::IsCalendarViewEnabled()) {
  2763. DoTab();
  2764. ExpectNotFocused(shelf_view_);
  2765. ExpectFocused(status_area_);
  2766. }
  2767. // And keep going forward, now we should be cycling back to the first shelf
  2768. // element.
  2769. DoTab();
  2770. EXPECT_TRUE(shelf_view_->shelf_widget()
  2771. ->navigation_widget()
  2772. ->GetHomeButton()
  2773. ->HasFocus());
  2774. ExpectNotFocused(status_area_);
  2775. }
  2776. // Verifies that hitting the Esc key can consistently unfocus the shelf.
  2777. TEST_F(ShelfViewFocusTest, UnfocusWithEsc) {
  2778. // The home button is focused at start.
  2779. EXPECT_TRUE(shelf_view_->shelf_widget()
  2780. ->navigation_widget()
  2781. ->GetHomeButton()
  2782. ->HasFocus());
  2783. // Focus the status area.
  2784. DoShiftTab();
  2785. ExpectNotFocused(shelf_view_);
  2786. ExpectFocused(status_area_);
  2787. // If calendar view is enabled, move the focusing ring from the unified tray
  2788. // to the date tray.
  2789. if (features::IsCalendarViewEnabled()) {
  2790. DoShiftTab();
  2791. ExpectNotFocused(shelf_view_);
  2792. ExpectFocused(status_area_);
  2793. }
  2794. // Advance backwards to the last element of the shelf.
  2795. DoShiftTab();
  2796. ExpectNotFocused(status_area_);
  2797. ExpectFocused(shelf_view_);
  2798. // Press Escape. Nothing should be focused.
  2799. ui::KeyEvent key_event(ui::ET_KEY_PRESSED, ui::VKEY_ESCAPE, ui::EF_NONE);
  2800. shelf_view_->GetWidget()->OnKeyEvent(&key_event);
  2801. ExpectNotFocused(status_area_);
  2802. ExpectNotFocused(shelf_view_);
  2803. }
  2804. class ShelfViewFocusWithNoShelfNavigationTest : public ShelfViewFocusTest {
  2805. public:
  2806. ShelfViewFocusWithNoShelfNavigationTest() {
  2807. scoped_feature_list_.InitWithFeatures(
  2808. {features::kHideShelfControlsInTabletMode}, {});
  2809. }
  2810. ~ShelfViewFocusWithNoShelfNavigationTest() override = default;
  2811. private:
  2812. base::test::ScopedFeatureList scoped_feature_list_;
  2813. };
  2814. TEST_F(ShelfViewFocusWithNoShelfNavigationTest,
  2815. ShelfWithoutNavigationControls) {
  2816. // The home button is focused at start.
  2817. ASSERT_TRUE(GetPrimaryShelf()->navigation_widget()->GetHomeButton());
  2818. EXPECT_TRUE(shelf_view_->shelf_widget()
  2819. ->navigation_widget()
  2820. ->GetHomeButton()
  2821. ->HasFocus());
  2822. // Switch to tablet mode, which should hide navigation buttons.
  2823. Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
  2824. test_api_->RunMessageLoopUntilAnimationsDone();
  2825. ASSERT_FALSE(GetPrimaryShelf()->navigation_widget()->GetHomeButton());
  2826. ExpectFocused(status_area_);
  2827. // Verify focus cycling skips the navigation widget.
  2828. DoTab();
  2829. ExpectFocused(status_area_);
  2830. DoTab();
  2831. ExpectFocused(shelf_view_);
  2832. DoShiftTab();
  2833. ExpectFocused(status_area_);
  2834. }
  2835. class ShelfViewGestureTapTest : public ShelfViewTest {
  2836. public:
  2837. ShelfViewGestureTapTest()
  2838. : ShelfViewTest(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
  2839. ~ShelfViewGestureTapTest() override = default;
  2840. // ShelfViewTest:
  2841. void SetUp() override {
  2842. ShelfViewTest::SetUp();
  2843. app_icon1_ = GetButtonByID(AddAppShortcut());
  2844. app_icon2_ = GetButtonByID(AddAppShortcut());
  2845. }
  2846. views::InkDropState GetInkDropStateOfAppIcon1() const {
  2847. return views::InkDrop::Get(app_icon1_)
  2848. ->GetInkDrop()
  2849. ->GetTargetInkDropState();
  2850. }
  2851. protected:
  2852. ShelfAppButton* app_icon1_ = nullptr;
  2853. ShelfAppButton* app_icon2_ = nullptr;
  2854. };
  2855. // Verifies the shelf app button's inkdrop behavior when the mouse click
  2856. // occurs after gesture long press but before the end of gesture.
  2857. TEST_F(ShelfViewGestureTapTest, MouseClickInterruptionAfterGestureLongPress) {
  2858. const gfx::Point app_icon1_center_point =
  2859. app_icon1_->GetBoundsInScreen().CenterPoint();
  2860. GetEventGenerator()->PressTouch(app_icon1_center_point);
  2861. // Fast forward to generate the ET_GESTURE_SHOW_PRESS event.
  2862. task_environment()->FastForwardBy(base::Milliseconds(200));
  2863. // Fast forward to generate the ET_GESTURE_LONG_PRESS event to show the
  2864. // context menu.
  2865. task_environment()->FastForwardBy(base::Milliseconds(1000));
  2866. ASSERT_TRUE(shelf_view_->IsShowingMenu());
  2867. // Mouse click at `app_icon2_` while gesture pressing `app_icon1_`.
  2868. GetEventGenerator()->MoveMouseTo(
  2869. app_icon2_->GetBoundsInScreen().CenterPoint());
  2870. GetEventGenerator()->ClickLeftButton();
  2871. // Release the gesture press on `app_icon1_`.
  2872. GetEventGenerator()->set_current_screen_location(app_icon1_center_point);
  2873. GetEventGenerator()->ReleaseTouch();
  2874. // Verify that the context menu shows and `app_icon1_`'s inkdrop is activated.
  2875. EXPECT_TRUE(shelf_view_->IsShowingMenu());
  2876. EXPECT_EQ(views::InkDropState::ACTIVATED, GetInkDropStateOfAppIcon1());
  2877. // Click at the mouse left button at an empty space. Verify that the context
  2878. // menu is closed and the inkdrop is deactivated.
  2879. GetEventGenerator()->MoveMouseTo(GetPrimaryDisplay().bounds().CenterPoint());
  2880. GetEventGenerator()->ClickLeftButton();
  2881. EXPECT_FALSE(shelf_view_->IsShowingMenu());
  2882. EXPECT_EQ(views::InkDropState::HIDDEN, GetInkDropStateOfAppIcon1());
  2883. }
  2884. // Verifies that removing an item that is still waiting for the context menu
  2885. // model works as expected.
  2886. TEST_F(ShelfViewGestureTapTest, InterruptContextMenuShowByItemRemoval) {
  2887. // Add two shelf app buttons.
  2888. const ShelfID id1 = AddAppShortcut();
  2889. const ShelfID id2 = AddAppShortcut();
  2890. auto item_delegate_owned =
  2891. std::make_unique<AsyncContextMenuShelfItemDelegate>();
  2892. AsyncContextMenuShelfItemDelegate* item_delegate = item_delegate_owned.get();
  2893. model_->ReplaceShelfItemDelegate(id1, std::move(item_delegate_owned));
  2894. ShelfAppButton* app_button = GetButtonByID(id1);
  2895. GetEventGenerator()->MoveTouch(app_button->GetBoundsInScreen().CenterPoint());
  2896. GetEventGenerator()->PressTouch();
  2897. // Fast forward to generate the ET_GESTURE_SHOW_PRESS event.
  2898. task_environment()->FastForwardBy(base::Milliseconds(200));
  2899. // Fast forward to generate the ET_GESTURE_LONG_PRESS event to show the
  2900. // context menu.
  2901. task_environment()->FastForwardBy(base::Milliseconds(1000));
  2902. EXPECT_TRUE(item_delegate->HasPendingContextMenuCallback());
  2903. // Remove the shelf item indexed by `id` before handling the pending context
  2904. // menu model request.
  2905. const int index = ShelfModel::Get()->ItemIndexByID(id1);
  2906. ShelfModel::Get()->RemoveItemAt(index);
  2907. EXPECT_FALSE(shelf_view_->drag_view());
  2908. // Initialize the mouse drag on the shelf app button specified by `id2`.
  2909. ShelfAppButton* app_button2 = GetButtonByID(id2);
  2910. GetEventGenerator()->MoveMouseTo(
  2911. app_button2->GetBoundsInScreen().CenterPoint());
  2912. GetEventGenerator()->PressLeftButton();
  2913. task_environment()->FastForwardBy(base::Milliseconds(200));
  2914. // Move the mouse. Verify that the shelf view has a view under drag.
  2915. GetEventGenerator()->MoveMouseBy(0, -100);
  2916. EXPECT_TRUE(shelf_view_->drag_view());
  2917. }
  2918. TEST_F(ShelfViewGestureTapTest,
  2919. PressEscapeKeyBeforeReleaseLongPressOnAppButton) {
  2920. const ShelfID id = AddAppShortcut();
  2921. auto item_delegate_owned =
  2922. std::make_unique<AsyncContextMenuShelfItemDelegate>();
  2923. AsyncContextMenuShelfItemDelegate* item_delegate = item_delegate_owned.get();
  2924. model_->ReplaceShelfItemDelegate(id, std::move(item_delegate_owned));
  2925. ShelfAppButton* app_button = GetButtonByID(id);
  2926. GetEventGenerator()->MoveTouch(app_button->GetBoundsInScreen().CenterPoint());
  2927. GetEventGenerator()->PressTouch();
  2928. // Fast forward to generate the ET_GESTURE_SHOW_PRESS event.
  2929. task_environment()->FastForwardBy(base::Milliseconds(200));
  2930. // Fast forward to generate the ET_GESTURE_LONG_PRESS event to show the
  2931. // context menu.
  2932. task_environment()->FastForwardBy(base::Milliseconds(1000));
  2933. EXPECT_TRUE(item_delegate->HasPendingContextMenuCallback());
  2934. // Build a dummy context menu and show it.
  2935. {
  2936. auto menu_model = std::make_unique<ui::SimpleMenuModel>(nullptr);
  2937. menu_model->AddItem(203, u"item");
  2938. item_delegate->RunPendingContextMenuCallback(std::move(menu_model));
  2939. EXPECT_TRUE(shelf_view_->IsShowingMenuForView(app_button));
  2940. }
  2941. // Press Escape. The context menu should be closed.
  2942. GetEventGenerator()->PressAndReleaseKey(ui::VKEY_ESCAPE);
  2943. EXPECT_FALSE(shelf_view_->IsShowingMenu());
  2944. EXPECT_FALSE(item_delegate->HasPendingContextMenuCallback());
  2945. // Release the gesture press. The context menu should show again.
  2946. GetEventGenerator()->ReleaseTouch();
  2947. task_environment()->FastForwardBy(base::Milliseconds(1000));
  2948. EXPECT_TRUE(item_delegate->HasPendingContextMenuCallback());
  2949. {
  2950. auto menu_model = std::make_unique<ui::SimpleMenuModel>(nullptr);
  2951. menu_model->AddItem(203, u"item");
  2952. item_delegate->RunPendingContextMenuCallback(std::move(menu_model));
  2953. EXPECT_TRUE(shelf_view_->IsShowingMenuForView(app_button));
  2954. }
  2955. // Verify that the ink drop of the app button for which the context menu shows
  2956. // for is activated.
  2957. EXPECT_EQ(
  2958. views::InkDropState::ACTIVATED,
  2959. views::InkDrop::Get(app_button)->GetInkDrop()->GetTargetInkDropState());
  2960. }
  2961. // Verifies the shelf app button's inkdrop behavior when the mouse click
  2962. // occurs before gesture long press.
  2963. TEST_F(ShelfViewGestureTapTest, MouseClickInterruptionBeforeGestureLongPress) {
  2964. const gfx::Point app_icon1_center_point =
  2965. app_icon1_->GetBoundsInScreen().CenterPoint();
  2966. GetEventGenerator()->PressTouch(app_icon1_center_point);
  2967. // Fast forward to generate the ET_GESTURE_SHOW_PRESS event.
  2968. task_environment()->FastForwardBy(base::Milliseconds(200));
  2969. // Mouse click at `app_icon2_` while gesture pressing `app_icon1_`. Note that
  2970. // we do not need to release the touch on `app_icon1_` because the gesture
  2971. // is interrupted by the mouse click.
  2972. GetEventGenerator()->MoveMouseTo(
  2973. app_icon2_->GetBoundsInScreen().CenterPoint());
  2974. GetEventGenerator()->ClickLeftButton();
  2975. // Fast forward until the callback is executed.
  2976. task_environment()->FastForwardBy(base::Milliseconds(200));
  2977. EXPECT_FALSE(shelf_view_->IsShowingMenu());
  2978. EXPECT_EQ(views::InkDropState::HIDDEN, GetInkDropStateOfAppIcon1());
  2979. }
  2980. } // namespace ash