apps_container_view.cc 70 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806
  1. // Copyright 2013 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/app_list/views/apps_container_view.h"
  5. #include <algorithm>
  6. #include <memory>
  7. #include <utility>
  8. #include <vector>
  9. #include "ash/app_list/app_list_model_provider.h"
  10. #include "ash/app_list/app_list_util.h"
  11. #include "ash/app_list/views/app_list_a11y_announcer.h"
  12. #include "ash/app_list/views/app_list_folder_view.h"
  13. #include "ash/app_list/views/app_list_item_view.h"
  14. #include "ash/app_list/views/app_list_keyboard_controller.h"
  15. #include "ash/app_list/views/app_list_main_view.h"
  16. #include "ash/app_list/views/app_list_nudge_controller.h"
  17. #include "ash/app_list/views/app_list_toast_container_view.h"
  18. #include "ash/app_list/views/app_list_toast_view.h"
  19. #include "ash/app_list/views/app_list_view.h"
  20. #include "ash/app_list/views/contents_view.h"
  21. #include "ash/app_list/views/continue_section_view.h"
  22. #include "ash/app_list/views/folder_background_view.h"
  23. #include "ash/app_list/views/page_switcher.h"
  24. #include "ash/app_list/views/recent_apps_view.h"
  25. #include "ash/app_list/views/search_box_view.h"
  26. #include "ash/app_list/views/search_result_page_dialog_controller.h"
  27. #include "ash/app_list/views/suggestion_chip_container_view.h"
  28. #include "ash/constants/ash_features.h"
  29. #include "ash/controls/gradient_layer_delegate.h"
  30. #include "ash/keyboard/ui/keyboard_ui_controller.h"
  31. #include "ash/public/cpp/app_list/app_list_config.h"
  32. #include "ash/public/cpp/app_list/app_list_model_delegate.h"
  33. #include "ash/public/cpp/app_list/app_list_switches.h"
  34. #include "ash/public/cpp/shelf_config.h"
  35. #include "ash/public/cpp/style/color_provider.h"
  36. #include "ash/search_box/search_box_constants.h"
  37. #include "ash/strings/grit/ash_strings.h"
  38. #include "base/bind.h"
  39. #include "base/check.h"
  40. #include "base/command_line.h"
  41. #include "base/cxx17_backports.h"
  42. #include "base/metrics/histogram_macros.h"
  43. #include "base/time/time.h"
  44. #include "ui/base/l10n/l10n_util.h"
  45. #include "ui/base/metadata/metadata_impl_macros.h"
  46. #include "ui/color/color_id.h"
  47. #include "ui/compositor/layer.h"
  48. #include "ui/compositor/layer_animation_element.h"
  49. #include "ui/compositor/layer_animator.h"
  50. #include "ui/compositor/scoped_layer_animation_settings.h"
  51. #include "ui/display/display.h"
  52. #include "ui/display/screen.h"
  53. #include "ui/events/event.h"
  54. #include "ui/gfx/geometry/rect_conversions.h"
  55. #include "ui/gfx/geometry/transform.h"
  56. #include "ui/gfx/paint_vector_icon.h"
  57. #include "ui/strings/grit/ui_strings.h"
  58. #include "ui/views/accessibility/view_accessibility.h"
  59. #include "ui/views/animation/animation_builder.h"
  60. #include "ui/views/controls/button/label_button.h"
  61. #include "ui/views/controls/separator.h"
  62. #include "ui/views/controls/textfield/textfield.h"
  63. #include "ui/views/focus/focus_manager.h"
  64. #include "ui/views/layout/flex_layout.h"
  65. #include "ui/views/view_class_properties.h"
  66. #include "ui/views/view_utils.h"
  67. namespace ash {
  68. namespace {
  69. // The number of rows for portrait mode with mode productivity launcher
  70. // enabled.
  71. constexpr int kPreferredGridRowsInPortraitProductivityLauncher = 5;
  72. // The number of columns for portrait mode with productivity launcher enabled.
  73. constexpr int kPreferredGridColumnsInPortraitProductivityLauncher = 5;
  74. // The long apps grid dimension when productivity launcher is not enabled:
  75. // * number of columns in landscape mode
  76. // * number of rows in portrait mode
  77. constexpr int kPreferredGridColumns = 5;
  78. // The short apps grid dimension when productivity launcher is not enabled:
  79. // * number of rows in landscape mode
  80. // * number of columns in portrait mode
  81. constexpr int kPreferredGridRows = 4;
  82. // The range of app list transition progress in which the suggestion chips'
  83. // opacity changes from 0 to 1.
  84. constexpr float kSuggestionChipOpacityStartProgress = 0.66;
  85. constexpr float kSuggestionChipOpacityEndProgress = 1;
  86. // Range of the height of centerline above screen bottom that all apps should
  87. // change opacity. NOTE: this is used to change page switcher's opacity as
  88. // well.
  89. constexpr float kAppsOpacityChangeStart = 8.0f;
  90. constexpr float kAppsOpacityChangeEnd = 144.0f;
  91. // The app list transition progress value for fullscreen state.
  92. constexpr float kAppListFullscreenProgressValue = 2.0;
  93. // The amount by which the apps container UI should be offset downwards when
  94. // shown on non apps page UI.
  95. constexpr int kNonAppsStateVerticalOffset = 24;
  96. // The opacity the apps container UI should have when shown on non apps page UI.
  97. constexpr float kNonAppsStateOpacity = 0.1;
  98. // The ratio of allowed bounds for apps grid view to its maximum margin.
  99. constexpr int kAppsGridMarginRatio = 16;
  100. constexpr int kAppsGridMarginRatioForSmallWidth = 12;
  101. constexpr int kAppsGridMarginRatioForSmallHeight = 24;
  102. // The margins within the apps container for app list folder view.
  103. constexpr int kFolderMargin = 16;
  104. // The suggestion chip container height.
  105. constexpr int kSuggestionChipContainerHeight = 32;
  106. // The suggestion chip container top margin.
  107. constexpr int kSuggestionChipContainerTopMargin = 16;
  108. // The horizontal margin between the apps grid view and the page switcher.
  109. constexpr int kGridToPageSwitcherMargin = 8;
  110. // Minimal horizontal distance from the page switcher to apps container bounds.
  111. constexpr int kPageSwitcherEndMargin = 16;
  112. // The minimum amount of vertical margin between the apps container edges and
  113. // the its contents.
  114. constexpr int kMinimumVerticalContainerMargin = 24;
  115. // The vertical margin above the `AppsGridView`. The space between suggestion
  116. // chips and the app grid. With Productivity launcher, the space between the
  117. // search box and the app grid.
  118. constexpr int kAppGridTopMargin = 24;
  119. // The number of columns available for the ContinueSectionView.
  120. constexpr int kContinueColumnCount = 4;
  121. // The vertical spacing between recent apps and continue section view.
  122. constexpr int kRecentAppsTopMargin = 16;
  123. // The vertical spacing above and below the separator when using kRegular/kDense
  124. // AppListConfigType.
  125. constexpr int kRegularSeparatorVerticalInset = 16;
  126. constexpr int kDenseSeparatorVerticalInset = 8;
  127. // The width of the separator.
  128. constexpr int kSeparatorWidth = 240;
  129. // The actual height of the fadeout gradient mask at the top and bottom of the
  130. // `scrollable_container_`.
  131. constexpr int kDefaultFadeoutMaskHeight = 16;
  132. // Max amount of time to wait for zero state results when refreshing recent apps
  133. // and continue section when launcher becomes visible.
  134. constexpr base::TimeDelta kZeroStateSearchTimeout = base::Milliseconds(16);
  135. } // namespace
  136. // A view that contains continue section, recent apps and a separator view,
  137. // which is shown when any of other views is shown.
  138. // The view is intended to be a wrapper around suggested content views that
  139. // makes applying identical transforms to suggested content views easier.
  140. class AppsContainerView::ContinueContainer : public views::View {
  141. public:
  142. ContinueContainer(AppListKeyboardController* keyboard_controller,
  143. AppListViewDelegate* view_delegate,
  144. views::Separator* separator)
  145. : view_delegate_(view_delegate), separator_(separator) {
  146. DCHECK(view_delegate_);
  147. DCHECK(separator_);
  148. SetPaintToLayer(ui::LAYER_NOT_DRAWN);
  149. SetLayoutManager(std::make_unique<views::FlexLayout>())
  150. ->SetOrientation(views::LayoutOrientation::kVertical);
  151. continue_section_ = AddChildView(std::make_unique<ContinueSectionView>(
  152. view_delegate, kContinueColumnCount,
  153. /*tablet_mode=*/true));
  154. continue_section_->SetPaintToLayer();
  155. continue_section_->layer()->SetFillsBoundsOpaquely(false);
  156. recent_apps_ = AddChildView(
  157. std::make_unique<RecentAppsView>(keyboard_controller, view_delegate));
  158. recent_apps_->SetPaintToLayer();
  159. recent_apps_->layer()->SetFillsBoundsOpaquely(false);
  160. UpdateRecentAppsMargins();
  161. UpdateContinueSectionVisibility();
  162. }
  163. // views::View:
  164. void ChildVisibilityChanged(views::View* child) override {
  165. if (child == recent_apps_ || child == continue_section_)
  166. UpdateSeparatorVisibility();
  167. if (child == continue_section_)
  168. UpdateRecentAppsMargins();
  169. }
  170. bool HasRecentApps() const { return recent_apps_->GetVisible(); }
  171. void UpdateAppListConfig(AppListConfig* config) {
  172. recent_apps_->UpdateAppListConfig(config);
  173. }
  174. void UpdateContinueSectionVisibility() {
  175. // The continue section view and recent apps view manage their own
  176. // visibility internally.
  177. continue_section_->UpdateElementsVisibility();
  178. recent_apps_->UpdateVisibility();
  179. UpdateSeparatorVisibility();
  180. }
  181. // Animates a fade-in for the continue section, recent apps and separator.
  182. void FadeInViews() {
  183. continue_section_->layer()->SetOpacity(0.0f);
  184. recent_apps_->layer()->SetOpacity(0.0f);
  185. separator_->layer()->SetOpacity(0.0f);
  186. views::AnimationBuilder()
  187. .SetPreemptionStrategy(ui::LayerAnimator::PreemptionStrategy::
  188. IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
  189. .Once()
  190. .At(base::Milliseconds(100))
  191. .SetOpacity(continue_section_, 1.0f)
  192. .SetOpacity(recent_apps_, 1.0f)
  193. .SetOpacity(separator_, 1.0f)
  194. .SetDuration(base::Milliseconds(200));
  195. }
  196. ContinueSectionView* continue_section() { return continue_section_; }
  197. RecentAppsView* recent_apps() { return recent_apps_; }
  198. private:
  199. void UpdateRecentAppsMargins() {
  200. // Remove recent apps top margin if continue section is hidden.
  201. recent_apps_->SetProperty(
  202. views::kMarginsKey,
  203. gfx::Insets::TLBR(
  204. continue_section_->GetVisible() ? kRecentAppsTopMargin : 0, 0, 0,
  205. 0));
  206. }
  207. void UpdateSeparatorVisibility() {
  208. separator_->SetVisible(recent_apps_->GetVisible() ||
  209. continue_section_->GetVisible());
  210. }
  211. AppListViewDelegate* const view_delegate_;
  212. ContinueSectionView* continue_section_ = nullptr;
  213. RecentAppsView* recent_apps_ = nullptr;
  214. views::Separator* separator_ = nullptr;
  215. };
  216. AppsContainerView::AppsContainerView(ContentsView* contents_view)
  217. : contents_view_(contents_view),
  218. app_list_keyboard_controller_(
  219. std::make_unique<AppListKeyboardController>(this)),
  220. app_list_nudge_controller_(std::make_unique<AppListNudgeController>()) {
  221. AppListModelProvider::Get()->AddObserver(this);
  222. SetPaintToLayer(ui::LAYER_NOT_DRAWN);
  223. scrollable_container_ = AddChildView(std::make_unique<views::View>());
  224. scrollable_container_->SetPaintToLayer(ui::LAYER_NOT_DRAWN);
  225. AppListViewDelegate* view_delegate =
  226. contents_view_->GetAppListMainView()->view_delegate();
  227. // The bounds of the |scrollable_container_| will visually clip the
  228. // |continue_container_| and |apps_grid_view_| layers.
  229. scrollable_container_->layer()->SetMasksToBounds(true);
  230. AppListA11yAnnouncer* a11y_announcer =
  231. contents_view->app_list_view()->a11y_announcer();
  232. if (features::IsProductivityLauncherEnabled()) {
  233. separator_ = scrollable_container_->AddChildView(
  234. std::make_unique<views::Separator>());
  235. separator_->SetColorId(ui::kColorAshSystemUIMenuSeparator);
  236. separator_->SetPreferredSize(
  237. gfx::Size(kSeparatorWidth, views::Separator::kThickness));
  238. // Initially set the vertical inset to kRegularSeparatorVerticalInset. The
  239. // value will be updated in `AppsContainerView::UpdateAppListConfig()`
  240. separator_->SetProperty(views::kMarginsKey,
  241. gfx::Insets::VH(kRegularSeparatorVerticalInset, 0));
  242. separator_->SetPaintToLayer();
  243. separator_->layer()->SetFillsBoundsOpaquely(false);
  244. // Visibility for `separator_` will be managed by the `continue_container_`.
  245. separator_->SetVisible(false);
  246. dialog_controller_ = std::make_unique<SearchResultPageDialogController>(
  247. contents_view_->GetSearchBoxView());
  248. continue_container_ =
  249. scrollable_container_->AddChildView(std::make_unique<ContinueContainer>(
  250. app_list_keyboard_controller_.get(), view_delegate, separator_));
  251. continue_container_->continue_section()->SetNudgeController(
  252. app_list_nudge_controller_.get());
  253. // Update the suggestion tasks after the app list nudge controller is set in
  254. // continue section.
  255. continue_container_->continue_section()->UpdateSuggestionTasks();
  256. // Add a empty container view. A toast view should be added to
  257. // `toast_container_` when the app list starts temporary sorting.
  258. if (features::IsLauncherAppSortEnabled()) {
  259. toast_container_ = scrollable_container_->AddChildView(
  260. std::make_unique<AppListToastContainerView>(
  261. app_list_nudge_controller_.get(),
  262. app_list_keyboard_controller_.get(), a11y_announcer,
  263. view_delegate,
  264. /*delegate=*/this, /*tablet_mode=*/true));
  265. toast_container_->SetPaintToLayer(ui::LAYER_NOT_DRAWN);
  266. }
  267. } else {
  268. // Add child view at index 0 so focus traversal goes to suggestion chips
  269. // before the views in the scrollable_container.
  270. suggestion_chip_container_view_ = AddChildViewAt(
  271. std::make_unique<SuggestionChipContainerView>(contents_view), 0);
  272. }
  273. apps_grid_view_ =
  274. scrollable_container_->AddChildView(std::make_unique<PagedAppsGridView>(
  275. contents_view, a11y_announcer,
  276. /*folder_delegate=*/nullptr,
  277. /*folder_controller=*/this,
  278. /*container_delegate=*/this, app_list_keyboard_controller_.get()));
  279. apps_grid_view_->Init();
  280. apps_grid_view_->pagination_model()->AddObserver(this);
  281. if (features::IsProductivityLauncherEnabled())
  282. apps_grid_view_->set_margin_for_gradient_mask(kDefaultFadeoutMaskHeight);
  283. // Page switcher should be initialized after AppsGridView.
  284. auto page_switcher = std::make_unique<PageSwitcher>(
  285. apps_grid_view_->pagination_model(), true /* vertical */,
  286. contents_view->app_list_view()->is_tablet_mode());
  287. page_switcher_ = AddChildView(std::move(page_switcher));
  288. auto app_list_folder_view = std::make_unique<AppListFolderView>(
  289. this, apps_grid_view_, contents_view_, a11y_announcer, view_delegate);
  290. folder_background_view_ = AddChildView(
  291. std::make_unique<FolderBackgroundView>(app_list_folder_view.get()));
  292. app_list_folder_view_ = AddChildView(std::move(app_list_folder_view));
  293. // The folder view is initially hidden.
  294. app_list_folder_view_->SetVisible(false);
  295. // NOTE: At this point, the apps grid folder and recent apps grids are not
  296. // fully initialized - they require an `app_list_config_` instance (because
  297. // they contain AppListItemView), which in turn requires widget, and the
  298. // view's contents bounds to be correctly calculated. The initialization
  299. // will be completed in `OnBoundsChanged()` when the apps container bounds are
  300. // first set.
  301. }
  302. AppsContainerView::~AppsContainerView() {
  303. AppListModelProvider::Get()->RemoveObserver(this);
  304. apps_grid_view_->pagination_model()->RemoveObserver(this);
  305. // Make sure |page_switcher_| is deleted before |apps_grid_view_| because
  306. // |page_switcher_| uses the PaginationModel owned by |apps_grid_view_|.
  307. delete page_switcher_;
  308. // App list folder view, if shown, may reference/observe a root apps grid view
  309. // item (associated with the item for which the folder is shown). Delete
  310. // `app_list_folder_view_` explicitly to ensure it's deleted before
  311. // `apps_grid_view_`.
  312. delete app_list_folder_view_;
  313. }
  314. void AppsContainerView::UpdateTopLevelGridDimensions() {
  315. const GridLayout grid_layout = CalculateGridLayout();
  316. apps_grid_view_->SetMaxColumnsAndRows(
  317. /*max_columns=*/grid_layout.columns,
  318. /*max_rows_on_first_page=*/grid_layout.first_page_rows,
  319. /*max_rows=*/grid_layout.rows);
  320. }
  321. gfx::Rect AppsContainerView::CalculateAvailableBoundsForAppsGrid(
  322. const gfx::Rect& contents_bounds) const {
  323. gfx::Rect available_bounds = contents_bounds;
  324. // Reserve horizontal margins to accommodate page switcher.
  325. available_bounds.Inset(
  326. gfx::Insets::VH(0, GetMinHorizontalMarginForAppsGrid()));
  327. // Reserve vertical space for search box and suggestion chips.
  328. available_bounds.Inset(gfx::Insets().set_top(GetMinTopMarginForAppsGrid(
  329. contents_view_->GetSearchBoxSize(AppListState::kStateApps))));
  330. // Remove space for vertical margins at the top and bottom of the apps
  331. // container.
  332. if (features::IsProductivityLauncherEnabled()) {
  333. available_bounds.Inset(gfx::Insets::VH(GetIdealVerticalMargin(), 0));
  334. } else {
  335. available_bounds.Inset(gfx::Insets::VH(kMinimumVerticalContainerMargin, 0));
  336. }
  337. return available_bounds;
  338. }
  339. void AppsContainerView::UpdateAppListConfig(const gfx::Rect& contents_bounds) {
  340. // For productivity launcher, the rows for this grid layout will be ignored
  341. // during creation of a new config.
  342. GridLayout grid_layout = CalculateGridLayout();
  343. const gfx::Rect available_bounds =
  344. CalculateAvailableBoundsForAppsGrid(contents_bounds);
  345. std::unique_ptr<AppListConfig> new_config =
  346. AppListConfigProvider::Get().CreateForFullscreenAppList(
  347. display::Screen::GetScreen()
  348. ->GetDisplayNearestView(GetWidget()->GetNativeView())
  349. .work_area()
  350. .size(),
  351. grid_layout.rows, grid_layout.columns, available_bounds.size(),
  352. app_list_config_.get());
  353. // `CreateForFullscreenAppList()` will create a new config only if it differs
  354. // from the current `app_list_config_`. Nothing to do if the old
  355. // `AppListConfig` can be used for the updated apps container bounds.
  356. if (!new_config)
  357. return;
  358. // Keep old config around until child views have been updated to use the new
  359. // config.
  360. auto old_config = std::move(app_list_config_);
  361. app_list_config_ = std::move(new_config);
  362. // Invalidate the cached container margins - app list config change generally
  363. // changes preferred apps grid margins, which can influence the container
  364. // margins.
  365. cached_container_margins_ = CachedContainerMargins();
  366. if (separator_) {
  367. const int separator_vertical_inset =
  368. app_list_config_->type() == AppListConfigType::kRegular
  369. ? kRegularSeparatorVerticalInset
  370. : kDenseSeparatorVerticalInset;
  371. separator_->SetProperty(views::kMarginsKey,
  372. gfx::Insets::VH(separator_vertical_inset, 0));
  373. }
  374. apps_grid_view()->UpdateAppListConfig(app_list_config_.get());
  375. app_list_folder_view()->UpdateAppListConfig(app_list_config_.get());
  376. if (continue_container_)
  377. continue_container_->UpdateAppListConfig(app_list_config_.get());
  378. }
  379. void AppsContainerView::OnActiveAppListModelsChanged(
  380. AppListModel* model,
  381. SearchModel* search_model) {
  382. // Nothing to do if the apps grid views have not yet been initialized.
  383. if (!app_list_config_)
  384. return;
  385. UpdateForActiveAppListModel();
  386. }
  387. void AppsContainerView::ShowFolderForItemView(AppListItemView* folder_item_view,
  388. bool focus_name_input,
  389. base::OnceClosure hide_callback) {
  390. // Prevent new animations from starting if there are currently animations
  391. // pending. This fixes crbug.com/357099.
  392. if (app_list_folder_view_->IsAnimationRunning())
  393. return;
  394. DCHECK(folder_item_view->is_folder());
  395. UMA_HISTOGRAM_ENUMERATION("Apps.AppListFolderOpened",
  396. kFullscreenAppListFolders, kMaxFolderOpened);
  397. app_list_folder_view_->ConfigureForFolderItemView(folder_item_view,
  398. std::move(hide_callback));
  399. SetShowState(SHOW_ACTIVE_FOLDER, false);
  400. // If there is no selected view in the root grid when a folder is opened,
  401. // silently focus the first item in the folder to avoid showing the selection
  402. // highlight or announcing to A11y, but still ensuring the arrow keys navigate
  403. // from the first item.
  404. if (focus_name_input) {
  405. app_list_folder_view_->FocusNameInput();
  406. } else {
  407. const bool silently = !apps_grid_view()->has_selected_view();
  408. app_list_folder_view_->FocusFirstItem(silently);
  409. }
  410. // Disable all the items behind the folder so that they will not be reached
  411. // during focus traversal.
  412. DisableFocusForShowingActiveFolder(true);
  413. }
  414. void AppsContainerView::ShowApps(AppListItemView* folder_item_view,
  415. bool select_folder) {
  416. DVLOG(1) << __FUNCTION__;
  417. if (app_list_folder_view_->IsAnimationRunning())
  418. return;
  419. const bool animate = !!folder_item_view;
  420. SetShowState(SHOW_APPS, animate);
  421. DisableFocusForShowingActiveFolder(false);
  422. if (folder_item_view) {
  423. // Focus `folder_item_view` but only show the selection highlight if there
  424. // was already one showing.
  425. if (select_folder)
  426. folder_item_view->RequestFocus();
  427. else
  428. folder_item_view->SilentlyRequestFocus();
  429. }
  430. }
  431. void AppsContainerView::ResetForShowApps() {
  432. DVLOG(1) << __FUNCTION__;
  433. UpdateSuggestionChips();
  434. UpdateRecentApps(/*needs_layout=*/false);
  435. SetShowState(SHOW_APPS, false);
  436. DisableFocusForShowingActiveFolder(false);
  437. }
  438. void AppsContainerView::SetDragAndDropHostOfCurrentAppList(
  439. ApplicationDragAndDropHost* drag_and_drop_host) {
  440. apps_grid_view()->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host);
  441. app_list_folder_view()->items_grid_view()->SetDragAndDropHostOfCurrentAppList(
  442. drag_and_drop_host);
  443. }
  444. void AppsContainerView::ReparentFolderItemTransit(
  445. AppListFolderItem* folder_item) {
  446. if (app_list_folder_view_->IsAnimationRunning())
  447. return;
  448. SetShowState(SHOW_ITEM_REPARENT, false);
  449. DisableFocusForShowingActiveFolder(false);
  450. }
  451. bool AppsContainerView::IsInFolderView() const {
  452. return show_state_ == SHOW_ACTIVE_FOLDER;
  453. }
  454. void AppsContainerView::ReparentDragEnded() {
  455. DVLOG(1) << __FUNCTION__;
  456. // The container will be showing apps if the folder was deleted mid-drag.
  457. if (show_state_ == SHOW_APPS)
  458. return;
  459. DCHECK_EQ(SHOW_ITEM_REPARENT, show_state_);
  460. show_state_ = AppsContainerView::SHOW_APPS;
  461. }
  462. void AppsContainerView::OnAppListVisibilityWillChange(bool visible) {
  463. // Start zero state search to refresh contents of the continue section and
  464. // recent apps (which are only shown for productivity launcher).
  465. // NOTE: Request another layout after recent apps get updated to handle the
  466. // case when recent apps get updated during app list state change animation.
  467. // The apps container layout may get dropped by the app list contents view,
  468. // so invalidating recent apps layout when recent apps visibiltiy changes
  469. // will not work well).
  470. // TODO(https://crbug.com/1306613): Remove explicit layout once the linked
  471. // issue is fixed.
  472. if (visible && features::IsProductivityLauncherEnabled()) {
  473. contents_view_->GetAppListMainView()->view_delegate()->StartZeroStateSearch(
  474. base::BindOnce(&AppsContainerView::UpdateRecentApps,
  475. weak_ptr_factory_.GetWeakPtr(),
  476. /*needs_layout=*/true),
  477. kZeroStateSearchTimeout);
  478. }
  479. }
  480. void AppsContainerView::OnAppListVisibilityChanged(bool shown) {
  481. if (toast_container_) {
  482. // Updates the visibility state in toast container.
  483. AppListToastContainerView::VisibilityState state =
  484. shown ? (is_active_page_
  485. ? AppListToastContainerView::VisibilityState::kShown
  486. : AppListToastContainerView::VisibilityState::
  487. kShownInBackground)
  488. : AppListToastContainerView::VisibilityState::kHidden;
  489. toast_container_->UpdateVisibilityState(state);
  490. // Check if the reorder nudge view needs update if the app list is showing.
  491. if (shown)
  492. toast_container_->MaybeUpdateReorderNudgeView();
  493. }
  494. // Layout requests may get ignored by apps container's view hierarchy while
  495. // app list animation is in progress - relayout the container if it needs
  496. // layout at this point.
  497. // TODO(https://crbug.com/1306613): Remove explicit layout once the linked
  498. // issue gets fixed.
  499. if (shown && needs_layout())
  500. Layout();
  501. }
  502. // PaginationModelObserver:
  503. void AppsContainerView::SelectedPageChanged(int old_selected,
  504. int new_selected) {
  505. // There is no |continue_container_| to translate when productivity launcher
  506. // is not enabled, so return early.
  507. if (!features::IsProductivityLauncherEnabled())
  508. return;
  509. // |continue_container_| is hidden above the grid when not on the first page.
  510. gfx::Transform transform;
  511. gfx::Vector2dF translate;
  512. translate.set_y(-scrollable_container_->bounds().height() * new_selected);
  513. transform.Translate(translate);
  514. continue_container_->layer()->SetTransform(transform);
  515. separator_->layer()->SetTransform(transform);
  516. if (toast_container_)
  517. toast_container_->layer()->SetTransform(transform);
  518. }
  519. void AppsContainerView::TransitionChanged() {
  520. // There is no |continue_container_| to translate when productivity launcher
  521. // is not enabled, so return early.
  522. if (!features::IsProductivityLauncherEnabled())
  523. return;
  524. auto* pagination_model = apps_grid_view_->pagination_model();
  525. const PaginationModel::Transition& transition =
  526. pagination_model->transition();
  527. if (!pagination_model->is_valid_page(transition.target_page))
  528. return;
  529. // Because |continue_container_| only shows on the first page, only update its
  530. // transform if its page is involved in the transition. Otherwise, there is
  531. // no need to transform the |continue_container_| because it will remain
  532. // hidden throughout the transition.
  533. if (transition.target_page == 0 || pagination_model->selected_page() == 0) {
  534. const int page_height = scrollable_container_->bounds().height();
  535. gfx::Vector2dF translate;
  536. if (transition.target_page == 0) {
  537. // Scroll the continue section down from above.
  538. translate.set_y(-page_height + page_height * transition.progress);
  539. } else {
  540. // Scroll the continue section upwards
  541. translate.set_y(-page_height * transition.progress);
  542. }
  543. gfx::Transform transform;
  544. transform.Translate(translate);
  545. continue_container_->layer()->SetTransform(transform);
  546. separator_->layer()->SetTransform(transform);
  547. if (toast_container_)
  548. toast_container_->layer()->SetTransform(transform);
  549. }
  550. }
  551. void AppsContainerView::TransitionStarted() {
  552. MaybeCreateGradientMask();
  553. }
  554. void AppsContainerView::TransitionEnded() {
  555. // TODO(crbug.com/1285184): Sometimes gradient mask is not removed because
  556. // this function does not get called in some cases.
  557. // Gradient mask is no longer necessary once transition is finished.
  558. MaybeRemoveGradientMask();
  559. }
  560. void AppsContainerView::ScrollStarted() {
  561. MaybeCreateGradientMask();
  562. }
  563. void AppsContainerView::ScrollEnded() {
  564. // Need to reset the mask because transition will not happen in some
  565. // cases. (See https://crbug.com/1049275)
  566. MaybeRemoveGradientMask();
  567. }
  568. // PagedAppsGridView::ContainerDelegate:
  569. bool AppsContainerView::IsPointWithinPageFlipBuffer(
  570. const gfx::Point& point_in_apps_grid) const {
  571. // The page flip buffer is the work area bounds excluding shelf bounds, which
  572. // is the same as AppsContainerView's bounds.
  573. gfx::Point point = point_in_apps_grid;
  574. ConvertPointToTarget(apps_grid_view_, this, &point);
  575. return this->GetContentsBounds().Contains(point);
  576. }
  577. bool AppsContainerView::IsPointWithinBottomDragBuffer(
  578. const gfx::Point& point,
  579. int page_flip_zone_size) const {
  580. // The bottom drag buffer is between the bottom of apps grid and top of shelf.
  581. gfx::Point point_in_parent = point;
  582. ConvertPointToTarget(apps_grid_view_, this, &point_in_parent);
  583. gfx::Rect parent_rect = this->GetContentsBounds();
  584. const int kBottomDragBufferMax = parent_rect.bottom();
  585. const int kBottomDragBufferMin = scrollable_container_->bounds().bottom() -
  586. apps_grid_view_->GetInsets().bottom() -
  587. page_flip_zone_size;
  588. return point_in_parent.y() > kBottomDragBufferMin &&
  589. point_in_parent.y() < kBottomDragBufferMax;
  590. }
  591. void AppsContainerView::MaybeCreateGradientMask() {
  592. if (features::IsBackgroundBlurEnabled()) {
  593. if (!layer()->layer_mask_layer() && !gradient_layer_delegate_) {
  594. gradient_layer_delegate_ =
  595. std::make_unique<GradientLayerDelegate>(/*animate_in=*/false);
  596. UpdateGradientMaskBounds();
  597. }
  598. if (gradient_layer_delegate_) {
  599. scrollable_container_->layer()->SetMaskLayer(
  600. gradient_layer_delegate_->layer());
  601. }
  602. }
  603. }
  604. void AppsContainerView::MaybeRemoveGradientMask() {
  605. if (scrollable_container_->layer()->layer_mask_layer() &&
  606. !keep_gradient_mask_for_cardified_state_) {
  607. scrollable_container_->layer()->SetMaskLayer(nullptr);
  608. }
  609. }
  610. void AppsContainerView::OnCardifiedStateStarted() {
  611. keep_gradient_mask_for_cardified_state_ = true;
  612. MaybeCreateGradientMask();
  613. }
  614. void AppsContainerView::OnCardifiedStateEnded() {
  615. keep_gradient_mask_for_cardified_state_ = false;
  616. MaybeRemoveGradientMask();
  617. }
  618. void AppsContainerView::OnNudgeRemoved() {
  619. const int continue_container_height =
  620. continue_container_->GetPreferredSize().height();
  621. const int toast_container_height =
  622. toast_container_ ? toast_container_->GetPreferredSize().height() : 0;
  623. apps_grid_view_->ConfigureFirstPagePadding(
  624. continue_container_height + toast_container_height + GetSeparatorHeight(),
  625. continue_container_->HasRecentApps());
  626. UpdateTopLevelGridDimensions();
  627. apps_grid_view_->AnimateOnNudgeRemoved();
  628. }
  629. void AppsContainerView::UpdateForNewSortingOrder(
  630. const absl::optional<AppListSortOrder>& new_order,
  631. bool animate,
  632. base::OnceClosure update_position_closure,
  633. base::OnceClosure animation_done_closure) {
  634. DCHECK(features::IsLauncherAppSortEnabled());
  635. DCHECK_EQ(animate, !update_position_closure.is_null());
  636. DCHECK(!animation_done_closure || animate);
  637. // A11y announcements must happen before animations, otherwise the undo
  638. // guidance is spoken first because focus moves immediately to the undo button
  639. // on the toast.
  640. if (new_order) {
  641. toast_container_->AnnounceSortOrder(*new_order);
  642. } else if (animate) {
  643. toast_container_->AnnounceUndoSort();
  644. }
  645. if (!animate) {
  646. // Reordering is not required so update the undo toast and return early.
  647. app_list_nudge_controller_->OnTemporarySortOrderChanged(new_order);
  648. toast_container_->OnTemporarySortOrderChanged(new_order);
  649. HandleFocusAfterSort();
  650. return;
  651. }
  652. // If app list sort order change is animated, hide any open folders as part of
  653. // animation. If the update is not animated, e.g. when committing sort order,
  654. // keep the folder open to prevent folder closure when apps within the folder
  655. // are reordered, or whe the folder gets renamed.
  656. SetShowState(SHOW_APPS, /*show_apps_with_animation=*/false);
  657. DisableFocusForShowingActiveFolder(false);
  658. // If `apps_grid_view_` is under page transition animation, finish the
  659. // animation before starting the reorder animation.
  660. ash::PaginationModel* pagination_model = apps_grid_view_->pagination_model();
  661. if (pagination_model->has_transition())
  662. pagination_model->FinishAnimation();
  663. // Abort the old reorder animation if any before closure update to avoid data
  664. // races on closures.
  665. apps_grid_view_->MaybeAbortWholeGridAnimation();
  666. DCHECK(!update_position_closure_);
  667. update_position_closure_ = std::move(update_position_closure);
  668. DCHECK(!reorder_animation_done_closure_);
  669. reorder_animation_done_closure_ = std::move(animation_done_closure);
  670. views::AnimationBuilder animation_builder =
  671. apps_grid_view_->FadeOutVisibleItemsForReorder(base::BindRepeating(
  672. &AppsContainerView::OnAppsGridViewFadeOutAnimationEnded,
  673. weak_ptr_factory_.GetWeakPtr(), new_order));
  674. // Configure the toast fade out animation if the toast is going to be hidden.
  675. const bool current_toast_visible = toast_container_->IsToastVisible();
  676. const bool target_toast_visible =
  677. toast_container_->GetVisibilityForSortOrder(new_order);
  678. if (current_toast_visible && !target_toast_visible) {
  679. animation_builder.GetCurrentSequence().SetOpacity(toast_container_->layer(),
  680. 0.f);
  681. }
  682. }
  683. void AppsContainerView::UpdateContinueSectionVisibility() {
  684. if (!continue_container_)
  685. return;
  686. // Get the continue container's height before Layout().
  687. const int initial_height = continue_container_->height();
  688. // Update continue container visibility and bounds.
  689. continue_container_->UpdateContinueSectionVisibility();
  690. Layout();
  691. // Only play animations if the tablet mode app list is visible. This function
  692. // can be called in clamshell mode when the tablet app list is cached.
  693. if (!contents_view_->app_list_view()->is_tablet_mode())
  694. return;
  695. // The change in continue container height is the amount by which the apps
  696. // grid view will be offset.
  697. const int vertical_offset = initial_height - continue_container_->height();
  698. AppListViewDelegate* view_delegate =
  699. contents_view_->GetAppListMainView()->view_delegate();
  700. if (view_delegate->ShouldHideContinueSection()) {
  701. // Continue section is being hidden. Slide each row of app icons up with a
  702. // different offset per row.
  703. apps_grid_view_->SlideVisibleItemsForHideContinueSection(vertical_offset);
  704. // Don't try to fade out the views on hide because they are already
  705. // invisible.
  706. return;
  707. }
  708. // Continue section is being shown. Transform the apps grid view up to its
  709. // original pre-Layout() position.
  710. gfx::Transform transform;
  711. transform.Translate(0, vertical_offset);
  712. apps_grid_view_->SetTransform(transform);
  713. // Animate to the identity transform to slide the apps grid view down to its
  714. // final position.
  715. views::AnimationBuilder()
  716. .SetPreemptionStrategy(ui::LayerAnimator::PreemptionStrategy::
  717. IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
  718. .Once()
  719. .SetTransform(apps_grid_view_, gfx::Transform(),
  720. gfx::Tween::ACCEL_LIN_DECEL_100_3)
  721. .SetDuration(base::Milliseconds(300));
  722. // Fade in the continue tasks and recent apps views.
  723. continue_container_->FadeInViews();
  724. }
  725. ContinueSectionView* AppsContainerView::GetContinueSectionView() {
  726. if (!continue_container_)
  727. return nullptr;
  728. return continue_container_->continue_section();
  729. }
  730. RecentAppsView* AppsContainerView::GetRecentAppsView() {
  731. if (!continue_container_)
  732. return nullptr;
  733. return continue_container_->recent_apps();
  734. }
  735. AppsGridView* AppsContainerView::GetAppsGridView() {
  736. return apps_grid_view_;
  737. }
  738. AppListToastContainerView* AppsContainerView::GetToastContainerView() {
  739. return toast_container_;
  740. }
  741. void AppsContainerView::UpdateControlVisibility(AppListViewState app_list_state,
  742. bool is_in_drag) {
  743. if (app_list_state == AppListViewState::kClosed)
  744. return;
  745. SetCanProcessEventsWithinSubtree(
  746. app_list_state == AppListViewState::kFullscreenAllApps ||
  747. app_list_state == AppListViewState::kPeeking);
  748. apps_grid_view_->UpdateControlVisibility(app_list_state, is_in_drag);
  749. page_switcher_->SetVisible(
  750. is_in_drag || app_list_state == AppListViewState::kFullscreenAllApps ||
  751. app_list_state == AppListViewState::kFullscreenSearch);
  752. // Ignore button press during dragging to avoid app list item views' opacity
  753. // being set to wrong value.
  754. page_switcher_->set_ignore_button_press(is_in_drag);
  755. if (suggestion_chip_container_view_) {
  756. suggestion_chip_container_view_->SetVisible(
  757. app_list_state == AppListViewState::kFullscreenAllApps ||
  758. app_list_state == AppListViewState::kPeeking || is_in_drag);
  759. }
  760. }
  761. void AppsContainerView::AnimateOpacity(float current_progress,
  762. AppListViewState target_view_state,
  763. const OpacityAnimator& animator) {
  764. if (suggestion_chip_container_view_) {
  765. const bool target_suggestion_chip_visibility =
  766. target_view_state == AppListViewState::kFullscreenAllApps ||
  767. target_view_state == AppListViewState::kPeeking;
  768. animator.Run(suggestion_chip_container_view_,
  769. target_suggestion_chip_visibility);
  770. }
  771. if (!apps_grid_view_->layer()->GetAnimator()->IsAnimatingProperty(
  772. ui::LayerAnimationElement::OPACITY)) {
  773. apps_grid_view_->UpdateOpacity(true /*restore_opacity*/,
  774. kAppsOpacityChangeStart,
  775. kAppsOpacityChangeEnd);
  776. apps_grid_view_->layer()->SetOpacity(current_progress > 1.0f ? 1.0f : 0.0f);
  777. }
  778. const bool target_grid_visibility =
  779. target_view_state == AppListViewState::kFullscreenAllApps ||
  780. target_view_state == AppListViewState::kFullscreenSearch;
  781. animator.Run(apps_grid_view_, target_grid_visibility);
  782. animator.Run(page_switcher_, target_grid_visibility);
  783. }
  784. void AppsContainerView::AnimateYPosition(AppListViewState target_view_state,
  785. const TransformAnimator& animator,
  786. float default_offset) {
  787. // Apps container position is calculated for app list progress relative to
  788. // peeking state, which may not match the progress value used to calculate
  789. // |default_offset| - when showing search results page, the transform offset
  790. // is calculated using progress relative to AppListViewState::kHalf.
  791. const float progress =
  792. contents_view_->app_list_view()->GetAppListTransitionProgress(
  793. AppListView::kProgressFlagNone |
  794. AppListView::kProgressFlagWithTransform);
  795. const int current_suggestion_chip_y = GetExpectedSuggestionChipY(progress);
  796. const int target_suggestion_chip_y = GetExpectedSuggestionChipY(
  797. AppListView::GetTransitionProgressForState(target_view_state));
  798. const int offset = current_suggestion_chip_y - target_suggestion_chip_y;
  799. if (suggestion_chip_container_view_) {
  800. suggestion_chip_container_view_->SetY(target_suggestion_chip_y);
  801. animator.Run(offset, suggestion_chip_container_view_->layer());
  802. }
  803. scrollable_container_->SetY(target_suggestion_chip_y + chip_grid_y_distance_);
  804. animator.Run(offset, scrollable_container_->layer());
  805. page_switcher_->SetY(target_suggestion_chip_y + chip_grid_y_distance_);
  806. animator.Run(offset, page_switcher_->layer());
  807. }
  808. void AppsContainerView::OnTabletModeChanged(bool started) {
  809. if (suggestion_chip_container_view_)
  810. suggestion_chip_container_view_->OnTabletModeChanged(started);
  811. apps_grid_view_->OnTabletModeChanged(started);
  812. app_list_folder_view_->OnTabletModeChanged(started);
  813. page_switcher_->set_is_tablet_mode(started);
  814. }
  815. void AppsContainerView::Layout() {
  816. gfx::Rect rect(GetContentsBounds());
  817. if (rect.IsEmpty())
  818. return;
  819. // Layout suggestion chips.
  820. gfx::Rect chip_container_rect = rect;
  821. chip_container_rect.set_y(GetExpectedSuggestionChipY(
  822. contents_view_->app_list_view()->GetAppListTransitionProgress(
  823. AppListView::kProgressFlagNone)));
  824. if (suggestion_chip_container_view_) {
  825. chip_container_rect.set_height(kSuggestionChipContainerHeight);
  826. chip_container_rect.Inset(gfx::Insets::VH(0, GetIdealHorizontalMargin()));
  827. suggestion_chip_container_view_->SetBoundsRect(chip_container_rect);
  828. } else {
  829. chip_container_rect.set_height(0);
  830. }
  831. // Set bounding box for the folder view - the folder may overlap with
  832. // suggestion chips, but not the search box.
  833. gfx::Rect folder_bounding_box = rect;
  834. int top_folder_inset = chip_container_rect.y();
  835. int bottom_folder_inset = kFolderMargin;
  836. if (features::IsProductivityLauncherEnabled())
  837. top_folder_inset += kFolderMargin;
  838. // Account for the hotseat which overlaps with contents bounds in tablet mode.
  839. if (contents_view_->app_list_view()->is_tablet_mode())
  840. bottom_folder_inset += ShelfConfig::Get()->hotseat_bottom_padding();
  841. folder_bounding_box.Inset(gfx::Insets::TLBR(
  842. top_folder_inset, kFolderMargin, bottom_folder_inset, kFolderMargin));
  843. app_list_folder_view_->SetBoundingBox(folder_bounding_box);
  844. // Leave the same available bounds for the apps grid view in both
  845. // fullscreen and peeking state to avoid resizing the view during
  846. // animation and dragging, which is an expensive operation.
  847. rect.set_y(chip_container_rect.bottom());
  848. rect.set_height(rect.height() -
  849. GetExpectedSuggestionChipY(kAppListFullscreenProgressValue) -
  850. chip_container_rect.height());
  851. // Layout apps grid.
  852. const gfx::Insets grid_insets = apps_grid_view_->GetInsets();
  853. const gfx::Insets margins = CalculateMarginsForAvailableBounds(
  854. GetContentsBounds(),
  855. contents_view_->GetSearchBoxSize(AppListState::kStateApps));
  856. gfx::Rect grid_rect = rect;
  857. grid_rect.Inset(gfx::Insets::TLBR(kAppGridTopMargin, margins.left(),
  858. margins.bottom(), margins.right()));
  859. // The grid rect insets are added to calculated margins. Given that the
  860. // grid bounds rect should include insets, they have to be removed from
  861. // added margins.
  862. grid_rect.Inset(-grid_insets);
  863. gfx::Rect scrollable_bounds = grid_rect;
  864. // With productivity launcher enabled, add space to the top of the
  865. // `scrollable_container_` bounds to make room for the gradient mask to be
  866. // placed above the continue section.
  867. if (features::IsProductivityLauncherEnabled())
  868. scrollable_bounds.Inset(
  869. gfx::Insets::TLBR(-kDefaultFadeoutMaskHeight, 0, 0, 0));
  870. scrollable_container_->SetBoundsRect(scrollable_bounds);
  871. if (gradient_layer_delegate_)
  872. UpdateGradientMaskBounds();
  873. bool separator_need_centering = false;
  874. bool first_page_config_changed = false;
  875. if (features::IsProductivityLauncherEnabled()) {
  876. const int continue_container_height =
  877. continue_container_->GetPreferredSize().height();
  878. continue_container_->SetBoundsRect(gfx::Rect(0, kDefaultFadeoutMaskHeight,
  879. grid_rect.width(),
  880. continue_container_height));
  881. const int toast_container_height =
  882. toast_container_ ? toast_container_->GetPreferredSize().height() : 0;
  883. if (toast_container_) {
  884. toast_container_->SetBoundsRect(gfx::Rect(
  885. 0, continue_container_->bounds().bottom() + GetSeparatorHeight(),
  886. grid_rect.width(), toast_container_height));
  887. }
  888. // When no views are shown between the recent apps and the apps grid,
  889. // vertically center the separator between them.
  890. if (toast_container_height == 0 && continue_container_->HasRecentApps())
  891. separator_need_centering = true;
  892. // Setting this offset prevents the app items in the grid from overlapping
  893. // with the continue section.
  894. first_page_config_changed = apps_grid_view_->ConfigureFirstPagePadding(
  895. continue_container_height + toast_container_height +
  896. GetSeparatorHeight(),
  897. continue_container_->HasRecentApps());
  898. }
  899. // Make sure that UpdateTopLevelGridDimensions() happens after setting the
  900. // apps grid's first page offset, because it can change the number of rows
  901. // shown in the grid.
  902. UpdateTopLevelGridDimensions();
  903. gfx::Rect apps_grid_bounds(grid_rect.size());
  904. // Set the apps grid bounds y to make room for the top gradient mask.
  905. if (features::IsProductivityLauncherEnabled())
  906. apps_grid_bounds.set_y(kDefaultFadeoutMaskHeight);
  907. if (apps_grid_view_->bounds() != apps_grid_bounds) {
  908. apps_grid_view_->SetBoundsRect(apps_grid_bounds);
  909. } else if (first_page_config_changed) {
  910. // Apps grid layout depends on the continue container bounds, so explicitly
  911. // call layout to ensure apps grid view gets laid out even if its bounds do
  912. // not change.
  913. apps_grid_view_->Layout();
  914. }
  915. if (separator_) {
  916. if (separator_need_centering) {
  917. // Center the separator between the recent apps and the first row of the
  918. // apps grid. This is done after the apps grid layout so the correct
  919. // tile padding is used.
  920. const int centering_offset =
  921. continue_container_->bounds().bottom() +
  922. apps_grid_view_->GetUnscaledFirstPageTilePadding() +
  923. GetSeparatorHeight() / 2;
  924. separator_->SetBoundsRect(
  925. gfx::Rect(gfx::Point((grid_rect.width() - kSeparatorWidth) / 2,
  926. centering_offset),
  927. gfx::Size(kSeparatorWidth, 1)));
  928. } else {
  929. separator_->SetBoundsRect(gfx::Rect(
  930. (grid_rect.width() - kSeparatorWidth) / 2,
  931. continue_container_->bounds().bottom() +
  932. separator_->GetProperty(views::kMarginsKey)->height() / 2,
  933. kSeparatorWidth, 1));
  934. }
  935. }
  936. // Record the distance of y position between suggestion chip container
  937. // and apps grid view to avoid duplicate calculation of apps grid view's
  938. // y position during dragging.
  939. chip_grid_y_distance_ = scrollable_container_->y() - chip_container_rect.y();
  940. // Layout page switcher.
  941. const int page_switcher_width = page_switcher_->GetPreferredSize().width();
  942. const gfx::Rect page_switcher_bounds(
  943. grid_rect.right() + kGridToPageSwitcherMargin, grid_rect.y(),
  944. page_switcher_width, grid_rect.height());
  945. page_switcher_->SetBoundsRect(page_switcher_bounds);
  946. switch (show_state_) {
  947. case SHOW_APPS:
  948. break;
  949. case SHOW_ACTIVE_FOLDER: {
  950. app_list_folder_view_->UpdatePreferredBounds();
  951. folder_background_view_->SetBoundsRect(rect);
  952. app_list_folder_view_->SetBoundsRect(
  953. app_list_folder_view_->preferred_bounds());
  954. break;
  955. }
  956. case SHOW_ITEM_REPARENT:
  957. case SHOW_NONE:
  958. break;
  959. }
  960. }
  961. bool AppsContainerView::OnKeyPressed(const ui::KeyEvent& event) {
  962. if (show_state_ == SHOW_APPS)
  963. return apps_grid_view_->OnKeyPressed(event);
  964. else
  965. return app_list_folder_view_->OnKeyPressed(event);
  966. }
  967. const char* AppsContainerView::GetClassName() const {
  968. return "AppsContainerView";
  969. }
  970. void AppsContainerView::OnBoundsChanged(const gfx::Rect& old_bounds) {
  971. const bool creating_initial_config = !app_list_config_;
  972. // The size and layout of apps grid items depend on the dimensions of the
  973. // display on which the apps container is shown. Given that the apps container
  974. // is shown in fullscreen app list view (and covers complete app list view
  975. // bounds), changes in the `AppsContainerView` bounds can be used as a proxy
  976. // to detect display size changes.
  977. UpdateAppListConfig(GetContentsBounds());
  978. DCHECK(app_list_config_);
  979. UpdateTopLevelGridDimensions();
  980. // Finish initialization of views that require app list config.
  981. if (creating_initial_config)
  982. UpdateForActiveAppListModel();
  983. }
  984. void AppsContainerView::AddedToWidget() {
  985. GetFocusManager()->AddFocusChangeListener(this);
  986. }
  987. void AppsContainerView::RemovedFromWidget() {
  988. GetFocusManager()->RemoveFocusChangeListener(this);
  989. }
  990. void AppsContainerView::OnDidChangeFocus(View* focused_before,
  991. View* focused_now) {
  992. // Ensure that `continue_container_` is visible (the first page is active)
  993. // after moving focus down from the last row on 2nd+ page to the search box
  994. // and then to `continue_container_`.
  995. if (!is_active_page_)
  996. return;
  997. if (!continue_container_ || !continue_container_->Contains(focused_now))
  998. return;
  999. if (apps_grid_view_->pagination_model()->selected_page() != 0)
  1000. apps_grid_view_->pagination_model()->SelectPage(0, /*animate=*/false);
  1001. }
  1002. void AppsContainerView::OnGestureEvent(ui::GestureEvent* event) {
  1003. // Ignore tap/long-press, allow those to pass to the ancestor view.
  1004. if (event->type() == ui::ET_GESTURE_TAP ||
  1005. event->type() == ui::ET_GESTURE_LONG_PRESS) {
  1006. return;
  1007. }
  1008. // Will forward events to |apps_grid_view_| if they occur in the same y-region
  1009. if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN &&
  1010. event->location().y() <= apps_grid_view_->bounds().y()) {
  1011. return;
  1012. }
  1013. // If a folder is currently opening or closing, we should ignore the event.
  1014. // This is here until the animation for pagination while closing folders is
  1015. // fixed: https://crbug.com/875133
  1016. if (app_list_folder_view_->IsAnimationRunning()) {
  1017. event->SetHandled();
  1018. return;
  1019. }
  1020. // Temporary event for use by |apps_grid_view_|
  1021. ui::GestureEvent grid_event(*event);
  1022. ConvertEventToTarget(apps_grid_view_, &grid_event);
  1023. apps_grid_view_->OnGestureEvent(&grid_event);
  1024. // If the temporary event was handled, we don't want to handle it again.
  1025. if (grid_event.handled())
  1026. event->SetHandled();
  1027. }
  1028. void AppsContainerView::OnShown() {
  1029. DVLOG(1) << __FUNCTION__;
  1030. // Explicitly hide the virtual keyboard before showing the apps container
  1031. // view. This prevents the virtual keyboard's "transient blur" feature from
  1032. // kicking in - if a text input loses focus, and a text input gains it within
  1033. // seconds, the virtual keyboard gets reshown. This is undesirable behavior
  1034. // for the app list (where search box gets focus by default).
  1035. if (keyboard::KeyboardUIController::HasInstance())
  1036. keyboard::KeyboardUIController::Get()->HideKeyboardExplicitlyBySystem();
  1037. GetViewAccessibility().OverrideIsLeaf(false);
  1038. is_active_page_ = true;
  1039. // Update the continue section.
  1040. if (continue_container_)
  1041. continue_container_->continue_section()->SetShownInBackground(false);
  1042. // Updates the visibility state in toast container.
  1043. if (toast_container_) {
  1044. toast_container_->UpdateVisibilityState(
  1045. AppListToastContainerView::VisibilityState::kShown);
  1046. }
  1047. if (dialog_controller_)
  1048. dialog_controller_->Reset(/*enabled=*/true);
  1049. }
  1050. void AppsContainerView::OnWillBeHidden() {
  1051. DVLOG(1) << __FUNCTION__;
  1052. if (show_state_ == SHOW_ACTIVE_FOLDER)
  1053. app_list_folder_view_->CloseFolderPage();
  1054. else
  1055. apps_grid_view_->CancelDragWithNoDropAnimation();
  1056. }
  1057. void AppsContainerView::OnHidden() {
  1058. // Apps container view is shown faded behind the search results UI - hide its
  1059. // contents from the screen reader as the apps grid is not normally
  1060. // actionable in this state.
  1061. GetViewAccessibility().OverrideIsLeaf(true);
  1062. is_active_page_ = false;
  1063. // Update the continue section.
  1064. if (continue_container_)
  1065. continue_container_->continue_section()->SetShownInBackground(true);
  1066. // Updates the visibility state in toast container.
  1067. if (toast_container_) {
  1068. toast_container_->UpdateVisibilityState(
  1069. AppListToastContainerView::VisibilityState::kShownInBackground);
  1070. }
  1071. if (dialog_controller_)
  1072. dialog_controller_->Reset(/*enabled=*/false);
  1073. }
  1074. void AppsContainerView::OnAnimationStarted(AppListState from_state,
  1075. AppListState to_state) {
  1076. gfx::Rect contents_bounds = GetDefaultContentsBounds();
  1077. const gfx::Rect from_rect =
  1078. GetPageBoundsForState(from_state, contents_bounds, gfx::Rect());
  1079. const gfx::Rect to_rect =
  1080. GetPageBoundsForState(to_state, contents_bounds, gfx::Rect());
  1081. if (from_rect != to_rect) {
  1082. DCHECK_EQ(from_rect.size(), to_rect.size());
  1083. DCHECK_EQ(from_rect.x(), to_rect.x());
  1084. SetBoundsRect(to_rect);
  1085. gfx::Transform initial_transform;
  1086. initial_transform.Translate(0, from_rect.y() - to_rect.y());
  1087. layer()->SetTransform(initial_transform);
  1088. auto settings = contents_view_->CreateTransitionAnimationSettings(layer());
  1089. layer()->SetTransform(gfx::Transform());
  1090. }
  1091. // Set the page opacity.
  1092. auto settings = contents_view_->CreateTransitionAnimationSettings(layer());
  1093. UpdateContainerOpacityForState(to_state);
  1094. }
  1095. void AppsContainerView::UpdatePageOpacityForState(AppListState state,
  1096. float search_box_opacity,
  1097. bool restore_opacity) {
  1098. UpdateContainerOpacityForState(state);
  1099. const float progress =
  1100. contents_view_->app_list_view()->GetAppListTransitionProgress(
  1101. AppListView::kProgressFlagNone);
  1102. UpdateContentsOpacity(progress, restore_opacity);
  1103. }
  1104. void AppsContainerView::UpdatePageBoundsForState(
  1105. AppListState state,
  1106. const gfx::Rect& contents_bounds,
  1107. const gfx::Rect& search_box_bounds) {
  1108. AppListPage::UpdatePageBoundsForState(state, contents_bounds,
  1109. search_box_bounds);
  1110. const float progress =
  1111. contents_view_->app_list_view()->GetAppListTransitionProgress(
  1112. AppListView::kProgressFlagNone);
  1113. UpdateContentsYPosition(progress);
  1114. }
  1115. gfx::Rect AppsContainerView::GetPageBoundsForState(
  1116. AppListState state,
  1117. const gfx::Rect& contents_bounds,
  1118. const gfx::Rect& search_box_bounds) const {
  1119. if (state == AppListState::kStateApps)
  1120. return contents_bounds;
  1121. gfx::Rect bounds = contents_bounds;
  1122. bounds.Offset(0, kNonAppsStateVerticalOffset);
  1123. return bounds;
  1124. }
  1125. int AppsContainerView::GetMinHorizontalMarginForAppsGrid() const {
  1126. return kPageSwitcherEndMargin + kGridToPageSwitcherMargin +
  1127. page_switcher_->GetPreferredSize().width();
  1128. }
  1129. int AppsContainerView::GetMinTopMarginForAppsGrid(
  1130. const gfx::Size& search_box_size) const {
  1131. const int suggestion_chip_container_size =
  1132. features::IsProductivityLauncherEnabled()
  1133. ? 0
  1134. : kSuggestionChipContainerHeight + kSuggestionChipContainerTopMargin;
  1135. return search_box_size.height() + kAppGridTopMargin +
  1136. suggestion_chip_container_size;
  1137. }
  1138. int AppsContainerView::GetIdealHorizontalMargin() const {
  1139. if (features::IsProductivityLauncherEnabled())
  1140. return 24;
  1141. const int available_width = GetContentsBounds().width();
  1142. if (available_width >=
  1143. kAppsGridMarginRatio * GetMinHorizontalMarginForAppsGrid()) {
  1144. return available_width / kAppsGridMarginRatio;
  1145. }
  1146. return available_width / kAppsGridMarginRatioForSmallWidth;
  1147. }
  1148. int AppsContainerView::GetIdealVerticalMargin() const {
  1149. if (!features::IsProductivityLauncherEnabled())
  1150. return GetContentsBounds().height() / kAppsGridMarginRatio;
  1151. const int screen_height =
  1152. display::Screen::GetScreen()
  1153. ->GetDisplayNearestView(GetWidget()->GetNativeView())
  1154. .bounds()
  1155. .height();
  1156. const float margin_ratio = (screen_height <= 800)
  1157. ? kAppsGridMarginRatioForSmallHeight
  1158. : kAppsGridMarginRatio;
  1159. return std::max(kMinimumVerticalContainerMargin,
  1160. static_cast<int>(screen_height / margin_ratio));
  1161. }
  1162. const gfx::Insets& AppsContainerView::CalculateMarginsForAvailableBounds(
  1163. const gfx::Rect& available_bounds,
  1164. const gfx::Size& search_box_size) {
  1165. if (cached_container_margins_.bounds_size == available_bounds.size() &&
  1166. cached_container_margins_.search_box_size == search_box_size) {
  1167. return cached_container_margins_.margins;
  1168. }
  1169. // For productivity launcher, the `grid_layout`'s rows will be ignored because
  1170. // the vertical margin will be constant.
  1171. const GridLayout grid_layout = CalculateGridLayout();
  1172. const gfx::Size min_grid_size = apps_grid_view()->GetMinimumTileGridSize(
  1173. grid_layout.columns, grid_layout.rows);
  1174. const gfx::Size max_grid_size = apps_grid_view()->GetMaximumTileGridSize(
  1175. grid_layout.columns, grid_layout.rows);
  1176. int available_height = available_bounds.height();
  1177. // Add search box, and suggestion chips container height (with its margins to
  1178. // search box and apps grid) to non apps grid size.
  1179. // NOTE: Not removing bottom apps grid inset because they are included into
  1180. // the total margin values.
  1181. available_height -= GetMinTopMarginForAppsGrid(search_box_size);
  1182. // Calculates margin value to ensure the apps grid size is within required
  1183. // bounds.
  1184. // |ideal_margin|: The value the margin would have with no restrictions on
  1185. // grid size.
  1186. // |available_size|: The available size for apps grid in the dimension where
  1187. // margin is applied.
  1188. // |min_size|: The min allowed size for apps grid in the dimension where
  1189. // margin is applied.
  1190. // |max_size|: The max allowed size for apps grid in the dimension where
  1191. // margin is applied.
  1192. const auto calculate_margin = [](int ideal_margin, int available_size,
  1193. int min_size, int max_size) -> int {
  1194. const int ideal_size = available_size - 2 * ideal_margin;
  1195. if (ideal_size < min_size)
  1196. return ideal_margin - (min_size - ideal_size + 1) / 2;
  1197. if (ideal_size > max_size)
  1198. return ideal_margin + (ideal_size - max_size) / 2;
  1199. return ideal_margin;
  1200. };
  1201. int vertical_margin = 0;
  1202. if (features::IsProductivityLauncherEnabled()) {
  1203. // Productivity launcher does not have a preset number of rows per page.
  1204. // Instead of adjusting the margins to fit a set number of rows, the grid
  1205. // will change the number of rows to fit within the provided space.
  1206. vertical_margin = GetIdealVerticalMargin();
  1207. } else {
  1208. vertical_margin =
  1209. calculate_margin(GetIdealVerticalMargin(), available_height,
  1210. min_grid_size.height(), max_grid_size.height());
  1211. }
  1212. const int horizontal_margin =
  1213. calculate_margin(GetIdealHorizontalMargin(), available_bounds.width(),
  1214. min_grid_size.width(), max_grid_size.width());
  1215. const int min_horizontal_margin = GetMinHorizontalMarginForAppsGrid();
  1216. cached_container_margins_.margins = gfx::Insets::TLBR(
  1217. std::max(vertical_margin, kMinimumVerticalContainerMargin),
  1218. std::max(horizontal_margin, min_horizontal_margin),
  1219. std::max(vertical_margin, kMinimumVerticalContainerMargin),
  1220. std::max(horizontal_margin, min_horizontal_margin));
  1221. cached_container_margins_.bounds_size = available_bounds.size();
  1222. cached_container_margins_.search_box_size = search_box_size;
  1223. return cached_container_margins_.margins;
  1224. }
  1225. void AppsContainerView::UpdateRecentApps(bool needs_layout) {
  1226. RecentAppsView* recent_apps = GetRecentAppsView();
  1227. if (!recent_apps || !app_list_config_)
  1228. return;
  1229. AppListModelProvider* const model_provider = AppListModelProvider::Get();
  1230. recent_apps->SetModels(model_provider->search_model(),
  1231. model_provider->model());
  1232. if (needs_layout)
  1233. Layout();
  1234. }
  1235. void AppsContainerView::UpdateSuggestionChips() {
  1236. if (!suggestion_chip_container_view_)
  1237. return;
  1238. suggestion_chip_container_view_->SetResults(
  1239. AppListModelProvider::Get()->search_model()->results());
  1240. }
  1241. base::ScopedClosureRunner AppsContainerView::DisableSuggestionChipsBlur() {
  1242. if (!suggestion_chip_container_view_)
  1243. return base::ScopedClosureRunner(base::DoNothing());
  1244. ++suggestion_chips_blur_disabler_count_;
  1245. if (suggestion_chips_blur_disabler_count_ == 1)
  1246. suggestion_chip_container_view_->SetBlurDisabled(true);
  1247. return base::ScopedClosureRunner(
  1248. base::BindOnce(&AppsContainerView::OnSuggestionChipsBlurDisablerReleased,
  1249. weak_ptr_factory_.GetWeakPtr()));
  1250. }
  1251. void AppsContainerView::SetShowState(ShowState show_state,
  1252. bool show_apps_with_animation) {
  1253. if (show_state_ == show_state)
  1254. return;
  1255. show_state_ = show_state;
  1256. // Layout before showing animation because the animation's target bounds are
  1257. // calculated based on the layout.
  1258. Layout();
  1259. switch (show_state_) {
  1260. case SHOW_APPS:
  1261. page_switcher_->SetCanProcessEventsWithinSubtree(true);
  1262. folder_background_view_->SetVisible(false);
  1263. apps_grid_view_->ResetForShowApps();
  1264. app_list_folder_view_->ResetItemsGridForClose();
  1265. if (show_apps_with_animation) {
  1266. app_list_folder_view_->ScheduleShowHideAnimation(false, false);
  1267. } else {
  1268. app_list_folder_view_->HideViewImmediately();
  1269. }
  1270. break;
  1271. case SHOW_ACTIVE_FOLDER:
  1272. page_switcher_->SetCanProcessEventsWithinSubtree(false);
  1273. folder_background_view_->SetVisible(true);
  1274. app_list_folder_view_->ScheduleShowHideAnimation(true, false);
  1275. break;
  1276. case SHOW_ITEM_REPARENT:
  1277. page_switcher_->SetCanProcessEventsWithinSubtree(true);
  1278. folder_background_view_->SetVisible(false);
  1279. app_list_folder_view_->ScheduleShowHideAnimation(false, true);
  1280. break;
  1281. default:
  1282. NOTREACHED();
  1283. }
  1284. }
  1285. void AppsContainerView::UpdateContainerOpacityForState(AppListState state) {
  1286. const float target_opacity =
  1287. state == AppListState::kStateApps ? 1.0f : kNonAppsStateOpacity;
  1288. if (layer()->GetTargetOpacity() != target_opacity)
  1289. layer()->SetOpacity(target_opacity);
  1290. }
  1291. void AppsContainerView::UpdateContentsOpacity(float progress,
  1292. bool restore_opacity) {
  1293. apps_grid_view_->UpdateOpacity(restore_opacity, kAppsOpacityChangeStart,
  1294. kAppsOpacityChangeEnd);
  1295. // Updates the opacity of page switcher buttons. The same rule as all apps in
  1296. // AppsGridView.
  1297. AppListView* app_list_view = contents_view_->app_list_view();
  1298. int screen_bottom = app_list_view->GetScreenBottom();
  1299. gfx::Rect switcher_bounds = page_switcher_->GetBoundsInScreen();
  1300. float centerline_above_work_area =
  1301. std::max<float>(screen_bottom - switcher_bounds.CenterPoint().y(), 0.f);
  1302. float opacity =
  1303. std::min(std::max((centerline_above_work_area - kAppsOpacityChangeStart) /
  1304. (kAppsOpacityChangeEnd - kAppsOpacityChangeStart),
  1305. 0.f),
  1306. 1.0f);
  1307. page_switcher_->layer()->SetOpacity(restore_opacity ? 1.0f : opacity);
  1308. if (suggestion_chip_container_view_) {
  1309. // Changes the opacity of suggestion chips between 0 and 1 when app list
  1310. // transition progress changes between |kSuggestionChipOpacityStartProgress|
  1311. // and |kSuggestionChipOpacityEndProgress|.
  1312. float chips_opacity =
  1313. base::clamp((progress - kSuggestionChipOpacityStartProgress) /
  1314. (kSuggestionChipOpacityEndProgress -
  1315. kSuggestionChipOpacityStartProgress),
  1316. 0.0f, 1.0f);
  1317. suggestion_chip_container_view_->layer()->SetOpacity(
  1318. restore_opacity ? 1.0 : chips_opacity);
  1319. }
  1320. }
  1321. void AppsContainerView::UpdateContentsYPosition(float progress) {
  1322. const int current_suggestion_chip_y = GetExpectedSuggestionChipY(progress);
  1323. if (suggestion_chip_container_view_)
  1324. suggestion_chip_container_view_->SetY(current_suggestion_chip_y);
  1325. scrollable_container_->SetY(current_suggestion_chip_y +
  1326. chip_grid_y_distance_);
  1327. page_switcher_->SetY(current_suggestion_chip_y + chip_grid_y_distance_);
  1328. // If app list is in drag, reset transforms that might started animating in
  1329. // AnimateYPosition().
  1330. if (contents_view_->app_list_view()->is_in_drag()) {
  1331. if (suggestion_chip_container_view_)
  1332. suggestion_chip_container_view_->layer()->SetTransform(gfx::Transform());
  1333. scrollable_container_->layer()->SetTransform(gfx::Transform());
  1334. page_switcher_->layer()->SetTransform(gfx::Transform());
  1335. }
  1336. }
  1337. void AppsContainerView::DisableFocusForShowingActiveFolder(bool disabled) {
  1338. if (suggestion_chip_container_view_) {
  1339. suggestion_chip_container_view_->DisableFocusForShowingActiveFolder(
  1340. disabled);
  1341. }
  1342. if (auto* recent_apps = GetRecentAppsView(); recent_apps) {
  1343. recent_apps->DisableFocusForShowingActiveFolder(disabled);
  1344. }
  1345. if (auto* continue_section = GetContinueSectionView(); continue_section) {
  1346. continue_section->DisableFocusForShowingActiveFolder(disabled);
  1347. }
  1348. if (toast_container_) {
  1349. toast_container_->DisableFocusForShowingActiveFolder(disabled);
  1350. }
  1351. apps_grid_view_->DisableFocusForShowingActiveFolder(disabled);
  1352. // Ignore the page switcher in accessibility tree so that buttons inside it
  1353. // will not be accessed by ChromeVox.
  1354. SetViewIgnoredForAccessibility(page_switcher_, disabled);
  1355. }
  1356. int AppsContainerView::GetExpectedSuggestionChipY(float progress) {
  1357. const gfx::Rect search_box_bounds =
  1358. contents_view_->GetSearchBoxExpectedBoundsForProgress(
  1359. AppListState::kStateApps, progress);
  1360. if (!suggestion_chip_container_view_)
  1361. return search_box_bounds.bottom();
  1362. return search_box_bounds.bottom() + kSuggestionChipContainerTopMargin;
  1363. }
  1364. AppsContainerView::GridLayout AppsContainerView::CalculateGridLayout() const {
  1365. DCHECK(GetWidget());
  1366. // Adapt columns and rows based on the display/root window size.
  1367. const gfx::Size size =
  1368. display::Screen::GetScreen()
  1369. ->GetDisplayNearestView(GetWidget()->GetNativeView())
  1370. .work_area()
  1371. .size();
  1372. const bool is_portrait_mode = size.height() > size.width();
  1373. const int available_height =
  1374. CalculateAvailableBoundsForAppsGrid(GetContentsBounds()).height();
  1375. int preferred_columns = 0;
  1376. int preferred_rows = 0;
  1377. int preferred_rows_first_page = 0;
  1378. if (is_portrait_mode) {
  1379. preferred_rows = features::IsProductivityLauncherEnabled()
  1380. ? kPreferredGridRowsInPortraitProductivityLauncher
  1381. : kPreferredGridColumns;
  1382. preferred_rows_first_page = preferred_rows;
  1383. preferred_columns =
  1384. features::IsProductivityLauncherEnabled()
  1385. ? kPreferredGridColumnsInPortraitProductivityLauncher
  1386. : kPreferredGridRows;
  1387. } else {
  1388. preferred_rows = kPreferredGridRows;
  1389. preferred_rows_first_page = preferred_rows;
  1390. // In landscape mode, the first page should show the preferred number of
  1391. // rows as well as an additional row for recent apps when possible.
  1392. if (continue_container_ && continue_container_->HasRecentApps())
  1393. preferred_rows_first_page++;
  1394. preferred_columns = kPreferredGridColumns;
  1395. }
  1396. GridLayout result;
  1397. result.columns = preferred_columns;
  1398. result.rows =
  1399. apps_grid_view_->CalculateMaxRows(available_height, preferred_rows);
  1400. result.first_page_rows = apps_grid_view_->CalculateFirstPageMaxRows(
  1401. available_height, preferred_rows_first_page);
  1402. return result;
  1403. }
  1404. void AppsContainerView::UpdateForActiveAppListModel() {
  1405. AppListModel* const model = AppListModelProvider::Get()->model();
  1406. apps_grid_view_->SetModel(model);
  1407. apps_grid_view_->SetItemList(model->top_level_item_list());
  1408. UpdateRecentApps(/*needs_layout=*/false);
  1409. UpdateSuggestionChips();
  1410. // If model changes, close the folder view if it's open, as the associated
  1411. // item list is about to go away.
  1412. SetShowState(SHOW_APPS, false);
  1413. }
  1414. void AppsContainerView::OnSuggestionChipsBlurDisablerReleased() {
  1415. DCHECK_GT(suggestion_chips_blur_disabler_count_, 0u);
  1416. --suggestion_chips_blur_disabler_count_;
  1417. if (suggestion_chips_blur_disabler_count_ == 0)
  1418. suggestion_chip_container_view_->SetBlurDisabled(false);
  1419. }
  1420. void AppsContainerView::UpdateGradientMaskBounds() {
  1421. const gfx::Rect container_bounds = scrollable_container_->bounds();
  1422. const gfx::Rect top_gradient_bounds(0, 0, container_bounds.width(),
  1423. kDefaultFadeoutMaskHeight);
  1424. const gfx::Rect bottom_gradient_bounds(
  1425. 0, container_bounds.height() - kDefaultFadeoutMaskHeight,
  1426. container_bounds.width(), kDefaultFadeoutMaskHeight);
  1427. gradient_layer_delegate_->set_start_fade_zone({top_gradient_bounds,
  1428. /*fade_in=*/true,
  1429. /*is_horizontal=*/false});
  1430. gradient_layer_delegate_->set_end_fade_zone({bottom_gradient_bounds,
  1431. /*fade_in=*/false,
  1432. /*is_horizonal=*/false});
  1433. gradient_layer_delegate_->layer()->SetBounds(container_bounds);
  1434. }
  1435. void AppsContainerView::OnAppsGridViewFadeOutAnimationEnded(
  1436. const absl::optional<AppListSortOrder>& new_order,
  1437. bool abort) {
  1438. // Update item positions after the fade out animation but before the fade in
  1439. // animation. NOTE: `update_position_closure_` can be empty in some edge
  1440. // cases. For example, the app list is set with a new order denoted by Order
  1441. // A. Then before the fade out animation is completed, the app list order is
  1442. // reset with the old value. In this case, `update_position_closure_` for
  1443. // Order A is never called. As a result, the closure for resetting the order
  1444. // is empty.
  1445. // Also update item positions only when the fade out animation ends normally.
  1446. // Because a fade out animation is aborted when:
  1447. // (1) Another reorder animation starts, or
  1448. // (2) The apps grid's view model updates due to the reasons such as app
  1449. // installation or model reset.
  1450. // It is meaningless to update item positions in either case.
  1451. if (update_position_closure_ && !abort)
  1452. std::move(update_position_closure_).Run();
  1453. // Record the undo toast's visibility before update.
  1454. const bool old_toast_visible = toast_container_->IsToastVisible();
  1455. toast_container_->OnTemporarySortOrderChanged(new_order);
  1456. HandleFocusAfterSort();
  1457. // Skip the fade in animation if the fade out animation is aborted.
  1458. if (abort) {
  1459. OnReorderAnimationEnded();
  1460. return;
  1461. }
  1462. const bool target_toast_visible = toast_container_->IsToastVisible();
  1463. const bool toast_visibility_change =
  1464. (old_toast_visible != target_toast_visible);
  1465. // When the undo toast's visibility changes, the apps grid's bounds should
  1466. // change. Meanwhile, the fade in animation relies on the apps grid's bounds
  1467. // (because of calculating the visible items). Therefore trigger layout before
  1468. // starting the fade in animation.
  1469. if (toast_visibility_change)
  1470. Layout();
  1471. ash::PaginationModel* pagination_model = apps_grid_view_->pagination_model();
  1472. bool page_change = (pagination_model->selected_page() != 0);
  1473. if (page_change) {
  1474. // Ensure that the undo toast is within the view port after reorder.
  1475. pagination_model->SelectPage(0, /*animate=*/false);
  1476. }
  1477. views::AnimationBuilder animation_builder =
  1478. apps_grid_view_->FadeInVisibleItemsForReorder(base::BindRepeating(
  1479. &AppsContainerView::OnAppsGridViewFadeInAnimationEnded,
  1480. weak_ptr_factory_.GetWeakPtr()));
  1481. // Fade in the undo toast when:
  1482. // (1) The toast's visibility becomes true from false, or
  1483. // (2) The apps page is scrolled to show the toast.
  1484. const bool should_fade_in_toast =
  1485. (target_toast_visible && (page_change || toast_visibility_change));
  1486. if (!should_fade_in_toast)
  1487. return;
  1488. // Hide the toast to prepare for the fade in animation,
  1489. toast_container_->layer()->SetOpacity(0.f);
  1490. animation_builder.GetCurrentSequence().SetOpacity(
  1491. toast_container_->layer(), 1.f, gfx::Tween::ACCEL_5_70_DECEL_90);
  1492. // Continue section should be faded in only when the page changes.
  1493. if (page_change) {
  1494. continue_container_->layer()->SetOpacity(0.f);
  1495. animation_builder.GetCurrentSequence().SetOpacity(
  1496. continue_container_->layer(), 1.f, gfx::Tween::ACCEL_5_70_DECEL_90);
  1497. }
  1498. }
  1499. void AppsContainerView::OnAppsGridViewFadeInAnimationEnded(bool aborted) {
  1500. if (aborted) {
  1501. // Ensure that children are visible when the fade in animation is aborted.
  1502. toast_container_->layer()->SetOpacity(1.f);
  1503. continue_container_->layer()->SetOpacity(1.f);
  1504. }
  1505. OnReorderAnimationEnded();
  1506. }
  1507. void AppsContainerView::OnReorderAnimationEnded() {
  1508. update_position_closure_.Reset();
  1509. if (reorder_animation_done_closure_)
  1510. std::move(reorder_animation_done_closure_).Run();
  1511. }
  1512. void AppsContainerView::HandleFocusAfterSort() {
  1513. // As the sort update on AppsContainerView can be called in both clamshell
  1514. // mode and tablet mode, return early if it's currently in clamshell mode
  1515. // because the AppsContainerView isn't visible.
  1516. if (!contents_view_->app_list_view()->is_tablet_mode())
  1517. return;
  1518. // If the sort is done and the toast is visible and not fading out, request
  1519. // the focus on the undo button on the toast. Otherwise request the focus on
  1520. // the search box.
  1521. if (toast_container_->IsToastVisible()) {
  1522. toast_container_->toast_view()->toast_button()->RequestFocus();
  1523. } else {
  1524. contents_view_->GetSearchBoxView()->search_box()->RequestFocus();
  1525. }
  1526. }
  1527. int AppsContainerView::GetSeparatorHeight() {
  1528. if (!separator_ || !separator_->GetVisible())
  1529. return 0;
  1530. return separator_->GetProperty(views::kMarginsKey)->height() +
  1531. views::Separator::kThickness;
  1532. }
  1533. } // namespace ash