shelf_view.cc 96 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625
  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 <memory>
  7. #include <utility>
  8. #include "ash/app_list/app_list_controller_impl.h"
  9. #include "ash/app_list/views/app_drag_icon_proxy.h"
  10. #include "ash/app_list/views/ghost_image_view.h"
  11. #include "ash/constants/ash_features.h"
  12. #include "ash/keyboard/keyboard_util.h"
  13. #include "ash/keyboard/ui/keyboard_ui_controller.h"
  14. #include "ash/public/cpp/metrics_util.h"
  15. #include "ash/public/cpp/shelf_model.h"
  16. #include "ash/public/cpp/shelf_types.h"
  17. #include "ash/public/cpp/window_properties.h"
  18. #include "ash/screen_util.h"
  19. #include "ash/session/session_controller_impl.h"
  20. #include "ash/shelf/hotseat_widget.h"
  21. #include "ash/shelf/scrollable_shelf_view.h"
  22. #include "ash/shelf/shelf.h"
  23. #include "ash/shelf/shelf_app_button.h"
  24. #include "ash/shelf/shelf_application_menu_model.h"
  25. #include "ash/shelf/shelf_button.h"
  26. #include "ash/shelf/shelf_context_menu_model.h"
  27. #include "ash/shelf/shelf_controller.h"
  28. #include "ash/shelf/shelf_focus_cycler.h"
  29. #include "ash/shelf/shelf_layout_manager.h"
  30. #include "ash/shelf/shelf_menu_model_adapter.h"
  31. #include "ash/shelf/shelf_tooltip_manager.h"
  32. #include "ash/shelf/shelf_widget.h"
  33. #include "ash/shell.h"
  34. #include "ash/shell_delegate.h"
  35. #include "ash/strings/grit/ash_strings.h"
  36. #include "ash/style/ash_color_provider.h"
  37. #include "ash/system/status_area_widget.h"
  38. #include "ash/utility/haptics_util.h"
  39. #include "ash/wm/desks/desks_util.h"
  40. #include "ash/wm/mru_window_tracker.h"
  41. #include "ash/wm/tablet_mode/tablet_mode_controller.h"
  42. #include "ash/wm/window_util.h"
  43. #include "base/auto_reset.h"
  44. #include "base/bind.h"
  45. #include "base/callback_helpers.h"
  46. #include "base/containers/adapters.h"
  47. #include "base/containers/contains.h"
  48. #include "base/cxx17_backports.h"
  49. #include "base/metrics/histogram_functions.h"
  50. #include "base/metrics/histogram_macros.h"
  51. #include "base/metrics/user_metrics.h"
  52. #include "base/scoped_observation.h"
  53. #include "base/strings/utf_string_conversions.h"
  54. #include "base/timer/timer.h"
  55. #include "components/account_id/account_id.h"
  56. #include "components/services/app_service/public/cpp/app_registry_cache_wrapper.h"
  57. #include "components/services/app_service/public/cpp/app_types.h"
  58. #include "ui/accessibility/ax_node_data.h"
  59. #include "ui/base/l10n/l10n_util.h"
  60. #include "ui/base/metadata/metadata_impl_macros.h"
  61. #include "ui/base/models/simple_menu_model.h"
  62. #include "ui/base/ui_base_features.h"
  63. #include "ui/color/color_id.h"
  64. #include "ui/compositor/animation_throughput_reporter.h"
  65. #include "ui/compositor/layer.h"
  66. #include "ui/compositor/layer_animation_observer.h"
  67. #include "ui/compositor/layer_animator.h"
  68. #include "ui/compositor/scoped_animation_duration_scale_mode.h"
  69. #include "ui/compositor/scoped_layer_animation_settings.h"
  70. #include "ui/display/scoped_display_for_new_windows.h"
  71. #include "ui/events/devices/haptic_touchpad_effects.h"
  72. #include "ui/events/event_utils.h"
  73. #include "ui/gfx/canvas.h"
  74. #include "ui/gfx/geometry/point.h"
  75. #include "ui/views/animation/bounds_animator.h"
  76. #include "ui/views/animation/ink_drop.h"
  77. #include "ui/views/controls/button/button.h"
  78. #include "ui/views/controls/menu/menu_model_adapter.h"
  79. #include "ui/views/controls/menu/menu_runner.h"
  80. #include "ui/views/controls/separator.h"
  81. #include "ui/views/focus/focus_search.h"
  82. #include "ui/views/view_model.h"
  83. #include "ui/views/view_model_utils.h"
  84. #include "ui/views/widget/widget.h"
  85. #include "ui/wm/core/coordinate_conversion.h"
  86. using gfx::Animation;
  87. using views::View;
  88. namespace ash {
  89. // The rip off distance, where the shelf icon gets unpinned if dragged over this
  90. // distance from the outer edge of the shelf, depends on the shelf size. The
  91. // distance is calculated by multiplying the shelf size by
  92. // `kRipOffDistanceFactor`.
  93. constexpr float kRipOffDistanceFactor = 0.75f;
  94. // The rip off drag and drop proxy image should get scaled by this factor.
  95. constexpr float kDragAndDropProxyScale = 1.2f;
  96. // The opacity represents that this partially disappeared item will get removed.
  97. constexpr float kDraggedImageOpacity = 0.5f;
  98. namespace {
  99. // The dimensions, in pixels, of the separator between pinned and unpinned
  100. // items.
  101. constexpr int kSeparatorSize = 20;
  102. constexpr int kSeparatorThickness = 1;
  103. constexpr char kShelfIconMoveAnimationHistogram[] =
  104. "Ash.ShelfIcon.AnimationSmoothness.Move";
  105. constexpr char kShelfIconFadeInAnimationHistogram[] =
  106. "Ash.ShelfIcon.AnimationSmoothness.FadeIn";
  107. constexpr char kShelfIconFadeOutAnimationHistogram[] =
  108. "Ash.ShelfIcon.AnimationSmoothness.FadeOut";
  109. // Helper to check if tablet mode is enabled.
  110. bool IsTabletModeEnabled() {
  111. return Shell::Get()->tablet_mode_controller() &&
  112. Shell::Get()->tablet_mode_controller()->InTabletMode();
  113. }
  114. // A class to temporarily disable a given bounds animator.
  115. class BoundsAnimatorDisabler {
  116. public:
  117. explicit BoundsAnimatorDisabler(views::BoundsAnimator* bounds_animator)
  118. : old_duration_(bounds_animator->GetAnimationDuration()),
  119. bounds_animator_(bounds_animator) {
  120. bounds_animator_->SetAnimationDuration(base::Milliseconds(1));
  121. }
  122. BoundsAnimatorDisabler(const BoundsAnimatorDisabler&) = delete;
  123. BoundsAnimatorDisabler& operator=(const BoundsAnimatorDisabler&) = delete;
  124. ~BoundsAnimatorDisabler() {
  125. bounds_animator_->SetAnimationDuration(old_duration_);
  126. }
  127. private:
  128. // The previous animation duration.
  129. base::TimeDelta old_duration_;
  130. // The bounds animator which gets used.
  131. views::BoundsAnimator* bounds_animator_;
  132. };
  133. void ReportMoveAnimationSmoothness(int smoothness) {
  134. base::UmaHistogramPercentage(kShelfIconMoveAnimationHistogram, smoothness);
  135. }
  136. void ReportFadeInAnimationSmoothness(int smoothness) {
  137. base::UmaHistogramPercentage(kShelfIconFadeInAnimationHistogram, smoothness);
  138. }
  139. void ReportFadeOutAnimationSmoothness(int smoothness) {
  140. base::UmaHistogramPercentage(kShelfIconFadeOutAnimationHistogram, smoothness);
  141. }
  142. // Returns the id of the display on which |view| is shown.
  143. int64_t GetDisplayIdForView(const View* view) {
  144. aura::Window* window = view->GetWidget()->GetNativeWindow();
  145. return display::Screen::GetScreen()->GetDisplayNearestWindow(window).id();
  146. }
  147. // Whether |item_view| is a ShelfAppButton and its state is STATE_DRAGGING.
  148. bool ShelfButtonIsInDrag(const ShelfItemType item_type,
  149. const views::View* item_view) {
  150. switch (item_type) {
  151. case TYPE_PINNED_APP:
  152. case TYPE_BROWSER_SHORTCUT:
  153. case TYPE_APP:
  154. case TYPE_UNPINNED_BROWSER_SHORTCUT:
  155. return static_cast<const ShelfAppButton*>(item_view)->state() &
  156. ShelfAppButton::STATE_DRAGGING;
  157. case TYPE_DIALOG:
  158. case TYPE_UNDEFINED:
  159. return false;
  160. }
  161. }
  162. // Called back by the shelf item delegates to determine whether an app menu item
  163. // should be included in the shelf app menu given its corresponding window. This
  164. // is used to filter out items whose windows are on inactive desks when the per-
  165. // desk shelf feature is enabled.
  166. bool ShouldIncludeMenuItem(aura::Window* window) {
  167. if (!features::IsPerDeskShelfEnabled())
  168. return true;
  169. return desks_util::BelongsToActiveDesk(window);
  170. }
  171. // Returns true if the app associated with |app_id| is a Remote App.
  172. bool IsRemoteApp(const std::string& app_id) {
  173. AccountId account_id =
  174. Shell::Get()->session_controller()->GetActiveAccountId();
  175. apps::AppRegistryCache* cache =
  176. apps::AppRegistryCacheWrapper::Get().GetAppRegistryCache(account_id);
  177. return cache && cache->GetAppType(app_id) == apps::AppType::kRemote;
  178. }
  179. bool IsStandaloneBrowser(const std::string& app_id) {
  180. AccountId account_id =
  181. Shell::Get()->session_controller()->GetActiveAccountId();
  182. apps::AppRegistryCache* cache =
  183. apps::AppRegistryCacheWrapper::Get().GetAppRegistryCache(account_id);
  184. return cache &&
  185. cache->GetAppType(app_id) == apps::AppType::kStandaloneBrowser;
  186. }
  187. // Records the user metric action for whenever a shelf item is pinned or
  188. // unpinned.
  189. void RecordPinUnpinUserAction(bool pinned) {
  190. if (pinned) {
  191. base::RecordAction(base::UserMetricsAction("Shelf_ItemPinned"));
  192. } else {
  193. base::RecordAction(base::UserMetricsAction("Shelf_ItemUnpinned"));
  194. }
  195. }
  196. } // namespace
  197. // Helper class that resets a view opacity when called. Used to reset drag view
  198. // opacity after animation to drop the drag icon proxy to its final bounds ends.
  199. // The resetter is no-op if the target view gets destroyed before the resetter
  200. // get run.
  201. class ShelfView::ViewOpacityResetter : public views::ViewObserver {
  202. public:
  203. explicit ViewOpacityResetter(views::View* view) : view_(view) {
  204. view_observer_.Observe(view);
  205. }
  206. ViewOpacityResetter(const ViewOpacityResetter&) = delete;
  207. ViewOpacityResetter& operator=(const ViewOpacityResetter&) = delete;
  208. ~ViewOpacityResetter() override { Run(); }
  209. // views::ViewObserver:
  210. void OnViewIsDeleting(View* observed_view) override {
  211. view_ = nullptr;
  212. view_observer_.Reset();
  213. }
  214. void Run() {
  215. if (view_ && view_->layer())
  216. view_->layer()->SetOpacity(1.0f);
  217. }
  218. private:
  219. views::View* view_;
  220. base::ScopedObservation<views::View, views::ViewObserver> view_observer_{
  221. this};
  222. };
  223. // ImplicitAnimationObserver used when adding an item.
  224. class ShelfView::FadeInAnimationDelegate
  225. : public ui::ImplicitAnimationObserver {
  226. public:
  227. explicit FadeInAnimationDelegate(ShelfView* shelf_view)
  228. : shelf_view_(shelf_view) {}
  229. ~FadeInAnimationDelegate() override { StopObservingImplicitAnimations(); }
  230. private:
  231. // ui::ImplicitAnimationObserver:
  232. void OnImplicitAnimationsCompleted() override {
  233. shelf_view_->OnFadeInAnimationEnded();
  234. }
  235. ShelfView* shelf_view_ = nullptr;
  236. };
  237. // AnimationDelegate used when deleting an item. This steadily decreased the
  238. // opacity of the layer as the animation progress.
  239. class ShelfView::FadeOutAnimationDelegate : public gfx::AnimationDelegate {
  240. public:
  241. FadeOutAnimationDelegate(ShelfView* host, std::unique_ptr<views::View> view)
  242. : shelf_view_(host), view_(std::move(view)) {}
  243. FadeOutAnimationDelegate(const FadeOutAnimationDelegate&) = delete;
  244. FadeOutAnimationDelegate& operator=(const FadeOutAnimationDelegate&) = delete;
  245. ~FadeOutAnimationDelegate() override = default;
  246. // AnimationDelegate overrides:
  247. void AnimationProgressed(const Animation* animation) override {
  248. view_->layer()->SetOpacity(1 - animation->GetCurrentValue());
  249. }
  250. void AnimationEnded(const Animation* animation) override {
  251. // Ensures that |view| is not used after destruction.
  252. shelf_view_->StopAnimatingViewIfAny(view_.get());
  253. // Remove the view which has been faded away.
  254. view_.reset();
  255. shelf_view_->OnFadeOutAnimationEnded();
  256. }
  257. void AnimationCanceled(const Animation* animation) override {}
  258. private:
  259. ShelfView* shelf_view_;
  260. std::unique_ptr<views::View> view_;
  261. };
  262. // AnimationDelegate used to trigger fading an element in. When an item is
  263. // inserted this delegate is attached to the animation that expands the size of
  264. // the item. When done it kicks off another animation to fade the item in.
  265. class ShelfView::StartFadeAnimationDelegate : public gfx::AnimationDelegate {
  266. public:
  267. StartFadeAnimationDelegate(ShelfView* host, views::View* view)
  268. : shelf_view_(host), view_(view) {}
  269. StartFadeAnimationDelegate(const StartFadeAnimationDelegate&) = delete;
  270. StartFadeAnimationDelegate& operator=(const StartFadeAnimationDelegate&) =
  271. delete;
  272. ~StartFadeAnimationDelegate() override = default;
  273. // AnimationDelegate overrides:
  274. void AnimationEnded(const Animation* animation) override {
  275. shelf_view_->FadeIn(view_);
  276. }
  277. void AnimationCanceled(const Animation* animation) override {
  278. view_->layer()->SetOpacity(1.0f);
  279. }
  280. private:
  281. ShelfView* shelf_view_;
  282. views::View* view_;
  283. };
  284. // static
  285. const int ShelfView::kMinimumDragDistance = 8;
  286. ShelfView::ShelfView(ShelfModel* model,
  287. Shelf* shelf,
  288. Delegate* delegate,
  289. ShelfButtonDelegate* shelf_button_delegate)
  290. : model_(model),
  291. shelf_(shelf),
  292. view_model_(std::make_unique<views::ViewModel>()),
  293. bounds_animator_(
  294. std::make_unique<views::BoundsAnimator>(this,
  295. /*use_transforms=*/true)),
  296. delegate_(delegate),
  297. shelf_button_delegate_(shelf_button_delegate) {
  298. DCHECK(model_);
  299. DCHECK(shelf_);
  300. Shell::Get()->tablet_mode_controller()->AddObserver(this);
  301. Shell::Get()->AddShellObserver(this);
  302. shelf_->AddObserver(this);
  303. bounds_animator_->AddObserver(this);
  304. bounds_animator_->SetAnimationDuration(
  305. ShelfConfig::Get()->shelf_animation_duration());
  306. set_context_menu_controller(this);
  307. set_allow_deactivate_on_esc(true);
  308. announcement_view_ = new views::View();
  309. AddChildView(announcement_view_);
  310. }
  311. ShelfView::~ShelfView() {
  312. // Shell destroys the TabletModeController before destroying all root windows.
  313. if (Shell::Get()->tablet_mode_controller())
  314. Shell::Get()->tablet_mode_controller()->RemoveObserver(this);
  315. shelf_->RemoveObserver(this);
  316. Shell::Get()->RemoveShellObserver(this);
  317. bounds_animator_->RemoveObserver(this);
  318. model_->RemoveObserver(this);
  319. }
  320. int ShelfView::GetSizeOfAppButtons(int count, int button_size) {
  321. const int button_spacing = ShelfConfig::Get()->button_spacing();
  322. if (count == 0)
  323. return 0;
  324. const int app_size = count * button_size;
  325. int total_padding = button_spacing * (count - 1);
  326. return app_size + total_padding;
  327. }
  328. void ShelfView::Init(views::FocusSearch* focus_search) {
  329. auto separator = std::make_unique<views::Separator>();
  330. separator->SetColorId(ui::kColorAshSystemUIMenuSeparator);
  331. separator->SetPreferredLength(kSeparatorSize);
  332. separator->SetVisible(false);
  333. ConfigureChildView(separator.get(), ui::LAYER_TEXTURED);
  334. separator_ = AddChildView(std::move(separator));
  335. model()->AddObserver(this);
  336. const ShelfItems& items(model_->items());
  337. for (ShelfItems::const_iterator i = items.begin(); i != items.end(); ++i) {
  338. views::View* child = CreateViewForItem(*i);
  339. child->SetPaintToLayer();
  340. int index = static_cast<int>(i - items.begin());
  341. view_model_->Add(child, index);
  342. // Add child view so it has the same ordering as in the |view_model_|.
  343. AddChildViewAt(child, index);
  344. }
  345. fade_in_animation_delegate_ = std::make_unique<FadeInAnimationDelegate>(this);
  346. focus_search_ = focus_search;
  347. // We'll layout when our bounds change.
  348. }
  349. bool ShelfView::IsShowingMenu() const {
  350. return shelf_menu_model_adapter_ &&
  351. shelf_menu_model_adapter_->IsShowingMenu();
  352. }
  353. void ShelfView::UpdateVisibleShelfItemBoundsUnion() {
  354. visible_shelf_item_bounds_union_.SetRect(0, 0, 0, 0);
  355. for (const auto i : visible_views_indices_) {
  356. const views::View* child = view_model_->view_at(i);
  357. if (ShouldShowTooltipForChildView(child)) {
  358. visible_shelf_item_bounds_union_.Union(
  359. GetChildViewTargetMirroredBounds(child));
  360. }
  361. }
  362. }
  363. bool ShelfView::ShouldShowTooltipForView(const views::View* view) const {
  364. if (!view || !view->parent())
  365. return false;
  366. if (view->parent() == this)
  367. return ShouldShowTooltipForChildView(view);
  368. return false;
  369. }
  370. ShelfAppButton* ShelfView::GetShelfAppButton(const ShelfID& id) {
  371. const int index = model_->ItemIndexByID(id);
  372. if (index < 0)
  373. return nullptr;
  374. views::View* const view = view_model_->view_at(index);
  375. DCHECK_EQ(ShelfAppButton::kViewClassName, view->GetClassName());
  376. return static_cast<ShelfAppButton*>(view);
  377. }
  378. void ShelfView::StopAnimatingViewIfAny(views::View* view) {
  379. if (bounds_animator_->IsAnimating(view))
  380. bounds_animator_->StopAnimatingView(view);
  381. }
  382. int ShelfView::GetButtonSize() const {
  383. return ShelfConfig::Get()->GetShelfButtonSize(
  384. shelf_->hotseat_widget()->target_hotseat_density());
  385. }
  386. int ShelfView::GetButtonIconSize() const {
  387. return ShelfConfig::Get()->GetShelfButtonIconSize(
  388. shelf_->hotseat_widget()->target_hotseat_density());
  389. }
  390. int ShelfView::GetShelfItemRippleSize() const {
  391. return GetButtonSize() +
  392. 2 * ShelfConfig::Get()->scrollable_shelf_ripple_padding();
  393. }
  394. void ShelfView::LayoutIfAppIconsOffsetUpdates() {
  395. if (app_icons_layout_offset_ != CalculateAppIconsLayoutOffset())
  396. LayoutToIdealBounds();
  397. }
  398. ShelfAppButton* ShelfView::GetShelfItemViewWithContextMenu() {
  399. if (context_menu_id_.IsNull())
  400. return nullptr;
  401. const int item_index = model_->ItemIndexByID(context_menu_id_);
  402. if (item_index < 0)
  403. return nullptr;
  404. return static_cast<ShelfAppButton*>(view_model_->view_at(item_index));
  405. }
  406. void ShelfView::AnnounceShelfItemNotificationBadge(views::View* button) {
  407. announcement_view_->GetViewAccessibility().OverrideName(
  408. l10n_util::GetStringFUTF16(IDS_SHELF_ITEM_HAS_NOTIFICATION_BADGE,
  409. GetTitleForView(button)));
  410. announcement_view_->NotifyAccessibilityEvent(ax::mojom::Event::kAlert,
  411. /*send_native_event=*/true);
  412. }
  413. bool ShelfView::LocationInsideVisibleShelfItemBounds(
  414. const gfx::Point& location) const {
  415. return visible_shelf_item_bounds_union_.Contains(location);
  416. }
  417. bool ShelfView::ShouldHideTooltip(const gfx::Point& cursor_location) const {
  418. // There are thin gaps between launcher buttons but the tooltip shouldn't hide
  419. // in the gaps, but the tooltip should hide if the mouse moved totally outside
  420. // of the buttons area.
  421. return !LocationInsideVisibleShelfItemBounds(cursor_location);
  422. }
  423. const std::vector<aura::Window*> ShelfView::GetOpenWindowsForView(
  424. views::View* view) {
  425. std::vector<aura::Window*> window_list =
  426. Shell::Get()->mru_window_tracker()->BuildWindowForCycleList(kActiveDesk);
  427. std::vector<aura::Window*> open_windows;
  428. const ShelfItem* item = ShelfItemForView(view);
  429. // The concept of a list of open windows doesn't make sense for something
  430. // that isn't an app shortcut: return an empty list.
  431. if (!item)
  432. return open_windows;
  433. for (auto* window : window_list) {
  434. const std::string window_app_id =
  435. ShelfID::Deserialize(window->GetProperty(kShelfIDKey)).app_id;
  436. if (window_app_id == item->id.app_id) {
  437. // TODO: In the very first version we only show one window. Add the proper
  438. // UI to show all windows for a given open app.
  439. open_windows.push_back(window);
  440. }
  441. }
  442. return open_windows;
  443. }
  444. std::u16string ShelfView::GetTitleForView(const views::View* view) const {
  445. if (view->parent() == this)
  446. return GetTitleForChildView(view);
  447. return std::u16string();
  448. }
  449. views::View* ShelfView::GetViewForEvent(const ui::Event& event) {
  450. if (event.target() == GetWidget()->GetNativeWindow())
  451. return this;
  452. return nullptr;
  453. }
  454. gfx::Rect ShelfView::GetVisibleItemsBoundsInScreen() {
  455. gfx::Size preferred_size = GetPreferredSize();
  456. gfx::Point origin(GetMirroredXWithWidthInView(0, preferred_size.width()), 0);
  457. ConvertPointToScreen(this, &origin);
  458. return gfx::Rect(origin, preferred_size);
  459. }
  460. gfx::Size ShelfView::CalculatePreferredSize() const {
  461. const int hotseat_size = shelf_->hotseat_widget()->GetHotseatSize();
  462. if (visible_views_indices_.empty()) {
  463. // There are no visible shelf items.
  464. return shelf_->IsHorizontalAlignment() ? gfx::Size(0, hotseat_size)
  465. : gfx::Size(hotseat_size, 0);
  466. }
  467. const gfx::Rect last_button_bounds =
  468. view_model_->ideal_bounds(visible_views_indices_.back());
  469. if (shelf_->IsHorizontalAlignment())
  470. return gfx::Size(last_button_bounds.right(), hotseat_size);
  471. return gfx::Size(hotseat_size, last_button_bounds.bottom());
  472. }
  473. void ShelfView::OnThemeChanged() {
  474. views::AccessiblePaneView::OnThemeChanged();
  475. if (separator_)
  476. separator_->SchedulePaint();
  477. }
  478. void ShelfView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
  479. // This bounds change is produced by the shelf movement (rotation, alignment
  480. // change, etc.) and all content has to follow. Using an animation at that
  481. // time would produce a time lag since the animation of the BoundsAnimator has
  482. // itself a delay before it arrives at the required location. As such we tell
  483. // the animator to go there immediately. We still want to use an animation
  484. // when the bounds change is caused by entering or exiting tablet mode, with
  485. // an exception of usage within the scrollable shelf. With scrollable shelf
  486. // (and hotseat), tablet mode transition causes hotseat bounds changes, so
  487. // animating shelf items as well would introduce a lag.
  488. BoundsAnimatorDisabler disabler(bounds_animator_.get());
  489. LayoutToIdealBounds();
  490. shelf_->NotifyShelfIconPositionsChanged();
  491. }
  492. bool ShelfView::OnKeyPressed(const ui::KeyEvent& event) {
  493. if (event.IsControlDown() &&
  494. keyboard_util::IsArrowKeyCode(event.key_code())) {
  495. bool swap_with_next = (event.key_code() == ui::VKEY_DOWN ||
  496. event.key_code() == ui::VKEY_RIGHT);
  497. SwapButtons(GetFocusManager()->GetFocusedView(), swap_with_next);
  498. return true;
  499. }
  500. return views::View::OnKeyPressed(event);
  501. }
  502. void ShelfView::OnMouseEvent(ui::MouseEvent* event) {
  503. gfx::Point location_in_screen(event->location());
  504. View::ConvertPointToScreen(this, &location_in_screen);
  505. switch (event->type()) {
  506. case ui::ET_MOUSEWHEEL:
  507. // The mousewheel event is handled by the ScrollableShelfView.
  508. break;
  509. case ui::ET_MOUSE_PRESSED:
  510. if (!event->IsOnlyLeftMouseButton()) {
  511. if (event->IsOnlyRightMouseButton()) {
  512. ShowContextMenuForViewImpl(this, location_in_screen,
  513. ui::MENU_SOURCE_MOUSE);
  514. event->SetHandled();
  515. }
  516. return;
  517. }
  518. [[fallthrough]];
  519. case ui::ET_MOUSE_DRAGGED:
  520. case ui::ET_MOUSE_RELEASED:
  521. // Convert the event location from current view to screen, since dragging
  522. // the shelf by mouse can open the fullscreen app list. Updating the
  523. // bounds of the app list during dragging is based on screen coordinate
  524. // space.
  525. event->set_location(location_in_screen);
  526. event->SetHandled();
  527. shelf_->ProcessMouseEvent(*event->AsMouseEvent());
  528. break;
  529. default:
  530. break;
  531. }
  532. }
  533. views::FocusTraversable* ShelfView::GetPaneFocusTraversable() {
  534. // ScrollableShelfView should handles the focus traversal.
  535. return nullptr;
  536. }
  537. void ShelfView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
  538. node_data->role = ax::mojom::Role::kToolbar;
  539. node_data->SetName(l10n_util::GetStringUTF8(IDS_ASH_SHELF_ACCESSIBLE_NAME));
  540. }
  541. View* ShelfView::GetTooltipHandlerForPoint(const gfx::Point& point) {
  542. // Similar implementation as views::View, but without going into each
  543. // child's subviews.
  544. View::Views children = GetChildrenInZOrder();
  545. for (auto* child : base::Reversed(children)) {
  546. if (!child->GetVisible())
  547. continue;
  548. gfx::Point point_in_child_coords(point);
  549. ConvertPointToTarget(this, child, &point_in_child_coords);
  550. if (child->HitTestPoint(point_in_child_coords) &&
  551. ShouldShowTooltipForChildView(child)) {
  552. return child;
  553. }
  554. }
  555. // If none of our children qualifies, just return the shelf view itself.
  556. return this;
  557. }
  558. void ShelfView::ViewHierarchyChanged(
  559. const views::ViewHierarchyChangedDetails& details) {
  560. if (!details.is_add) {
  561. if (details.child == current_ghost_view_) {
  562. current_ghost_view_ = nullptr;
  563. current_ghost_view_index_ = absl::nullopt;
  564. }
  565. if (details.child == last_ghost_view_) {
  566. last_ghost_view_ = nullptr;
  567. current_ghost_view_index_ = absl::nullopt;
  568. }
  569. }
  570. }
  571. void ShelfView::OnShelfButtonAboutToRequestFocusFromTabTraversal(
  572. ShelfButton* button,
  573. bool reverse) {
  574. if (ShouldFocusOut(reverse, button)) {
  575. shelf_->shelf_focus_cycler()->FocusOut(reverse, SourceView::kShelfView);
  576. }
  577. }
  578. void ShelfView::ButtonPressed(views::Button* sender,
  579. const ui::Event& event,
  580. views::InkDrop* ink_drop) {
  581. if (!ShouldEventActivateButton(sender, event)) {
  582. ink_drop->SnapToHidden();
  583. return;
  584. }
  585. // Prevent concurrent requests that may show application or context menus.
  586. if (!item_awaiting_response_.IsNull()) {
  587. const ShelfItem* item = ShelfItemForView(sender);
  588. if (item && item->id != item_awaiting_response_)
  589. ink_drop->AnimateToState(views::InkDropState::DEACTIVATED);
  590. return;
  591. }
  592. // Ensure the keyboard is hidden and stays hidden (as long as it isn't locked)
  593. if (keyboard::KeyboardUIController::Get()->IsEnabled())
  594. keyboard::KeyboardUIController::Get()->HideKeyboardExplicitlyBySystem();
  595. // Record the index for the last pressed shelf item.
  596. last_pressed_index_ = view_model_->GetIndexOfView(sender);
  597. DCHECK(last_pressed_index_.has_value());
  598. // Place new windows on the same display as the button. Opening windows is
  599. // usually an async operation so we wait until window activation changes
  600. // (ShelfItemStatusChanged) before destroying the scoped object. Post a task
  601. // to destroy the scoped object just in case the window activation event does
  602. // not get fired.
  603. aura::Window* window = sender->GetWidget()->GetNativeWindow();
  604. scoped_display_for_new_windows_ =
  605. std::make_unique<display::ScopedDisplayForNewWindows>(
  606. window->GetRootWindow());
  607. base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
  608. FROM_HERE,
  609. base::BindOnce(&ShelfView::DestroyScopedDisplay,
  610. weak_factory_.GetWeakPtr()),
  611. base::Milliseconds(100));
  612. // Slow down activation animations if Control key is pressed.
  613. std::unique_ptr<ui::ScopedAnimationDurationScaleMode> slowing_animations;
  614. if (event.IsControlDown()) {
  615. slowing_animations = std::make_unique<ui::ScopedAnimationDurationScaleMode>(
  616. ui::ScopedAnimationDurationScaleMode::SLOW_DURATION);
  617. }
  618. // Collect usage statistics before we decide what to do with the click.
  619. switch (model_->items()[last_pressed_index_.value()].type) {
  620. case TYPE_PINNED_APP:
  621. case TYPE_BROWSER_SHORTCUT:
  622. case TYPE_APP:
  623. case TYPE_UNPINNED_BROWSER_SHORTCUT:
  624. base::RecordAction(base::UserMetricsAction("Launcher_ClickOnApp"));
  625. break;
  626. case TYPE_DIALOG:
  627. break;
  628. case TYPE_UNDEFINED:
  629. NOTREACHED() << "ShelfItemType must be set.";
  630. break;
  631. }
  632. // Run AfterItemSelected directly if the item has no delegate (ie. in tests).
  633. const ShelfItem& item = model_->items()[last_pressed_index_.value()];
  634. if (!model_->GetShelfItemDelegate(item.id)) {
  635. AfterItemSelected(item, sender, event.Clone(), ink_drop, SHELF_ACTION_NONE,
  636. {});
  637. return;
  638. }
  639. // Notify the item of its selection; handle the result in AfterItemSelected.
  640. item_awaiting_response_ = item.id;
  641. model_->GetShelfItemDelegate(item.id)->ItemSelected(
  642. event.Clone(), GetDisplayIdForView(this), LAUNCH_FROM_SHELF,
  643. base::BindOnce(&ShelfView::AfterItemSelected, weak_factory_.GetWeakPtr(),
  644. item, sender, event.Clone(), ink_drop),
  645. base::BindRepeating(&ShouldIncludeMenuItem));
  646. }
  647. bool ShelfView::IsShowingMenuForView(const views::View* view) const {
  648. return IsShowingMenu() &&
  649. shelf_menu_model_adapter_->IsShowingMenuForView(*view);
  650. }
  651. ////////////////////////////////////////////////////////////////////////////////
  652. // ShelfView, FocusTraversable implementation:
  653. views::FocusSearch* ShelfView::GetFocusSearch() {
  654. return focus_search_;
  655. }
  656. ////////////////////////////////////////////////////////////////////////////////
  657. // ShelfView, AccessiblePaneView implementation:
  658. views::View* ShelfView::GetDefaultFocusableChild() {
  659. return default_last_focusable_child_ ? FindLastFocusableChild()
  660. : FindFirstFocusableChild();
  661. }
  662. void ShelfView::ShowContextMenuForViewImpl(views::View* source,
  663. const gfx::Point& point,
  664. ui::MenuSourceType source_type) {
  665. // Prevent concurrent requests that may show application or context menus.
  666. const ShelfItem* item = ShelfItemForView(source);
  667. if (!item_awaiting_response_.IsNull()) {
  668. if (item && item->id != item_awaiting_response_) {
  669. views::InkDrop::Get(source)->AnimateToState(
  670. views::InkDropState::DEACTIVATED, nullptr);
  671. }
  672. return;
  673. }
  674. last_pressed_index_ = absl::nullopt;
  675. if (!item || !model_->GetShelfItemDelegate(item->id)) {
  676. ShowShelfContextMenu(ShelfID(), point, source, source_type, nullptr);
  677. return;
  678. }
  679. item_awaiting_response_ = item->id;
  680. context_menu_callback_.Reset(base::BindOnce(
  681. &ShelfView::ShowShelfContextMenu, weak_factory_.GetWeakPtr(), item->id,
  682. point, source, source_type));
  683. const int64_t display_id = GetDisplayIdForView(this);
  684. model_->GetShelfItemDelegate(item->id)->GetContextMenu(
  685. display_id, context_menu_callback_.callback());
  686. }
  687. void ShelfView::OnTabletModeStarted() {
  688. // Close all menus when tablet mode starts to ensure that the clamshell only
  689. // context menu options are not available in tablet mode.
  690. if (shelf_menu_model_adapter_)
  691. shelf_menu_model_adapter_->Cancel();
  692. }
  693. void ShelfView::OnTabletModeEnded() {
  694. // Close all menus when tablet mode ends so that menu options are kept
  695. // consistent with device state.
  696. if (shelf_menu_model_adapter_)
  697. shelf_menu_model_adapter_->Cancel();
  698. }
  699. void ShelfView::OnShelfConfigUpdated() {
  700. // Ensure the shelf app buttons have an icon which is up to date with the
  701. // current ShelfConfig sizing.
  702. for (size_t i = 0; i < view_model_->view_size(); i++) {
  703. ShelfAppButton* button =
  704. static_cast<ShelfAppButton*>(view_model_->view_at(i));
  705. if (!button->IsIconSizeCurrent())
  706. ShelfItemChanged(i, model_->items()[i]);
  707. }
  708. }
  709. bool ShelfView::ShouldEventActivateButton(View* view, const ui::Event& event) {
  710. // This only applies to app buttons.
  711. DCHECK_EQ(ShelfAppButton::kViewClassName, view->GetClassName());
  712. if (dragging())
  713. return false;
  714. // Ignore if we are already in a pointer event sequence started with a repost
  715. // event on the same shelf item. See crbug.com/343005 for more detail.
  716. if (is_repost_event_on_same_item_)
  717. return false;
  718. // Don't activate the item twice on double-click. Otherwise the window starts
  719. // animating open due to the first click, then immediately minimizes due to
  720. // the second click. The user most likely intended to open or minimize the
  721. // item once, not do both.
  722. if (event.flags() & ui::EF_IS_DOUBLE_CLICK)
  723. return false;
  724. const bool repost = IsRepostEvent(event);
  725. // Ignore if this is a repost event on the last pressed shelf item.
  726. auto index = view_model_->GetIndexOfView(view);
  727. if (!index.has_value())
  728. return false;
  729. return !repost || last_pressed_index_ != index;
  730. }
  731. bool ShelfView::IsDraggedView(const views::View* view) const {
  732. return drag_view_ == view;
  733. }
  734. views::View* ShelfView::FindFirstFocusableChild() {
  735. if (visible_views_indices_.empty())
  736. return nullptr;
  737. return view_model_->view_at(visible_views_indices_.front());
  738. }
  739. views::View* ShelfView::FindLastFocusableChild() {
  740. if (visible_views_indices_.empty())
  741. return nullptr;
  742. return view_model_->view_at(visible_views_indices_.back());
  743. }
  744. views::View* ShelfView::FindFirstOrLastFocusableChild(bool last) {
  745. return last ? FindLastFocusableChild() : FindFirstFocusableChild();
  746. }
  747. bool ShelfView::HandleGestureEvent(const ui::GestureEvent* event) {
  748. // Avoid changing |event|'s location since |event| may be received by post
  749. // event handlers.
  750. ui::GestureEvent copy_event(*event);
  751. // Convert the event location from current view to screen, since swiping up on
  752. // the shelf can open the fullscreen app list. Updating the bounds of the app
  753. // list during dragging is based on screen coordinate space.
  754. gfx::Point location_in_screen(copy_event.location());
  755. View::ConvertPointToScreen(this, &location_in_screen);
  756. copy_event.set_location(location_in_screen);
  757. if (shelf_->ProcessGestureEvent(copy_event))
  758. return true;
  759. return false;
  760. }
  761. bool ShelfView::ShouldShowTooltipForChildView(
  762. const views::View* child_view) const {
  763. DCHECK_EQ(this, child_view->parent());
  764. // Don't show a tooltip for a view that's currently being dragged.
  765. if (child_view == drag_view_)
  766. return false;
  767. return ShelfItemForView(child_view) && !IsShowingMenuForView(child_view);
  768. }
  769. // static
  770. void ShelfView::ConfigureChildView(views::View* view,
  771. ui::LayerType layer_type) {
  772. view->SetPaintToLayer(layer_type);
  773. view->layer()->SetFillsBoundsOpaquely(false);
  774. }
  775. void ShelfView::CalculateIdealBounds() {
  776. DCHECK(static_cast<size_t>(model()->item_count()) ==
  777. view_model_->view_size());
  778. const int button_spacing = ShelfConfig::Get()->button_spacing();
  779. UpdateSeparatorIndex();
  780. const int hotseat_size = shelf_->hotseat_widget()->GetHotseatSize();
  781. // Don't show the separator if it isn't needed, or would appear after all
  782. // visible items.
  783. separator_->SetVisible(separator_index_.has_value() &&
  784. separator_index_ < visible_views_indices_.back());
  785. // Set |separator_index_| to nullopt if it is not visible.
  786. if (!separator_->GetVisible())
  787. separator_index_ = absl::nullopt;
  788. app_icons_layout_offset_ = CalculateAppIconsLayoutOffset();
  789. int x = shelf()->PrimaryAxisValue(app_icons_layout_offset_, 0);
  790. int y = shelf()->PrimaryAxisValue(0, app_icons_layout_offset_);
  791. // The padding is handled in ScrollableShelfView.
  792. const int button_size = GetButtonSize();
  793. for (size_t i = 0; i < view_model_->view_size(); ++i) {
  794. if (view_model_->view_at(i)->GetVisible()) {
  795. gfx::Rect ideal_view_bounds(x, y, button_size, button_size);
  796. view_model_->set_ideal_bounds(i, ideal_view_bounds);
  797. if (view_model_->view_at(i) == drag_view_ &&
  798. current_ghost_view_index_ != i && !dragged_off_shelf_) {
  799. if (current_ghost_view_)
  800. current_ghost_view_->FadeOut();
  801. last_ghost_view_ = current_ghost_view_;
  802. auto current_ghost_view = std::make_unique<GhostImageView>(GridIndex());
  803. gfx::Size icon_size = drag_view_
  804. ->GetIdealIconBounds(ideal_view_bounds.size(),
  805. /*icon_scale=*/1.0f)
  806. .size();
  807. gfx::Rect ghost_view_bounds = ideal_view_bounds;
  808. // Ensure that the ghost_view_bounds are a square that encloses the
  809. // icon_size with the same center. The ghost view should draw as a
  810. // circle.
  811. const int icon_width = std::min(icon_size.width(), icon_size.height());
  812. ghost_view_bounds.ClampToCenteredSize(
  813. gfx::Size(icon_width, icon_width));
  814. current_ghost_view->Init(ghost_view_bounds,
  815. ghost_view_bounds.width() / 2);
  816. current_ghost_view_ = AddChildView(std::move(current_ghost_view));
  817. current_ghost_view_->FadeIn();
  818. current_ghost_view_index_ = i;
  819. }
  820. x = shelf()->PrimaryAxisValue(x + button_size + button_spacing, x);
  821. y = shelf()->PrimaryAxisValue(y, y + button_size + button_spacing);
  822. } else {
  823. view_model_->set_ideal_bounds(i, gfx::Rect(x, y, 0, 0));
  824. }
  825. if (i == separator_index_) {
  826. // Place the separator halfway between the two icons it separates,
  827. // vertically centered.
  828. int half_space = button_spacing / 2;
  829. int secondary_offset = (hotseat_size - kSeparatorSize) / 2;
  830. x -= shelf()->PrimaryAxisValue(half_space, 0);
  831. y -= shelf()->PrimaryAxisValue(0, half_space);
  832. separator_->SetBounds(
  833. x + shelf()->PrimaryAxisValue(0, secondary_offset),
  834. y + shelf()->PrimaryAxisValue(secondary_offset, 0),
  835. shelf()->PrimaryAxisValue(kSeparatorThickness, kSeparatorSize),
  836. shelf()->PrimaryAxisValue(kSeparatorSize, kSeparatorThickness));
  837. x += shelf()->PrimaryAxisValue(half_space, 0);
  838. y += shelf()->PrimaryAxisValue(0, half_space);
  839. }
  840. }
  841. }
  842. views::View* ShelfView::CreateViewForItem(const ShelfItem& item) {
  843. views::View* view = nullptr;
  844. switch (item.type) {
  845. case TYPE_PINNED_APP:
  846. case TYPE_BROWSER_SHORTCUT:
  847. case TYPE_APP:
  848. case TYPE_UNPINNED_BROWSER_SHORTCUT:
  849. case TYPE_DIALOG: {
  850. ShelfAppButton* button = new ShelfAppButton(
  851. this, shelf_button_delegate_ ? shelf_button_delegate_ : this);
  852. button->SetImage(item.image);
  853. button->SetNotificationBadgeColor(item.notification_badge_color);
  854. button->ReflectItemStatus(item);
  855. view = button;
  856. break;
  857. }
  858. case TYPE_UNDEFINED:
  859. return nullptr;
  860. }
  861. view->set_context_menu_controller(this);
  862. ConfigureChildView(view, ui::LAYER_NOT_DRAWN);
  863. return view;
  864. }
  865. int ShelfView::GetAvailableSpaceForAppIcons() const {
  866. return shelf()->PrimaryAxisValue(width(), height());
  867. }
  868. void ShelfView::UpdateSeparatorIndex() {
  869. // A separator is shown after the last pinned item only if it's followed by a
  870. // visible app item.
  871. absl::optional<size_t> first_unpinned_index = absl::nullopt;
  872. absl::optional<size_t> last_pinned_index = absl::nullopt;
  873. absl::optional<size_t> dragged_item_index = absl::nullopt;
  874. if (drag_view_)
  875. dragged_item_index = view_model_->GetIndexOfView(drag_view_);
  876. const bool can_drag_view_across_separator =
  877. drag_view_ && CanDragAcrossSeparator(drag_view_);
  878. for (size_t index = model()->item_count(); index > 0; --index) {
  879. const size_t i = index - 1;
  880. const auto& item = model()->items()[i];
  881. if (IsItemPinned(item)) {
  882. // Dragged pinned item may be moved to the unpinned side of the shelf and
  883. // may end up right of an unpinned app. Dismisses the dragged item to
  884. // check the next one.
  885. if (i == dragged_item_index && can_drag_view_across_separator)
  886. continue;
  887. last_pinned_index = i;
  888. break;
  889. }
  890. if (item.type == TYPE_APP && item.is_on_active_desk)
  891. first_unpinned_index = i;
  892. }
  893. // If there is no unpinned item in shelf, return -1 as the separator should be
  894. // hidden.
  895. if (!first_unpinned_index.has_value()) {
  896. separator_index_ = absl::nullopt;
  897. return;
  898. }
  899. // If the dragged item is between the pinned apps and unpinned apps, move it
  900. // to the pinned app side if it is closer to the pinned section compared to
  901. // its ideal bounds.
  902. if (can_drag_view_across_separator &&
  903. last_pinned_index < dragged_item_index &&
  904. dragged_item_index <= first_unpinned_index &&
  905. drag_view_relative_to_ideal_bounds_ == RelativePosition::kLeft) {
  906. separator_index_ = dragged_item_index;
  907. return;
  908. }
  909. separator_index_ = last_pinned_index;
  910. }
  911. bool ShelfView::ShouldHandleDrag(const std::string& app_id,
  912. const gfx::Point& location_in_screen) const {
  913. // Remote Apps are not pinnable.
  914. if (IsRemoteApp(app_id))
  915. return false;
  916. // Do not handle drag if an operation for another app is already in progress,
  917. // or the cursor is not inside the shelf bounds. This could happen if mouse /
  918. // touch operations overlap.
  919. return !app_id.empty() &&
  920. (drag_and_drop_shelf_id_.IsNull() ||
  921. drag_and_drop_shelf_id_.app_id == app_id) &&
  922. GetBoundsInScreen().Contains(location_in_screen);
  923. }
  924. bool ShelfView::StartDrag(const std::string& app_id,
  925. const gfx::Point& location_in_screen,
  926. const gfx::Rect& drag_icon_bounds_in_screen) {
  927. // Don't start a drag if another one is in progress.
  928. if (!drag_and_drop_shelf_id_.IsNull())
  929. return false;
  930. if (!ShouldHandleDrag(app_id, location_in_screen))
  931. return false;
  932. DCHECK(!is_active_drag_and_drop_host_);
  933. is_active_drag_and_drop_host_ = true;
  934. // If the AppsGridView (which was dispatching this event) was opened by our
  935. // button, ShelfView dragging operations are locked and we have to unlock.
  936. CancelDrag(absl::nullopt);
  937. drag_and_drop_item_pinned_ = false;
  938. drag_and_drop_shelf_id_ = ShelfID(app_id);
  939. // Check if the application is pinned - if not, we have to pin it so
  940. // that we can re-arrange the shelf order accordingly. Note that items have
  941. // to be pinned to give them the same (order) possibilities as a shortcut.
  942. if (!model_->IsAppPinned(app_id)) {
  943. ShelfModel::ScopedUserTriggeredMutation user_triggered(model_);
  944. if (model_->ItemIndexByAppID(app_id) >= 0) {
  945. model_->PinExistingItemWithID(app_id);
  946. } else {
  947. model_->AddAndPinAppWithFactoryConstructedDelegate(app_id);
  948. drag_and_drop_item_pinned_ = true;
  949. }
  950. }
  951. views::View* drag_and_drop_view =
  952. view_model_->view_at(model_->ItemIndexByID(drag_and_drop_shelf_id_));
  953. DCHECK(drag_and_drop_view);
  954. // Since there is already an icon presented by the caller, we hide this item
  955. // for now. That has to be done by reducing the size since the visibility will
  956. // change once a regrouping animation is performed.
  957. pre_drag_and_drop_size_ = drag_and_drop_view->size();
  958. drag_and_drop_view->SetSize(gfx::Size());
  959. // First we have to center the mouse cursor over the item.
  960. const gfx::Point start_point_in_screen =
  961. drag_and_drop_view->GetBoundsInScreen().CenterPoint();
  962. gfx::Point pt = start_point_in_screen;
  963. views::View::ConvertPointFromScreen(drag_and_drop_view, &pt);
  964. gfx::Point point_in_root = start_point_in_screen;
  965. wm::ConvertPointFromScreen(window_util::GetRootWindowAt(location_in_screen),
  966. &point_in_root);
  967. ui::MouseEvent event(ui::ET_MOUSE_PRESSED, pt, point_in_root,
  968. ui::EventTimeForNow(), 0, 0);
  969. PointerPressedOnButton(drag_and_drop_view, DRAG_AND_DROP, event);
  970. // Drag the item where it really belongs.
  971. Drag(location_in_screen, drag_icon_bounds_in_screen);
  972. return true;
  973. }
  974. bool ShelfView::Drag(const gfx::Point& location_in_screen,
  975. const gfx::Rect& drag_icon_bounds_in_screen) {
  976. if (drag_and_drop_shelf_id_.IsNull() ||
  977. !GetBoundsInScreen().Contains(location_in_screen))
  978. return false;
  979. drag_icon_bounds_in_screen_ = drag_icon_bounds_in_screen;
  980. gfx::Point pt = location_in_screen;
  981. views::View* drag_and_drop_view =
  982. view_model_->view_at(model_->ItemIndexByID(drag_and_drop_shelf_id_));
  983. ConvertPointFromScreen(drag_and_drop_view, &pt);
  984. gfx::Point point_in_root = location_in_screen;
  985. wm::ConvertPointFromScreen(window_util::GetRootWindowAt(location_in_screen),
  986. &point_in_root);
  987. ui::MouseEvent event(ui::ET_MOUSE_DRAGGED, pt, point_in_root,
  988. ui::EventTimeForNow(), 0, 0);
  989. PointerDraggedOnButton(drag_and_drop_view, DRAG_AND_DROP, event);
  990. return true;
  991. }
  992. void ShelfView::EndDrag(bool cancel,
  993. std::unique_ptr<AppDragIconProxy> icon_proxy) {
  994. drag_scroll_dir_ = 0;
  995. scrolling_timer_.Stop();
  996. speed_up_drag_scrolling_.Stop();
  997. if (drag_and_drop_shelf_id_.IsNull()) {
  998. is_active_drag_and_drop_host_ = false;
  999. return;
  1000. }
  1001. drag_icon_proxy_ = std::move(icon_proxy);
  1002. views::View* drag_and_drop_view =
  1003. view_model_->view_at(model_->ItemIndexByID(drag_and_drop_shelf_id_));
  1004. PointerReleasedOnButton(drag_and_drop_view, DRAG_AND_DROP, cancel);
  1005. // Either destroy the temporarily created item - or - make the item visible.
  1006. if (drag_and_drop_item_pinned_ && cancel) {
  1007. ShelfModel::ScopedUserTriggeredMutation user_triggered(model_);
  1008. model_->UnpinAppWithID(drag_and_drop_shelf_id_.app_id);
  1009. } else if (drag_and_drop_view) {
  1010. std::unique_ptr<gfx::AnimationDelegate> animation_delegate;
  1011. // Resets the dragged view's opacity at the end of drag. Otherwise, if
  1012. // the app is already pinned on shelf before drag starts, the dragged view
  1013. // will be invisible when drag ends.
  1014. animation_delegate =
  1015. std::make_unique<StartFadeAnimationDelegate>(this, drag_and_drop_view);
  1016. if (cancel) {
  1017. // When a hosted drag gets canceled, the item can remain in the same slot
  1018. // and it might have moved within the bounds. In that case the item need
  1019. // to animate back to its correct location.
  1020. AnimateToIdealBounds();
  1021. bounds_animator_->SetAnimationDelegate(drag_and_drop_view,
  1022. std::move(animation_delegate));
  1023. } else {
  1024. drag_and_drop_view->SetSize(pre_drag_and_drop_size_);
  1025. }
  1026. }
  1027. drag_icon_bounds_in_screen_ = gfx::Rect();
  1028. drag_and_drop_shelf_id_ = ShelfID();
  1029. is_active_drag_and_drop_host_ = false;
  1030. }
  1031. void ShelfView::SwapButtons(views::View* button_to_swap, bool with_next) {
  1032. if (!button_to_swap)
  1033. return;
  1034. // Find the index of the button to swap in the view model.
  1035. size_t src_index = static_cast<size_t>(-1);
  1036. for (size_t i = 0; i < view_model_->view_size(); ++i) {
  1037. View* view = view_model_->view_at(i);
  1038. if (view == button_to_swap) {
  1039. src_index = i;
  1040. break;
  1041. }
  1042. }
  1043. // Swapping items in the model is sufficient, everything will then be
  1044. // reflected in the views.
  1045. if (model_->Swap(src_index, with_next)) {
  1046. AnimateToIdealBounds();
  1047. const ShelfItem src_item = model_->items()[src_index];
  1048. const ShelfItem dst_item =
  1049. model_->items()[src_index + (with_next ? 1 : -1)];
  1050. AnnounceSwapEvent(src_item, dst_item);
  1051. }
  1052. }
  1053. void ShelfView::PointerPressedOnButton(views::View* view,
  1054. Pointer pointer,
  1055. const ui::LocatedEvent& event) {
  1056. if (drag_view_)
  1057. return;
  1058. if (IsShowingMenu())
  1059. shelf_menu_model_adapter_->Cancel();
  1060. auto index = view_model_->GetIndexOfView(view);
  1061. if (!index.has_value() || view_model_->view_size() < 1)
  1062. return; // View is being deleted, ignore request.
  1063. // Reset drag icon proxy from previous drag (which could be set if the drop
  1064. // animation is still in progress), as drag icon proxy is not expected to be
  1065. // reused after it starts animating out.
  1066. drag_icon_proxy_.reset();
  1067. // Only when the repost event occurs on the same shelf item, we should ignore
  1068. // the call in ShelfView::ButtonPressed(...).
  1069. is_repost_event_on_same_item_ =
  1070. IsRepostEvent(event) && (last_pressed_index_ == index);
  1071. CHECK_EQ(ShelfAppButton::kViewClassName, view->GetClassName());
  1072. drag_view_ = static_cast<ShelfAppButton*>(view);
  1073. drag_origin_ = gfx::Point(event.x(), event.y());
  1074. UMA_HISTOGRAM_ENUMERATION("Ash.ShelfAlignmentUsage",
  1075. static_cast<ShelfAlignmentUmaEnumValue>(
  1076. shelf_->SelectValueForShelfAlignment(
  1077. SHELF_ALIGNMENT_UMA_ENUM_VALUE_BOTTOM,
  1078. SHELF_ALIGNMENT_UMA_ENUM_VALUE_LEFT,
  1079. SHELF_ALIGNMENT_UMA_ENUM_VALUE_RIGHT)),
  1080. SHELF_ALIGNMENT_UMA_ENUM_VALUE_COUNT);
  1081. }
  1082. void ShelfView::PointerDraggedOnButton(const views::View* view,
  1083. Pointer pointer,
  1084. const ui::LocatedEvent& event) {
  1085. if (CanPrepareForDrag(pointer, event))
  1086. PrepareForDrag(pointer, event);
  1087. if (drag_pointer_ == pointer)
  1088. ContinueDrag(event);
  1089. }
  1090. void ShelfView::PointerReleasedOnButton(const views::View* view,
  1091. Pointer pointer,
  1092. bool canceled) {
  1093. drag_scroll_dir_ = 0;
  1094. scrolling_timer_.Stop();
  1095. speed_up_drag_scrolling_.Stop();
  1096. is_repost_event_on_same_item_ = false;
  1097. if (canceled) {
  1098. CancelDrag(absl::nullopt);
  1099. } else if (drag_pointer_ == pointer) {
  1100. FinalizeRipOffDrag(false);
  1101. drag_pointer_ = NONE;
  1102. // Check if the pin status of |drag_view_| should be changed when
  1103. // |drag_view_| is dragged over the separator. Do nothing if |drag_view_| is
  1104. // already handled in FinalizedRipOffDrag.
  1105. if (drag_view_) {
  1106. if (ShouldUpdateDraggedViewPinStatus(
  1107. view_model_->GetIndexOfView(view).value())) {
  1108. const std::string drag_app_id = ShelfItemForView(drag_view_)->id.app_id;
  1109. ShelfModel::ScopedUserTriggeredMutation user_triggered(model_);
  1110. if (model_->IsAppPinned(drag_app_id)) {
  1111. model_->UnpinAppWithID(drag_app_id);
  1112. } else {
  1113. model_->PinExistingItemWithID(drag_app_id);
  1114. }
  1115. }
  1116. }
  1117. AnimateToIdealBounds();
  1118. }
  1119. if (drag_pointer_ != NONE)
  1120. return;
  1121. delegate_->CancelScrollForItemDrag();
  1122. if (!drag_view_ || dragged_off_shelf_)
  1123. drag_icon_proxy_.reset();
  1124. if (drag_icon_proxy_) {
  1125. const gfx::Rect drag_view_ideal_bounds = view_model_->ideal_bounds(
  1126. view_model_->GetIndexOfView(drag_view_).value());
  1127. gfx::Rect target_bounds_in_screen =
  1128. drag_view_->GetIdealIconBounds(drag_view_ideal_bounds.size(),
  1129. /*icon_scale=*/1.0f);
  1130. target_bounds_in_screen.Offset(drag_view_ideal_bounds.x(),
  1131. drag_view_ideal_bounds.y());
  1132. target_bounds_in_screen = GetMirroredRect(target_bounds_in_screen);
  1133. views::View::ConvertRectToScreen(this, &target_bounds_in_screen);
  1134. if (!delegate_->AreBoundsWithinVisibleSpace(target_bounds_in_screen)) {
  1135. drag_icon_proxy_.reset();
  1136. drag_view_->layer()->SetOpacity(1.0f);
  1137. } else {
  1138. drag_icon_proxy_->AnimateToBoundsAndCloseWidget(
  1139. target_bounds_in_screen,
  1140. base::BindOnce(&ShelfView::OnDragIconProxyAnimatedOut,
  1141. base::Unretained(this),
  1142. std::make_unique<ViewOpacityResetter>(drag_view_)));
  1143. }
  1144. }
  1145. // If the drag pointer is NONE, no drag operation is going on and the
  1146. // |drag_view_| can be released.
  1147. drag_view_ = nullptr;
  1148. drag_view_relative_to_ideal_bounds_ = RelativePosition::kNotAvailable;
  1149. RemoveGhostView();
  1150. }
  1151. void ShelfView::OnDragIconProxyAnimatedOut(
  1152. std::unique_ptr<ViewOpacityResetter> opacity_resetter) {
  1153. opacity_resetter->Run();
  1154. drag_icon_proxy_.reset();
  1155. }
  1156. void ShelfView::LayoutToIdealBounds() {
  1157. if (bounds_animator_->IsAnimating()) {
  1158. AnimateToIdealBounds();
  1159. return;
  1160. }
  1161. CalculateIdealBounds();
  1162. views::ViewModelUtils::SetViewBoundsToIdealBounds(*view_model_);
  1163. UpdateVisibleShelfItemBoundsUnion();
  1164. }
  1165. bool ShelfView::IsItemPinned(const ShelfItem& item) const {
  1166. return IsPinnedShelfItemType(item.type);
  1167. }
  1168. bool ShelfView::IsItemVisible(const ShelfItem& item) const {
  1169. return (IsItemPinned(item) || item.is_on_active_desk) &&
  1170. !(model_->in_shelf_party() && item.status == STATUS_CLOSED);
  1171. }
  1172. void ShelfView::OnTabletModeChanged() {
  1173. // The layout change will happen as part of shelf config update.
  1174. }
  1175. void ShelfView::AnimateToIdealBounds() {
  1176. CalculateIdealBounds();
  1177. move_animation_tracker_.emplace(
  1178. GetWidget()->GetCompositor()->RequestNewThroughputTracker());
  1179. move_animation_tracker_->Start(metrics_util::ForSmoothness(
  1180. base::BindRepeating(&ReportMoveAnimationSmoothness)));
  1181. for (size_t i = 0; i < view_model_->view_size(); ++i) {
  1182. View* view = view_model_->view_at(i);
  1183. bounds_animator_->AnimateViewTo(view, view_model_->ideal_bounds(i));
  1184. // Now that the item animation starts, we have to make sure that the
  1185. // padding of the first gets properly transferred to the new first item.
  1186. view->SetBorder(nullptr);
  1187. }
  1188. UpdateVisibleShelfItemBoundsUnion();
  1189. }
  1190. void ShelfView::FadeIn(views::View* view) {
  1191. view->SetVisible(true);
  1192. view->layer()->SetOpacity(0);
  1193. ui::ScopedLayerAnimationSettings fade_in_animation_settings(
  1194. view->layer()->GetAnimator());
  1195. fade_in_animation_settings.SetTweenType(gfx::Tween::EASE_OUT);
  1196. fade_in_animation_settings.SetPreemptionStrategy(
  1197. ui::LayerAnimator::IMMEDIATELY_SET_NEW_TARGET);
  1198. fade_in_animation_settings.AddObserver(fade_in_animation_delegate_.get());
  1199. ui::AnimationThroughputReporter reporter(
  1200. fade_in_animation_settings.GetAnimator(),
  1201. metrics_util::ForSmoothness(
  1202. base::BindRepeating(&ReportFadeInAnimationSmoothness)));
  1203. view->layer()->SetOpacity(1.f);
  1204. }
  1205. void ShelfView::PrepareForDrag(Pointer pointer, const ui::LocatedEvent& event) {
  1206. DCHECK(!dragging());
  1207. DCHECK(drag_view_);
  1208. drag_pointer_ = pointer;
  1209. start_drag_index_ = view_model_->GetIndexOfView(drag_view_);
  1210. drag_scroll_dir_ = 0;
  1211. if (!start_drag_index_.has_value()) {
  1212. CancelDrag(absl::nullopt);
  1213. return;
  1214. }
  1215. // Cancel in-flight request for app item context menu model (made when app
  1216. // context menu is requested), to prevent the pending callback from showing
  1217. // a context menu just after drag starts.
  1218. if (!context_menu_callback_.IsCancelled()) {
  1219. GetShelfAppButton(item_awaiting_response_)
  1220. ->OnContextMenuModelRequestCanceled();
  1221. ResetActiveMenuModelRequest();
  1222. }
  1223. // Move the view to the front so that it appears on top of other views.
  1224. ReorderChildView(drag_view_, children().size());
  1225. bounds_animator_->StopAnimatingView(drag_view_);
  1226. drag_view_->OnDragStarted(&event);
  1227. // Drag icon proxy from previous drag may be around if the icon is still
  1228. // animating to the final position. Reset it here to cancel the animation.
  1229. drag_icon_proxy_.reset();
  1230. delegate_->CancelScrollForItemDrag();
  1231. drag_view_->layer()->SetOpacity(0.0f);
  1232. if (!is_active_drag_and_drop_host_) {
  1233. aura::Window* root_window = GetWidget()->GetNativeWindow()->GetRootWindow();
  1234. gfx::Point screen_location = event.root_location();
  1235. ::wm::ConvertPointToScreen(root_window, &screen_location);
  1236. // Scale up the icon only if the button is not considered as dragged and
  1237. // scaled up in ShelfAppButton.
  1238. float scale_factor = (drag_view_->state() & ShelfAppButton::STATE_DRAGGING)
  1239. ? 1.0f
  1240. : kDragAndDropProxyScale;
  1241. drag_icon_proxy_ = std::make_unique<AppDragIconProxy>(
  1242. root_window, drag_view_->GetIconImage(), screen_location,
  1243. gfx::Vector2d(), scale_factor, /*use_blurred_background=*/false);
  1244. if (pointer == MOUSE) {
  1245. haptics_util::PlayHapticTouchpadEffect(
  1246. ui::HapticTouchpadEffect::kTick,
  1247. ui::HapticTouchpadEffectStrength::kMedium);
  1248. }
  1249. }
  1250. }
  1251. void ShelfView::ContinueDrag(const ui::LocatedEvent& event) {
  1252. DCHECK(dragging());
  1253. DCHECK(drag_view_);
  1254. const auto index = view_model_->GetIndexOfView(drag_view_);
  1255. DCHECK(index.has_value());
  1256. const bool dragged_off_shelf_before = dragged_off_shelf_;
  1257. // Handle rip off functionality if this is not a drag and drop host operation
  1258. // and not the app list item.
  1259. if (drag_and_drop_shelf_id_.IsNull() &&
  1260. RemovableByRipOff(index.value()) != NOT_REMOVABLE) {
  1261. HandleRipOffDrag(event);
  1262. // Check if the item got ripped off the shelf - if it did we are done.
  1263. if (dragged_off_shelf_) {
  1264. drag_scroll_dir_ = 0;
  1265. scrolling_timer_.Stop();
  1266. speed_up_drag_scrolling_.Stop();
  1267. if (!dragged_off_shelf_before)
  1268. model_->OnItemRippedOff();
  1269. return;
  1270. }
  1271. }
  1272. // Calculates the drag point in screen before MoveDragViewTo is called.
  1273. gfx::Point drag_point_in_screen(event.location());
  1274. ConvertPointToScreen(drag_view_, &drag_point_in_screen);
  1275. gfx::Point drag_point(event.location());
  1276. ConvertPointToTarget(drag_view_, this, &drag_point);
  1277. MoveDragViewTo(shelf_->PrimaryAxisValue(drag_point.x() - drag_origin_.x(),
  1278. drag_point.y() - drag_origin_.y()));
  1279. if (drag_icon_proxy_)
  1280. drag_icon_proxy_->UpdatePosition(drag_point_in_screen);
  1281. // If drag_icon_proxy_ is available, get its item bounds, otherwise (in case
  1282. // of an ApplicationDragAndDropHost drag), last drag icon bounds are cached in
  1283. // `drag_icon_bounds_in_screen_`.
  1284. const gfx::Rect drag_icon_bounds_in_screen =
  1285. drag_icon_proxy_ ? drag_icon_proxy_->GetBoundsInScreen()
  1286. : drag_icon_bounds_in_screen_;
  1287. delegate_->ScheduleScrollForItemDragIfNeeded(drag_icon_bounds_in_screen);
  1288. if (dragged_off_shelf_before) {
  1289. model_->OnItemReturnedFromRipOff(
  1290. static_cast<int>(view_model_->GetIndexOfView(drag_view_).value()));
  1291. }
  1292. }
  1293. void ShelfView::MoveDragViewTo(int primary_axis_coordinate) {
  1294. const size_t current_item_index =
  1295. view_model_->GetIndexOfView(drag_view_).value();
  1296. const std::pair<size_t, size_t> indices(GetDragRange(current_item_index));
  1297. if (shelf_->IsHorizontalAlignment()) {
  1298. int x = GetMirroredXWithWidthInView(primary_axis_coordinate,
  1299. drag_view_->width());
  1300. x = std::max(view_model_->ideal_bounds(indices.first).x(), x);
  1301. x = std::min(view_model_->ideal_bounds(indices.second).right() -
  1302. view_model_->ideal_bounds(current_item_index).width(),
  1303. x);
  1304. if (drag_view_->x() != x)
  1305. drag_view_->SetX(x);
  1306. } else {
  1307. int y = std::max(view_model_->ideal_bounds(indices.first).y(),
  1308. primary_axis_coordinate);
  1309. y = std::min(view_model_->ideal_bounds(indices.second).bottom() -
  1310. view_model_->ideal_bounds(current_item_index).height(),
  1311. y);
  1312. if (drag_view_->y() != y)
  1313. drag_view_->SetY(y);
  1314. }
  1315. size_t target_index = views::ViewModelUtils::DetermineMoveIndex(
  1316. *view_model_, drag_view_, shelf_->IsHorizontalAlignment(),
  1317. drag_view_->x(), drag_view_->y());
  1318. target_index = base::clamp(target_index, indices.first, indices.second);
  1319. // Check the relative position of |drag_view_| and its ideal bounds if it can
  1320. // be dragged across the separator to pin or unpin.
  1321. if (CanDragAcrossSeparator(drag_view_)) {
  1322. // Compare the center points of |drag_view_| and its ideal bounds to
  1323. // determine whether the separator should be moved to the left or right by
  1324. // using |drag_view_relative_to_ideal_bounds_|. The actual position will
  1325. // be updated in CalculateIdealBounds.
  1326. gfx::Point drag_view_center = drag_view_->bounds().CenterPoint();
  1327. int drag_view_position =
  1328. shelf()->PrimaryAxisValue(drag_view_center.x(), drag_view_center.y());
  1329. gfx::Point ideal_bound_center =
  1330. view_model_->ideal_bounds(target_index).CenterPoint();
  1331. int ideal_bound_position = shelf()->PrimaryAxisValue(
  1332. ideal_bound_center.x(), ideal_bound_center.y());
  1333. drag_view_relative_to_ideal_bounds_ =
  1334. drag_view_position < ideal_bound_position ? RelativePosition::kLeft
  1335. : RelativePosition::kRight;
  1336. if (target_index == current_item_index) {
  1337. AnimateToIdealBounds();
  1338. NotifyAccessibilityEvent(ax::mojom::Event::kChildrenChanged,
  1339. true /* send_native_event */);
  1340. }
  1341. }
  1342. if (target_index == current_item_index)
  1343. return;
  1344. // Change the model if the dragged item index is changed, the ShelfItemMoved()
  1345. // callback will handle the |view_model_| update.
  1346. model_->Move(current_item_index, target_index);
  1347. bounds_animator_->StopAnimatingView(drag_view_);
  1348. }
  1349. void ShelfView::HandleRipOffDrag(const ui::LocatedEvent& event) {
  1350. auto current_index = view_model_->GetIndexOfView(drag_view_);
  1351. DCHECK(current_index.has_value());
  1352. std::string dragged_app_id = model_->items()[current_index.value()].id.app_id;
  1353. aura::Window* root_window = GetWidget()->GetNativeWindow()->GetRootWindow();
  1354. gfx::Point screen_location = event.root_location();
  1355. ::wm::ConvertPointToScreen(root_window, &screen_location);
  1356. // To avoid ugly forwards and backwards flipping we use different constants
  1357. // for ripping off / re-inserting the items.
  1358. if (dragged_off_shelf_) {
  1359. // If the shelf/overflow bubble bounds contains |screen_location| we insert
  1360. // the item back into the shelf.
  1361. if (GetBoundsForDragInsertInScreen().Contains(screen_location)) {
  1362. if (!is_active_drag_and_drop_host_) {
  1363. drag_icon_proxy_ = std::make_unique<AppDragIconProxy>(
  1364. root_window, drag_view_->GetIconImage(), screen_location,
  1365. /*cursor_offset_from_center=*/gfx::Vector2d(),
  1366. /*scale_factor=*/1.0f,
  1367. /*use_blurred_background=*/false);
  1368. }
  1369. // Re-insert the item and return simply false since the caller will handle
  1370. // the move as in any normal case.
  1371. dragged_off_shelf_ = false;
  1372. // After re-insertion, trigger an animation to ideal bounds to show the
  1373. // ghost view.
  1374. AnimateToIdealBounds();
  1375. return;
  1376. }
  1377. drag_icon_proxy_->UpdatePosition(screen_location);
  1378. return;
  1379. }
  1380. // Mark the item as dragged off the shelf if the drag distance exceeds
  1381. // `rip_off_distance`.
  1382. int rip_off_distance =
  1383. ShelfConfig::Get()->shelf_size() * kRipOffDistanceFactor;
  1384. int delta = CalculateShelfDistance(screen_location);
  1385. bool dragged_off_shelf = delta > rip_off_distance;
  1386. if (dragged_off_shelf) {
  1387. if (!is_active_drag_and_drop_host_) {
  1388. // Create a new, scaled up drag icon proxy when the item is dragged off
  1389. // shelf - keep cursor position consistent with the host provided icon.
  1390. const gfx::Point center = drag_view_->GetLocalBounds().CenterPoint();
  1391. const gfx::Vector2d cursor_offset_from_center = drag_origin_ - center;
  1392. drag_icon_proxy_ = std::make_unique<AppDragIconProxy>(
  1393. root_window, drag_view_->GetIconImage(), screen_location,
  1394. cursor_offset_from_center, /*scale_factor=*/1.0f,
  1395. /*use_blurred_background=*/false);
  1396. delegate_->CancelScrollForItemDrag();
  1397. }
  1398. dragged_off_shelf_ = true;
  1399. RemoveGhostView();
  1400. if (RemovableByRipOff(current_index.value()) == REMOVABLE) {
  1401. // Move the item to the back and hide it. ShelfItemMoved() callback will
  1402. // handle the |view_model_| update and call AnimateToIdealBounds().
  1403. if (current_index.value() !=
  1404. static_cast<size_t>(model_->item_count() - 1)) {
  1405. model_->Move(current_index.value(), model_->item_count() - 1);
  1406. }
  1407. // Make the item partially disappear to show that it will get removed if
  1408. // dropped.
  1409. drag_icon_proxy_->SetOpacity(kDraggedImageOpacity);
  1410. }
  1411. }
  1412. }
  1413. void ShelfView::FinalizeRipOffDrag(bool cancel) {
  1414. if (!dragged_off_shelf_)
  1415. return;
  1416. // Make sure we do not come in here again.
  1417. dragged_off_shelf_ = false;
  1418. // Coming here we should always have a |drag_view_|.
  1419. DCHECK(drag_view_);
  1420. DCHECK(drag_icon_proxy_);
  1421. delegate_->CancelScrollForItemDrag();
  1422. auto current_index = view_model_->GetIndexOfView(drag_view_);
  1423. // If the view isn't part of the model anymore, a sync operation must have
  1424. // removed it. In that case we shouldn't change the model and only delete the
  1425. // proxy image.
  1426. if (!current_index.has_value()) {
  1427. drag_icon_proxy_.reset();
  1428. return;
  1429. }
  1430. // Set to true when the animation should snap back to where it was before.
  1431. bool snap_back = false;
  1432. // Items which cannot be dragged off will be handled as a cancel.
  1433. if (!cancel) {
  1434. if (RemovableByRipOff(current_index.value()) != REMOVABLE) {
  1435. // Make sure we do not try to remove un-removable items like items which
  1436. // were not pinned or have to be always there.
  1437. cancel = true;
  1438. snap_back = true;
  1439. } else {
  1440. // Make sure the item stays invisible upon removal.
  1441. drag_view_->SetVisible(false);
  1442. ShelfModel::ScopedUserTriggeredMutation user_triggered(model_);
  1443. model_->UnpinAppWithID(model_->items()[current_index.value()].id.app_id);
  1444. }
  1445. }
  1446. if (cancel || snap_back) {
  1447. if (!cancelling_drag_model_changed_) {
  1448. // Only do something if the change did not come through a model change.
  1449. gfx::Rect drag_bounds = drag_icon_proxy_->GetBoundsInScreen();
  1450. gfx::Point relative_to = GetBoundsInScreen().origin();
  1451. gfx::Rect target(
  1452. gfx::PointAtOffsetFromOrigin(drag_bounds.origin() - relative_to),
  1453. drag_bounds.size());
  1454. drag_view_->SetBoundsRect(target);
  1455. // Hide the status from the active item since we snap it back now. Upon
  1456. // animation end the flag gets cleared if |snap_back_from_rip_off_view_|
  1457. // is set.
  1458. snap_back_from_rip_off_view_ = drag_view_;
  1459. drag_view_->AddState(ShelfAppButton::STATE_HIDDEN);
  1460. // When a canceling drag model is happening, the view model is diverged
  1461. // from the menu model and movements / animations should not be done.
  1462. model_->Move(current_index.value(), start_drag_index_.value());
  1463. AnimateToIdealBounds();
  1464. }
  1465. drag_view_->layer()->SetOpacity(1.0f);
  1466. model_->OnItemReturnedFromRipOff(model_->item_count() - 1);
  1467. }
  1468. drag_icon_proxy_.reset();
  1469. }
  1470. ShelfView::RemovableState ShelfView::RemovableByRipOff(int index) const {
  1471. DCHECK(index >= 0 && index < model_->item_count());
  1472. ShelfItemType type = model_->items()[index].type;
  1473. if (type == TYPE_DIALOG)
  1474. return NOT_REMOVABLE;
  1475. if (model_->items()[index].pinned_by_policy)
  1476. return NOT_REMOVABLE;
  1477. // Note: Only pinned app shortcuts can be removed!
  1478. const std::string& app_id = model_->items()[index].id.app_id;
  1479. // Pinned standalone browser apps should not be removable.
  1480. if (IsStandaloneBrowser(app_id))
  1481. return DRAGGABLE;
  1482. return (type == TYPE_PINNED_APP && model_->IsAppPinned(app_id)) ? REMOVABLE
  1483. : DRAGGABLE;
  1484. }
  1485. bool ShelfView::SameDragType(ShelfItemType typea, ShelfItemType typeb) const {
  1486. if (IsPinnedShelfItemType(typea) && IsPinnedShelfItemType(typeb))
  1487. return true;
  1488. if (typea == TYPE_UNDEFINED || typeb == TYPE_UNDEFINED) {
  1489. NOTREACHED() << "ShelfItemType must be set.";
  1490. return false;
  1491. }
  1492. // Running app or dialog.
  1493. return typea == typeb;
  1494. }
  1495. bool ShelfView::ShouldFocusOut(bool reverse, views::View* button) {
  1496. // The logic here seems backwards, but is actually correct. For instance if
  1497. // the ShelfView's internal focus cycling logic attemmpts to focus the first
  1498. // child after hitting Tab, we intercept that and instead, advance through
  1499. // to the status area.
  1500. return (reverse && button == FindLastFocusableChild()) ||
  1501. (!reverse && button == FindFirstFocusableChild());
  1502. }
  1503. std::pair<size_t, size_t> ShelfView::GetDragRange(size_t index) {
  1504. DCHECK(base::Contains(visible_views_indices_, index));
  1505. const ShelfItem& dragged_item = model_->items()[index];
  1506. // If |drag_view_| is allowed to be dragged across the separator, return the
  1507. // first and the last index of the |visible_views_indices_|.
  1508. if (CanDragAcrossSeparator(drag_view_)) {
  1509. return std::make_pair(visible_views_indices_[0],
  1510. visible_views_indices_.back());
  1511. }
  1512. absl::optional<size_t> first = absl::nullopt;
  1513. absl::optional<size_t> last = absl::nullopt;
  1514. for (size_t i : visible_views_indices_) {
  1515. if (SameDragType(model_->items()[i].type, dragged_item.type)) {
  1516. if (!first.has_value())
  1517. first = i;
  1518. last = i;
  1519. } else if (first.has_value()) {
  1520. break;
  1521. }
  1522. }
  1523. DCHECK(first.has_value());
  1524. DCHECK(last.has_value());
  1525. // TODO(afakhry): Consider changing this when taking into account inactive
  1526. // desks.
  1527. return std::make_pair(first.value(), last.value());
  1528. }
  1529. bool ShelfView::ShouldUpdateDraggedViewPinStatus(size_t dragged_view_index) {
  1530. if (!features::IsDragUnpinnedAppToPinEnabled())
  1531. return false;
  1532. DCHECK(base::Contains(visible_views_indices_, dragged_view_index));
  1533. bool is_moved_item_pinned =
  1534. IsPinnedShelfItemType(model_->items()[dragged_view_index].type);
  1535. if (!separator_index_.has_value()) {
  1536. // If there is no |separator_index_|, all the apps in shelf are expected to
  1537. // have the same pinned status.
  1538. for (auto index : visible_views_indices_) {
  1539. if (index != dragged_view_index) {
  1540. // Return true if the pin status of the moved item is different from
  1541. // others.
  1542. return is_moved_item_pinned !=
  1543. IsPinnedShelfItemType(model_->items()[index].type);
  1544. }
  1545. }
  1546. return false;
  1547. }
  1548. // If the separator is shown, check whether the pin status of dragged item
  1549. // matches the pin status implied by the dragged view position relative to the
  1550. // separator.
  1551. bool should_pinned_by_position =
  1552. dragged_view_index <= separator_index_.value();
  1553. return should_pinned_by_position != is_moved_item_pinned;
  1554. }
  1555. bool ShelfView::CanDragAcrossSeparator(views::View* drag_view) const {
  1556. if (!features::IsDragUnpinnedAppToPinEnabled())
  1557. return false;
  1558. DCHECK(drag_view);
  1559. // The dragged item is not allowed to be unpinned if |drag_view| is pinned by
  1560. // policy, dragged from app list, or its item type is TYPE_BROWSER_SHORTCUT
  1561. // or TYPE_UNPINNED_BROWSER_SHORTCUT.
  1562. // Therefore, the |drag_view| can not be dragged across the separator.
  1563. bool can_change_pin_state =
  1564. ShelfItemForView(drag_view)->type == TYPE_PINNED_APP ||
  1565. ShelfItemForView(drag_view)->type == TYPE_APP;
  1566. // Note that |drag_and_drop_shelf_id_| is set only when the current drag view
  1567. // is from app list, which can not be dragged to the unpinned app side.
  1568. return !ShelfItemForView(drag_view)->pinned_by_policy &&
  1569. drag_and_drop_shelf_id_ == ShelfID() && can_change_pin_state;
  1570. }
  1571. void ShelfView::OnFadeInAnimationEnded() {
  1572. // Call PreferredSizeChanged() to notify container to re-layout at the end
  1573. // of fade-in animation.
  1574. PreferredSizeChanged();
  1575. }
  1576. void ShelfView::OnFadeOutAnimationEnded() {
  1577. if (fade_out_animation_tracker_) {
  1578. fade_out_animation_tracker_->Stop();
  1579. fade_out_animation_tracker_.reset();
  1580. }
  1581. // Call PreferredSizeChanged() to notify container to re-layout at the end
  1582. // of removal animation.
  1583. PreferredSizeChanged();
  1584. AnimateToIdealBounds();
  1585. }
  1586. gfx::Rect ShelfView::GetMenuAnchorRect(const views::View& source,
  1587. const gfx::Point& location,
  1588. bool context_menu) const {
  1589. // Application menus for items are anchored on the icon bounds.
  1590. if (ShelfItemForView(&source) || !context_menu)
  1591. return source.GetBoundsInScreen();
  1592. gfx::Rect shelf_bounds_in_screen;
  1593. if (ShelfConfig::Get()->is_in_app() && IsTabletModeEnabled()) {
  1594. // Use the shelf widget background as the menu anchor point in tablet mode
  1595. // and in app.
  1596. ShelfWidget* shelf_widget = shelf_->shelf_widget();
  1597. shelf_bounds_in_screen = shelf_widget->GetOpaqueBackground()->bounds();
  1598. const gfx::Rect widget_bounds =
  1599. shelf_widget->GetRootView()->GetBoundsInScreen();
  1600. shelf_bounds_in_screen.Offset(widget_bounds.x(), widget_bounds.y());
  1601. } else {
  1602. shelf_bounds_in_screen = GetBoundsInScreen();
  1603. }
  1604. gfx::Point origin;
  1605. switch (shelf_->alignment()) {
  1606. case ShelfAlignment::kBottom:
  1607. case ShelfAlignment::kBottomLocked:
  1608. origin = gfx::Point(location.x(), shelf_bounds_in_screen.y());
  1609. break;
  1610. case ShelfAlignment::kLeft:
  1611. origin = gfx::Point(shelf_bounds_in_screen.right(), location.y());
  1612. break;
  1613. case ShelfAlignment::kRight:
  1614. origin = gfx::Point(shelf_bounds_in_screen.x(), location.y());
  1615. break;
  1616. }
  1617. return gfx::Rect(origin, gfx::Size());
  1618. }
  1619. void ShelfView::AnnounceShelfAlignment() {
  1620. std::u16string announcement;
  1621. switch (shelf_->alignment()) {
  1622. case ShelfAlignment::kBottom:
  1623. case ShelfAlignment::kBottomLocked:
  1624. announcement = l10n_util::GetStringUTF16(IDS_SHELF_ALIGNMENT_BOTTOM);
  1625. break;
  1626. case ShelfAlignment::kLeft:
  1627. announcement = l10n_util::GetStringUTF16(IDS_SHELF_ALIGNMENT_LEFT);
  1628. break;
  1629. case ShelfAlignment::kRight:
  1630. announcement = l10n_util::GetStringUTF16(IDS_SHELF_ALIGNMENT_RIGHT);
  1631. break;
  1632. }
  1633. announcement_view_->GetViewAccessibility().OverrideName(announcement);
  1634. announcement_view_->NotifyAccessibilityEvent(ax::mojom::Event::kAlert,
  1635. /*send_native_event=*/true);
  1636. }
  1637. bool ShelfView::IsAnimating() const {
  1638. return bounds_animator_->IsAnimating();
  1639. }
  1640. gfx::Rect ShelfView::GetDragIconBoundsInScreenForTest() const {
  1641. if (!drag_icon_proxy_)
  1642. return gfx::Rect();
  1643. return drag_icon_proxy_->GetBoundsInScreen();
  1644. }
  1645. void ShelfView::AnnounceShelfAutohideBehavior() {
  1646. std::u16string announcement;
  1647. switch (shelf_->auto_hide_behavior()) {
  1648. case ShelfAutoHideBehavior::kAlways:
  1649. announcement = l10n_util::GetStringUTF16(IDS_SHELF_STATE_AUTO_HIDE);
  1650. break;
  1651. case ShelfAutoHideBehavior::kNever:
  1652. announcement = l10n_util::GetStringUTF16(IDS_SHELF_STATE_ALWAYS_SHOWN);
  1653. break;
  1654. case ShelfAutoHideBehavior::kAlwaysHidden:
  1655. announcement = l10n_util::GetStringUTF16(IDS_SHELF_STATE_ALWAYS_HIDDEN);
  1656. break;
  1657. }
  1658. announcement_view_->GetViewAccessibility().OverrideName(announcement);
  1659. announcement_view_->NotifyAccessibilityEvent(ax::mojom::Event::kAlert,
  1660. /*send_native_event=*/true);
  1661. }
  1662. void ShelfView::AnnouncePinUnpinEvent(const ShelfItem& item, bool pinned) {
  1663. std::u16string item_title =
  1664. item.title.empty()
  1665. ? l10n_util::GetStringUTF16(IDS_SHELF_ITEM_GENERIC_NAME)
  1666. : item.title;
  1667. std::u16string announcement = l10n_util::GetStringFUTF16(
  1668. pinned ? IDS_SHELF_ITEM_WAS_PINNED : IDS_SHELF_ITEM_WAS_UNPINNED,
  1669. item_title);
  1670. announcement_view_->GetViewAccessibility().OverrideName(announcement);
  1671. announcement_view_->NotifyAccessibilityEvent(ax::mojom::Event::kAlert,
  1672. /*send_native_event=*/true);
  1673. }
  1674. void ShelfView::AnnounceSwapEvent(const ShelfItem& first_item,
  1675. const ShelfItem& second_item) {
  1676. std::u16string first_item_title =
  1677. first_item.title.empty()
  1678. ? l10n_util::GetStringUTF16(IDS_SHELF_ITEM_GENERIC_NAME)
  1679. : first_item.title;
  1680. std::u16string second_item_title =
  1681. second_item.title.empty()
  1682. ? l10n_util::GetStringUTF16(IDS_SHELF_ITEM_GENERIC_NAME)
  1683. : second_item.title;
  1684. std::u16string announcement = l10n_util::GetStringFUTF16(
  1685. IDS_SHELF_ITEMS_WERE_SWAPPED, first_item_title, second_item_title);
  1686. announcement_view_->GetViewAccessibility().OverrideName(announcement);
  1687. announcement_view_->NotifyAccessibilityEvent(ax::mojom::Event::kAlert,
  1688. /*send_native_event=*/true);
  1689. }
  1690. gfx::Rect ShelfView::GetBoundsForDragInsertInScreen() {
  1691. const ScrollableShelfView* scrollable_shelf_view =
  1692. shelf_->hotseat_widget()->scrollable_shelf_view();
  1693. gfx::Rect bounds = scrollable_shelf_view->visible_space();
  1694. views::View::ConvertRectToScreen(scrollable_shelf_view, &bounds);
  1695. return bounds;
  1696. }
  1697. absl::optional<size_t> ShelfView::CancelDrag(
  1698. absl::optional<size_t> modified_index) {
  1699. drag_scroll_dir_ = 0;
  1700. scrolling_timer_.Stop();
  1701. speed_up_drag_scrolling_.Stop();
  1702. FinalizeRipOffDrag(true);
  1703. delegate_->CancelScrollForItemDrag();
  1704. drag_icon_proxy_.reset();
  1705. if (!drag_view_)
  1706. return modified_index;
  1707. bool was_dragging = dragging();
  1708. auto drag_view_index = view_model_->GetIndexOfView(drag_view_);
  1709. drag_pointer_ = NONE;
  1710. drag_view_ = nullptr;
  1711. if (drag_view_index == modified_index) {
  1712. // The view that was being dragged is being modified. Don't do anything.
  1713. return modified_index;
  1714. }
  1715. if (!was_dragging)
  1716. return modified_index;
  1717. // Restore previous position, tracking the position of the modified view.
  1718. bool at_end = modified_index == view_model_->view_size();
  1719. views::View* modified_view =
  1720. (modified_index.has_value() && !at_end)
  1721. ? view_model_->view_at(modified_index.value())
  1722. : nullptr;
  1723. model_->Move(drag_view_index.value(), start_drag_index_.value());
  1724. // If the modified view will be at the end of the list, return the new end of
  1725. // the list.
  1726. if (at_end)
  1727. return view_model_->view_size();
  1728. return modified_view ? view_model_->GetIndexOfView(modified_view)
  1729. : absl::nullopt;
  1730. }
  1731. void ShelfView::OnGestureEvent(ui::GestureEvent* event) {
  1732. if (!ShouldHandleGestures(*event))
  1733. return;
  1734. if (HandleGestureEvent(event))
  1735. event->StopPropagation();
  1736. }
  1737. void ShelfView::ShelfItemAdded(int model_index) {
  1738. // ShelfView must keep view_model_ in sync with ShelfModel::items_ as its very
  1739. // first response to ShelfModel changes. Failure to do so can result in UaF in
  1740. // methods like ShelfView::ShelfItemForView, which can be implicitly called by
  1741. // other classes. See https://crbug.com/1238959 for more details.
  1742. const ShelfItem& item(model_->items()[model_index]);
  1743. views::View* view = CreateViewForItem(item);
  1744. {
  1745. base::AutoReset<bool> cancelling_drag(&cancelling_drag_model_changed_,
  1746. true);
  1747. model_index = static_cast<int>(CancelDrag(model_index).value());
  1748. }
  1749. view_model_->Add(view, static_cast<size_t>(model_index));
  1750. // If |item| is pinned and the mutation is user-triggered, report the pinning
  1751. // action for accessibility and UMA. Do it now, because if |item| is hidden
  1752. // then we will soon bail out but we still want to report the pinning action.
  1753. if (model_->is_current_mutation_user_triggered() &&
  1754. item.type == TYPE_PINNED_APP) {
  1755. AnnouncePinUnpinEvent(item, /*pinned=*/true);
  1756. RecordPinUnpinUserAction(/*pinned=*/true);
  1757. }
  1758. // Add child view so it has the same ordering as in the |view_model_|.
  1759. // Note: No need to call UpdateShelfItemViewsVisibility() here directly, since
  1760. // it will be called by ScrollableShelfView::ViewHierarchyChanged() as a
  1761. // result of the below call.
  1762. AddChildViewAt(view, model_index);
  1763. if (!IsItemVisible(item))
  1764. return;
  1765. // Hide the view, it'll be made visible when the animation is done. Using
  1766. // opacity 0 here to avoid messing with CalculateIdealBounds which touches
  1767. // the view's visibility.
  1768. view->layer()->SetOpacity(0);
  1769. // Give the button its ideal bounds. That way if we end up animating the
  1770. // button before this animation completes it doesn't appear at some random
  1771. // spot (because it was in the middle of animating from 0,0 0x0 to its
  1772. // target).
  1773. CalculateIdealBounds();
  1774. view->SetBoundsRect(
  1775. view_model_->ideal_bounds(static_cast<size_t>(model_index)));
  1776. if (model_->is_current_mutation_user_triggered() &&
  1777. drag_and_drop_shelf_id_ != item.id) {
  1778. view->ScrollViewToVisible();
  1779. }
  1780. // The first animation moves all the views to their target position. |view|
  1781. // is hidden, so it visually appears as though we are providing space for
  1782. // it. When done we'll fade the view in.
  1783. AnimateToIdealBounds();
  1784. DCHECK_LE(static_cast<size_t>(model_index), visible_views_indices_.back());
  1785. bounds_animator_->SetAnimationDelegate(
  1786. view, std::unique_ptr<gfx::AnimationDelegate>(
  1787. new StartFadeAnimationDelegate(this, view)));
  1788. }
  1789. void ShelfView::ShelfItemRemoved(int model_index, const ShelfItem& old_item) {
  1790. // ShelfView must keep view_model_ in sync with ShelfModel::items_ as its very
  1791. // first response to ShelfModel changes. Failure to do so can result in UaF in
  1792. // methods like ShelfView::ShelfItemForView, which can be implicitly called by
  1793. // other classes. See https://crbug.com/1238959 for more details.
  1794. //
  1795. // If std::move is not called on |view|, |view| will be deleted once out of
  1796. // scope.
  1797. std::unique_ptr<views::View> view(view_model_->view_at(model_index));
  1798. shelf_button_delegate_->OnButtonWillBeRemoved();
  1799. view_model_->Remove(model_index);
  1800. if (old_item.id == context_menu_id_ && shelf_menu_model_adapter_)
  1801. shelf_menu_model_adapter_->Cancel();
  1802. if (old_item.id == item_awaiting_response_)
  1803. ResetActiveMenuModelRequest();
  1804. {
  1805. base::AutoReset<bool> cancelling_drag(&cancelling_drag_model_changed_,
  1806. true);
  1807. CancelDrag(absl::nullopt);
  1808. }
  1809. if (view.get() == shelf_->tooltip()->GetCurrentAnchorView())
  1810. shelf_->tooltip()->Close();
  1811. if (view->GetVisible() && view->layer()->opacity() > 0.0f) {
  1812. UpdateShelfItemViewsVisibility();
  1813. // There could be multiple fade out animations running. Only start
  1814. // tracking for the first one.
  1815. if (!fade_out_animation_tracker_) {
  1816. fade_out_animation_tracker_.emplace(
  1817. GetWidget()->GetCompositor()->RequestNewThroughputTracker());
  1818. fade_out_animation_tracker_->Start(metrics_util::ForSmoothness(
  1819. base::BindRepeating(&ReportFadeOutAnimationSmoothness)));
  1820. }
  1821. // The first animation fades out the view. When done we'll animate the rest
  1822. // of the views to their target location.
  1823. bounds_animator_->AnimateViewTo(view.get(), view->bounds());
  1824. auto* const view_ptr = view.get();
  1825. bounds_animator_->SetAnimationDelegate(
  1826. view_ptr,
  1827. std::make_unique<FadeOutAnimationDelegate>(this, std::move(view)));
  1828. } else {
  1829. // Ensures that |view| is not used after destruction.
  1830. StopAnimatingViewIfAny(view.get());
  1831. // Removes |view| to trigger ViewHierarchyChanged function in the parent
  1832. // view if any.
  1833. view.reset();
  1834. // If there is no fade out animation, notify the parent view of the
  1835. // changed size before bounds animations start.
  1836. PreferredSizeChanged();
  1837. // We don't need to show a fade out animation for invisible |view|. When an
  1838. // item is ripped out from the shelf, its |view| is already invisible.
  1839. AnimateToIdealBounds();
  1840. }
  1841. if (model_->is_current_mutation_user_triggered() &&
  1842. old_item.type == TYPE_PINNED_APP) {
  1843. AnnouncePinUnpinEvent(old_item, /*pinned=*/false);
  1844. RecordPinUnpinUserAction(/*pinned=*/false);
  1845. }
  1846. }
  1847. void ShelfView::ShelfItemChanged(int model_index, const ShelfItem& old_item) {
  1848. // Bail if the view and shelf sizes do not match. ShelfItemChanged may be
  1849. // called here before ShelfItemAdded, due to ChromeShelfController's
  1850. // item initialization, which calls SetItem during ShelfItemAdded.
  1851. if (model_->items().size() != view_model_->view_size())
  1852. return;
  1853. const ShelfItem& item = model_->items()[model_index];
  1854. // If there's a change in the item's active desk, perform the update at the
  1855. // end of this function in order to guarantee that both |model_| and
  1856. // |view_model_| are consistent if there are other changes in the item.
  1857. base::ScopedClosureRunner run_at_scope_exit;
  1858. if (old_item.is_on_active_desk != item.is_on_active_desk) {
  1859. run_at_scope_exit.ReplaceClosure(base::BindOnce(
  1860. &ShelfView::ShelfItemsUpdatedForDeskChange, base::Unretained(this)));
  1861. }
  1862. if (old_item.type != item.type) {
  1863. // Type changed, swap the views.
  1864. model_index = static_cast<int>(CancelDrag(model_index).value());
  1865. std::unique_ptr<views::View> old_view(view_model_->view_at(model_index));
  1866. bounds_animator_->StopAnimatingView(old_view.get());
  1867. // Removing and re-inserting a view in our view model will strip the ideal
  1868. // bounds from the item. To avoid recalculation of everything the bounds
  1869. // get remembered and restored after the insertion to the previous value.
  1870. gfx::Rect old_ideal_bounds = view_model_->ideal_bounds(model_index);
  1871. view_model_->Remove(model_index);
  1872. views::View* new_view = CreateViewForItem(item);
  1873. // The view must be added to the |view_model_| before it's added as a child
  1874. // so that the model is consistent when UpdateShelfItemViewsVisibility() is
  1875. // called as a result the hierarchy changes caused by AddChildView(). See
  1876. // ScrollableShelfView::ViewHierarchyChanged().
  1877. view_model_->Add(new_view, model_index);
  1878. AddChildView(new_view);
  1879. view_model_->set_ideal_bounds(model_index, old_ideal_bounds);
  1880. bounds_animator_->StopAnimatingView(new_view);
  1881. new_view->SetBoundsRect(old_view->bounds());
  1882. bounds_animator_->AnimateViewTo(new_view, old_ideal_bounds);
  1883. // If an item is being pinned or unpinned, show the new status of the
  1884. // shelf immediately so that the separator gets drawn as needed.
  1885. if (old_item.type == TYPE_PINNED_APP || item.type == TYPE_PINNED_APP) {
  1886. if (model_->is_current_mutation_user_triggered()) {
  1887. AnnouncePinUnpinEvent(old_item, item.type == TYPE_PINNED_APP);
  1888. RecordPinUnpinUserAction(item.type == TYPE_PINNED_APP);
  1889. }
  1890. AnimateToIdealBounds();
  1891. }
  1892. return;
  1893. }
  1894. views::View* view = view_model_->view_at(model_index);
  1895. switch (item.type) {
  1896. case TYPE_PINNED_APP:
  1897. case TYPE_BROWSER_SHORTCUT:
  1898. case TYPE_APP:
  1899. case TYPE_UNPINNED_BROWSER_SHORTCUT:
  1900. case TYPE_DIALOG: {
  1901. CHECK_EQ(ShelfAppButton::kViewClassName, view->GetClassName());
  1902. ShelfAppButton* button = static_cast<ShelfAppButton*>(view);
  1903. button->ReflectItemStatus(item);
  1904. button->SetImage(item.image);
  1905. button->SetNotificationBadgeColor(item.notification_badge_color);
  1906. button->SchedulePaint();
  1907. break;
  1908. }
  1909. case TYPE_UNDEFINED:
  1910. break;
  1911. }
  1912. if (model_->in_shelf_party())
  1913. HandleShelfParty();
  1914. }
  1915. void ShelfView::ShelfItemsUpdatedForDeskChange() {
  1916. DCHECK(features::IsPerDeskShelfEnabled());
  1917. // The order here matters, since switching/removing desks, or moving windows
  1918. // between desks will affect shelf items' visibility, we need to update the
  1919. // visibility of the views first before we layout.
  1920. UpdateShelfItemViewsVisibility();
  1921. // Signal to the parent ScrollableShelfView so that it can recenter the items
  1922. // after their visibility have been updated (via
  1923. // `UpdateAvailableSpaceAndScroll()`).
  1924. PreferredSizeChanged();
  1925. LayoutToIdealBounds();
  1926. }
  1927. void ShelfView::ShelfItemMoved(int start_index, int target_index) {
  1928. view_model_->Move(start_index, target_index);
  1929. // Reorder the child view to be in the same order as in the |view_model_|.
  1930. ReorderChildView(view_model_->view_at(target_index), target_index);
  1931. NotifyAccessibilityEvent(ax::mojom::Event::kChildrenChanged,
  1932. true /* send_native_event */);
  1933. // When cancelling a drag due to a shelf item being added, the currently
  1934. // dragged item is moved back to its initial position. AnimateToIdealBounds
  1935. // will be called again when the new item is added to the |view_model_| but
  1936. // at this time the |view_model_| is inconsistent with the |model_|.
  1937. if (!cancelling_drag_model_changed_)
  1938. AnimateToIdealBounds();
  1939. }
  1940. void ShelfView::ShelfItemDelegateChanged(const ShelfID& id,
  1941. ShelfItemDelegate* old_delegate,
  1942. ShelfItemDelegate* delegate) {
  1943. if (id == context_menu_id_ && shelf_menu_model_adapter_)
  1944. shelf_menu_model_adapter_->Cancel();
  1945. }
  1946. void ShelfView::ShelfItemStatusChanged(const ShelfID& id) {
  1947. scoped_display_for_new_windows_.reset();
  1948. int index = model_->ItemIndexByID(id);
  1949. if (index < 0)
  1950. return;
  1951. const ShelfItem item = model_->items()[index];
  1952. ShelfAppButton* button = GetShelfAppButton(id);
  1953. button->ReflectItemStatus(item);
  1954. button->SchedulePaint();
  1955. if (model_->in_shelf_party())
  1956. HandleShelfParty();
  1957. }
  1958. void ShelfView::ShelfItemRippedOff() {
  1959. // On the display where the drag started, there is nothing to do.
  1960. if (dragging())
  1961. return;
  1962. // When a dragged item has been ripped off the shelf, it is moved to the end.
  1963. // Now we need to hide it.
  1964. view_model_->view_at(model_->item_count() - 1)->layer()->SetOpacity(0.f);
  1965. }
  1966. void ShelfView::ShelfItemReturnedFromRipOff(int index) {
  1967. // On the display where the drag started, there is nothing to do.
  1968. if (dragging())
  1969. return;
  1970. // Show the item and prevent it from animating into place from the position
  1971. // where it was sitting with zero opacity.
  1972. views::View* view = view_model_->view_at(index);
  1973. const gfx::Rect bounds = bounds_animator_->GetTargetBounds(view);
  1974. bounds_animator_->StopAnimatingView(view);
  1975. view->SetBoundsRect(bounds);
  1976. view->layer()->SetOpacity(1.f);
  1977. }
  1978. void ShelfView::ShelfPartyToggled(bool in_shelf_party) {
  1979. HandleShelfParty();
  1980. }
  1981. void ShelfView::OnShelfAlignmentChanged(aura::Window* root_window,
  1982. ShelfAlignment old_alignment) {
  1983. LayoutToIdealBounds();
  1984. for (size_t visible_index : visible_views_indices_)
  1985. view_model_->view_at(visible_index)->Layout();
  1986. AnnounceShelfAlignment();
  1987. }
  1988. void ShelfView::OnShelfAutoHideBehaviorChanged() {
  1989. AnnounceShelfAutohideBehavior();
  1990. }
  1991. void ShelfView::AfterItemSelected(const ShelfItem& item,
  1992. views::Button* sender,
  1993. std::unique_ptr<ui::Event> event,
  1994. views::InkDrop* ink_drop,
  1995. ShelfAction action,
  1996. ShelfItemDelegate::AppMenuItems menu_items) {
  1997. item_awaiting_response_ = ShelfID();
  1998. shelf_button_pressed_metric_tracker_.ButtonPressed(*event, sender, action);
  1999. // Record AppList metric for any action considered an app launch.
  2000. if (action == SHELF_ACTION_NEW_WINDOW_CREATED ||
  2001. action == SHELF_ACTION_WINDOW_ACTIVATED) {
  2002. Shell::Get()->app_list_controller()->RecordShelfAppLaunched();
  2003. }
  2004. // The app list handles its own ink drop effect state changes.
  2005. if (action == SHELF_ACTION_APP_LIST_DISMISSED) {
  2006. ink_drop->SnapToActivated();
  2007. ink_drop->AnimateToState(views::InkDropState::HIDDEN);
  2008. } else if (action != SHELF_ACTION_APP_LIST_SHOWN && !dragging()) {
  2009. if (action != SHELF_ACTION_NEW_WINDOW_CREATED && menu_items.size() > 1 &&
  2010. !dragging()) {
  2011. // Show the app menu with 2 or more items, if no window was created. The
  2012. // menu is not shown in case item drag started while the selection request
  2013. // was in progress.
  2014. ShowMenu(std::make_unique<ShelfApplicationMenuModel>(
  2015. item.title, std::move(menu_items),
  2016. model_->GetShelfItemDelegate(item.id)),
  2017. sender, item.id, gfx::Point(), /*context_menu=*/false,
  2018. ui::GetMenuSourceTypeForEvent(*event));
  2019. shelf_->UpdateVisibilityState();
  2020. } else {
  2021. ink_drop->AnimateToState(views::InkDropState::ACTION_TRIGGERED);
  2022. }
  2023. }
  2024. shelf_->shelf_layout_manager()->OnShelfItemSelected(action);
  2025. }
  2026. void ShelfView::ShowShelfContextMenu(
  2027. const ShelfID& shelf_id,
  2028. const gfx::Point& point,
  2029. views::View* source,
  2030. ui::MenuSourceType source_type,
  2031. std::unique_ptr<ui::SimpleMenuModel> model) {
  2032. if (!model) {
  2033. const int64_t display_id = GetDisplayIdForView(this);
  2034. model = std::make_unique<ShelfContextMenuModel>(nullptr, display_id);
  2035. }
  2036. ShowMenu(std::move(model), source, shelf_id, point, /*context_menu=*/true,
  2037. source_type);
  2038. }
  2039. void ShelfView::ShowMenu(std::unique_ptr<ui::SimpleMenuModel> menu_model,
  2040. views::View* source,
  2041. const ShelfID& shelf_id,
  2042. const gfx::Point& click_point,
  2043. bool context_menu,
  2044. ui::MenuSourceType source_type) {
  2045. // Delayed callbacks to show context and application menus may conflict; hide
  2046. // the old menu before showing a new menu in that case.
  2047. if (IsShowingMenu())
  2048. shelf_menu_model_adapter_->Cancel();
  2049. item_awaiting_response_ = ShelfID();
  2050. if (menu_model->GetItemCount() == 0)
  2051. return;
  2052. context_menu_id_ = shelf_id;
  2053. menu_owner_ = source;
  2054. closing_event_time_ = base::TimeTicks();
  2055. // NOTE: If you convert to HAS_MNEMONICS be sure to update menu building code.
  2056. int run_types = views::MenuRunner::USE_ASH_SYS_UI_LAYOUT;
  2057. if (context_menu) {
  2058. run_types |=
  2059. views::MenuRunner::CONTEXT_MENU | views::MenuRunner::FIXED_ANCHOR;
  2060. }
  2061. const ShelfItem* item = ShelfItemForView(source);
  2062. if ((source_type == ui::MenuSourceType::MENU_SOURCE_MOUSE ||
  2063. source_type == ui::MenuSourceType::MENU_SOURCE_KEYBOARD) &&
  2064. item) {
  2065. views::InkDrop::Get(source)->GetInkDrop()->AnimateToState(
  2066. views::InkDropState::ACTIVATED);
  2067. }
  2068. // Only selected shelf items with context menu opened can be dragged.
  2069. if (context_menu && item && ShelfButtonIsInDrag(item->type, source) &&
  2070. source_type == ui::MenuSourceType::MENU_SOURCE_TOUCH) {
  2071. run_types |= views::MenuRunner::SEND_GESTURE_EVENTS_TO_OWNER;
  2072. }
  2073. shelf_menu_model_adapter_ = std::make_unique<ShelfMenuModelAdapter>(
  2074. item ? item->id.app_id : std::string(), std::move(menu_model), source,
  2075. source_type,
  2076. base::BindOnce(&ShelfView::OnMenuClosed, base::Unretained(this), source),
  2077. IsTabletModeEnabled(),
  2078. /*for_application_menu_items*/ !context_menu);
  2079. shelf_menu_model_adapter_->Run(
  2080. GetMenuAnchorRect(*source, click_point, context_menu),
  2081. shelf_->IsHorizontalAlignment()
  2082. ? views::MenuAnchorPosition::kBubbleTopRight
  2083. : views::MenuAnchorPosition::kBubbleLeft,
  2084. run_types);
  2085. if (!context_menu_shown_callback_.is_null())
  2086. context_menu_shown_callback_.Run();
  2087. }
  2088. void ShelfView::OnMenuClosed(views::View* source) {
  2089. menu_owner_ = nullptr;
  2090. context_menu_id_ = ShelfID();
  2091. closing_event_time_ = shelf_menu_model_adapter_->GetClosingEventTime();
  2092. const ShelfItem* item = ShelfItemForView(source);
  2093. if (item)
  2094. static_cast<ShelfAppButton*>(source)->OnMenuClosed();
  2095. shelf_menu_model_adapter_.reset();
  2096. const bool is_in_drag = item && ShelfButtonIsInDrag(item->type, source);
  2097. // Update the shelf visibility since auto-hide or alignment might have
  2098. // changes, but don't update if shelf item is being dragged. Since shelf
  2099. // should be kept as visible during shelf item drag even menu is closed.
  2100. if (!is_in_drag)
  2101. shelf_->UpdateVisibilityState();
  2102. }
  2103. void ShelfView::OnBoundsAnimatorProgressed(views::BoundsAnimator* animator) {
  2104. shelf_->NotifyShelfIconPositionsChanged();
  2105. // Do not call PreferredSizeChanged() so that container does not re-layout
  2106. // during the bounds animation.
  2107. }
  2108. void ShelfView::OnBoundsAnimatorDone(views::BoundsAnimator* animator) {
  2109. shelf_->set_is_tablet_mode_animation_running(false);
  2110. if (move_animation_tracker_) {
  2111. move_animation_tracker_->Stop();
  2112. move_animation_tracker_.reset();
  2113. }
  2114. if (snap_back_from_rip_off_view_ && animator == bounds_animator_.get()) {
  2115. if (!animator->IsAnimating(snap_back_from_rip_off_view_)) {
  2116. // Coming here the animation of the ShelfAppButton is finished and the
  2117. // previously hidden status can be shown again. Since the button itself
  2118. // might have gone away or changed locations we check that the button
  2119. // is still in the shelf and show its status again.
  2120. const auto& entries = view_model_->entries();
  2121. const auto iter = std::find_if(
  2122. entries.begin(), entries.end(), [this](const auto& entry) {
  2123. return entry.view == snap_back_from_rip_off_view_;
  2124. });
  2125. if (iter != entries.end())
  2126. snap_back_from_rip_off_view_->ClearState(ShelfAppButton::STATE_HIDDEN);
  2127. snap_back_from_rip_off_view_ = nullptr;
  2128. }
  2129. }
  2130. }
  2131. bool ShelfView::IsRepostEvent(const ui::Event& event) {
  2132. if (closing_event_time_.is_null())
  2133. return false;
  2134. // If the current (press down) event is a repost event, the time stamp of
  2135. // these two events should be the same.
  2136. return closing_event_time_ == event.time_stamp();
  2137. }
  2138. const ShelfItem* ShelfView::ShelfItemForView(const views::View* view) const {
  2139. const auto view_index = view_model_->GetIndexOfView(view);
  2140. return (!view_index.has_value()) ? nullptr
  2141. : &(model_->items()[view_index.value()]);
  2142. }
  2143. int ShelfView::CalculateShelfDistance(const gfx::Point& coordinate) const {
  2144. const gfx::Rect bounds = GetBoundsInScreen();
  2145. int distance = shelf_->SelectValueForShelfAlignment(
  2146. bounds.y() - coordinate.y(), coordinate.x() - bounds.right(),
  2147. bounds.x() - coordinate.x());
  2148. return distance > 0 ? distance : 0;
  2149. }
  2150. bool ShelfView::CanPrepareForDrag(Pointer pointer,
  2151. const ui::LocatedEvent& event) {
  2152. // Bail if dragging has already begun, or if no item has been pressed.
  2153. if (dragging() || !drag_view_)
  2154. return false;
  2155. // Dragging only begins once the pointer has travelled a minimum distance.
  2156. if ((std::abs(event.x() - drag_origin_.x()) < kMinimumDragDistance) &&
  2157. (std::abs(event.y() - drag_origin_.y()) < kMinimumDragDistance)) {
  2158. return false;
  2159. }
  2160. return true;
  2161. }
  2162. bool ShelfView::ShouldHandleGestures(const ui::GestureEvent& event) const {
  2163. if (event.type() == ui::ET_GESTURE_SCROLL_BEGIN) {
  2164. float x_offset = event.details().scroll_x_hint();
  2165. float y_offset = event.details().scroll_y_hint();
  2166. if (!shelf_->IsHorizontalAlignment())
  2167. std::swap(x_offset, y_offset);
  2168. return std::abs(x_offset) < std::abs(y_offset);
  2169. }
  2170. return true;
  2171. }
  2172. std::u16string ShelfView::GetTitleForChildView(const views::View* view) const {
  2173. const ShelfItem* item = ShelfItemForView(view);
  2174. return item ? item->title : std::u16string();
  2175. }
  2176. void ShelfView::UpdateShelfItemViewsVisibility() {
  2177. visible_views_indices_.clear();
  2178. for (size_t i = 0; i < view_model_->view_size(); ++i) {
  2179. View* view = view_model_->view_at(i);
  2180. // To receive drag event continuously from |drag_view_| during the dragging
  2181. // off from the shelf, don't make |drag_view_| invisible. It will be
  2182. // eventually invisible and removed from the |view_model_| by
  2183. // FinalizeRipOffDrag().
  2184. const bool has_to_show = dragged_off_shelf_ && view == drag_view();
  2185. const bool is_visible = has_to_show || IsItemVisible(model()->items()[i]);
  2186. view->SetVisible(is_visible);
  2187. if (is_visible)
  2188. visible_views_indices_.push_back(i);
  2189. }
  2190. }
  2191. void ShelfView::DestroyScopedDisplay() {
  2192. scoped_display_for_new_windows_.reset();
  2193. }
  2194. int ShelfView::CalculateAppIconsLayoutOffset() const {
  2195. const ScrollableShelfView* scrollable_shelf_view =
  2196. shelf_->hotseat_widget()->scrollable_shelf_view();
  2197. const gfx::Insets& edge_padding_insets =
  2198. scrollable_shelf_view->edge_padding_insets();
  2199. // Note that `edge_padding_insets` fetched from `scrollable_shelf_view` is
  2200. // mirrored under RTL.
  2201. if (scrollable_shelf_view->ShouldAdaptToRTL())
  2202. return edge_padding_insets.right();
  2203. return shelf_->IsHorizontalAlignment() ? edge_padding_insets.left()
  2204. : edge_padding_insets.top();
  2205. }
  2206. gfx::Rect ShelfView::GetChildViewTargetMirroredBounds(
  2207. const views::View* child) const {
  2208. DCHECK_EQ(this, child->parent());
  2209. return GetMirroredRect(bounds_animator_->GetTargetBounds(child));
  2210. }
  2211. void ShelfView::HandleShelfParty() {
  2212. UpdateShelfItemViewsVisibility();
  2213. PreferredSizeChanged();
  2214. AnimateToIdealBounds();
  2215. }
  2216. void ShelfView::RemoveGhostView() {
  2217. if (current_ghost_view_) {
  2218. current_ghost_view_index_ = absl::nullopt;
  2219. current_ghost_view_->FadeOut();
  2220. current_ghost_view_ = nullptr;
  2221. }
  2222. if (last_ghost_view_) {
  2223. last_ghost_view_->FadeOut();
  2224. last_ghost_view_ = nullptr;
  2225. }
  2226. }
  2227. void ShelfView::ResetActiveMenuModelRequest() {
  2228. context_menu_callback_.Cancel();
  2229. item_awaiting_response_ = ShelfID();
  2230. }
  2231. BEGIN_METADATA(ShelfView, views::AccessiblePaneView)
  2232. END_METADATA
  2233. } // namespace ash