app_list_folder_view.cc 55 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396
  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/app_list_folder_view.h"
  5. #include <algorithm>
  6. #include <limits>
  7. #include <utility>
  8. #include <vector>
  9. #include "ash/accessibility/accessibility_controller_impl.h"
  10. #include "ash/app_list/app_list_metrics.h"
  11. #include "ash/app_list/app_list_model_provider.h"
  12. #include "ash/app_list/app_list_util.h"
  13. #include "ash/app_list/model/app_list_folder_item.h"
  14. #include "ash/app_list/model/app_list_model.h"
  15. #include "ash/app_list/views/app_list_a11y_announcer.h"
  16. #include "ash/app_list/views/app_list_folder_controller.h"
  17. #include "ash/app_list/views/app_list_item_view.h"
  18. #include "ash/app_list/views/folder_background_view.h"
  19. #include "ash/app_list/views/folder_header_view.h"
  20. #include "ash/app_list/views/page_switcher.h"
  21. #include "ash/app_list/views/scrollable_apps_grid_view.h"
  22. #include "ash/app_list/views/search_box_view.h"
  23. #include "ash/app_list/views/top_icon_animation_view.h"
  24. #include "ash/constants/ash_features.h"
  25. #include "ash/controls/rounded_scroll_bar.h"
  26. #include "ash/controls/scroll_view_gradient_helper.h"
  27. #include "ash/keyboard/ui/keyboard_ui_controller.h"
  28. #include "ash/public/cpp/app_list/app_list_color_provider.h"
  29. #include "ash/public/cpp/app_list/app_list_config.h"
  30. #include "ash/public/cpp/app_list/app_list_features.h"
  31. #include "ash/public/cpp/app_list/app_list_model_delegate.h"
  32. #include "ash/public/cpp/metrics_util.h"
  33. #include "ash/public/cpp/pagination/pagination_model.h"
  34. #include "ash/public/cpp/style/color_provider.h"
  35. #include "ash/shell.h"
  36. #include "ash/strings/grit/ash_strings.h"
  37. #include "ash/style/system_shadow.h"
  38. #include "base/barrier_closure.h"
  39. #include "base/bind.h"
  40. #include "base/check.h"
  41. #include "base/strings/utf_string_conversions.h"
  42. #include "ui/accessibility/ax_node_data.h"
  43. #include "ui/base/l10n/l10n_util.h"
  44. #include "ui/base/metadata/metadata_impl_macros.h"
  45. #include "ui/compositor/layer.h"
  46. #include "ui/compositor/layer_animation_observer.h"
  47. #include "ui/compositor/scoped_animation_duration_scale_mode.h"
  48. #include "ui/compositor/scoped_layer_animation_settings.h"
  49. #include "ui/events/event.h"
  50. #include "ui/events/types/event_type.h"
  51. #include "ui/gfx/animation/slide_animation.h"
  52. #include "ui/gfx/canvas.h"
  53. #include "ui/gfx/geometry/point.h"
  54. #include "ui/gfx/geometry/rect_conversions.h"
  55. #include "ui/strings/grit/ui_strings.h"
  56. #include "ui/views/accessibility/view_accessibility.h"
  57. #include "ui/views/animation/animation_delegate_views.h"
  58. #include "ui/views/controls/label.h"
  59. #include "ui/views/controls/scroll_view.h"
  60. #include "ui/views/controls/textfield/textfield.h"
  61. #include "ui/views/highlight_border.h"
  62. #include "ui/views/layout/box_layout.h"
  63. #include "ui/views/layout/fill_layout.h"
  64. #include "ui/views/layout/flex_layout.h"
  65. #include "ui/views/layout/flex_layout_types.h"
  66. #include "ui/views/painter.h"
  67. #include "ui/views/view_class_properties.h"
  68. #include "ui/views/widget/widget.h"
  69. using views::BoxLayout;
  70. namespace ash {
  71. namespace {
  72. constexpr int kFolderHeaderPadding = 12;
  73. constexpr int kOnscreenKeyboardTopPadding = 16;
  74. constexpr int kTileSpacingInFolder = 8;
  75. constexpr int kScrollViewGradientSize = 16;
  76. // Insets for the vertical scroll bar. The top is pushed down slightly to align
  77. // with the icons, which keeps the scroll bar out of the rounded corner area.
  78. constexpr auto kVerticalScrollInsets =
  79. gfx::Insets::TLBR(kTileSpacingInFolder, 0, 1, 1);
  80. // Duration for fading in the target page when opening
  81. // or closing a folder, and the duration for the top folder icon animation
  82. // for flying in or out the folder.
  83. constexpr base::TimeDelta kFolderTransitionDuration = base::Milliseconds(250);
  84. // A utility function for `background_view` to update its background color.
  85. void SetBackgroundViewColor(views::View* background_view,
  86. SkColor color,
  87. bool is_productivity_launcher_enabled) {
  88. if (is_productivity_launcher_enabled) {
  89. background_view->SetBackground(color == SK_ColorTRANSPARENT
  90. ? nullptr
  91. : views::CreateSolidBackground(color));
  92. } else {
  93. background_view->layer()->SetColor(color);
  94. }
  95. }
  96. // Returns true if ChromeVox (spoken feedback) is enabled.
  97. bool IsSpokenFeedbackEnabled() {
  98. return Shell::HasInstance() && // May be null in tests.
  99. Shell::Get()->accessibility_controller()->spoken_feedback().enabled();
  100. }
  101. // Transit from the background of the folder item's icon to the opened
  102. // folder's background when opening the folder. Transit the other way when
  103. // closing the folder.
  104. class BackgroundAnimation : public AppListFolderView::Animation,
  105. public ui::ImplicitAnimationObserver,
  106. public views::ViewObserver {
  107. public:
  108. BackgroundAnimation(bool is_productivity_launcher_enabled,
  109. bool show,
  110. AppListFolderView* folder_view,
  111. views::View* background_view)
  112. : is_productivity_launcher_enabled_(is_productivity_launcher_enabled),
  113. show_(show),
  114. folder_view_(folder_view),
  115. background_view_(background_view),
  116. shadow_(folder_view->shadow()) {
  117. background_view_observer_.Observe(background_view_);
  118. shadow_->GetLayer()->SetVisible(true);
  119. }
  120. BackgroundAnimation(const BackgroundAnimation&) = delete;
  121. BackgroundAnimation& operator=(const BackgroundAnimation&) = delete;
  122. ~BackgroundAnimation() override {
  123. if (!show_)
  124. shadow_->GetLayer()->SetVisible(false);
  125. }
  126. private:
  127. // AppListFolderView::Animation:
  128. void ScheduleAnimation(base::OnceClosure completion_callback) override {
  129. DCHECK(!completion_callback_);
  130. completion_callback_ = std::move(completion_callback);
  131. // Calculate the source and target states.
  132. const int icon_radius =
  133. folder_view_->GetAppListConfig()->folder_icon_radius();
  134. const int folder_radius =
  135. folder_view_->GetAppListConfig()->folder_background_radius();
  136. const int from_radius = show_ ? icon_radius : folder_radius;
  137. const int to_radius = show_ ? folder_radius : icon_radius;
  138. gfx::Rect from_rect = show_ ? folder_view_->folder_item_icon_bounds()
  139. : background_view_->bounds();
  140. from_rect -= background_view_->bounds().OffsetFromOrigin();
  141. gfx::Rect to_rect = show_ ? background_view_->bounds()
  142. : folder_view_->folder_item_icon_bounds();
  143. to_rect -= background_view_->bounds().OffsetFromOrigin();
  144. const SkColor background_color =
  145. AppListColorProvider::Get()->GetFolderBackgroundColor();
  146. const SkColor bubble_color =
  147. AppListColorProvider::Get()->GetFolderBubbleColor();
  148. const SkColor from_color = show_ ? bubble_color : background_color;
  149. const SkColor to_color = show_ ? background_color : bubble_color;
  150. SetBackgroundViewColor(background_view_, from_color,
  151. is_productivity_launcher_enabled_);
  152. background_view_->layer()->SetClipRect(from_rect);
  153. background_view_->layer()->SetRoundedCornerRadius(
  154. gfx::RoundedCornersF(from_radius));
  155. ui::ScopedLayerAnimationSettings settings(
  156. background_view_->layer()->GetAnimator());
  157. settings.SetTransitionDuration(kFolderTransitionDuration);
  158. settings.SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN);
  159. settings.AddObserver(this);
  160. SetBackgroundViewColor(background_view_, to_color,
  161. is_productivity_launcher_enabled_);
  162. background_view_->layer()->SetClipRect(to_rect);
  163. background_view_->layer()->SetRoundedCornerRadius(
  164. gfx::RoundedCornersF(to_radius));
  165. is_animating_ = true;
  166. }
  167. bool IsAnimationRunning() override { return is_animating_; }
  168. // ui::ImplicitAnimationObserver:
  169. void OnImplicitAnimationsScheduled() override {
  170. // Remove the highlight border at the start of the closing animation.
  171. if (is_productivity_launcher_enabled_ && !show_)
  172. folder_view_->UpdateHighlightBorder(false);
  173. }
  174. // ui::ImplicitAnimationObserver:
  175. void OnImplicitAnimationsCompleted() override {
  176. // Add the highlight border when the showing animation is completed.
  177. if (is_productivity_launcher_enabled_ && show_)
  178. folder_view_->UpdateHighlightBorder(true);
  179. is_animating_ = false;
  180. folder_view_->RecordAnimationSmoothness();
  181. if (completion_callback_)
  182. std::move(completion_callback_).Run();
  183. }
  184. // views::ViewObserver:
  185. void OnViewLayerClipRectChanged(views::View* observed_view) override {
  186. // Shadow is painted on the nine patch layer according to its owner's shape,
  187. // so the shadow cannot be animated with the change of shadow layer's
  188. // attributes. We need to use the intermediate clip rect shape from
  189. // background animation to update shadow's contents bounds and corner
  190. // radius.
  191. DCHECK_EQ(observed_view, background_view_);
  192. // If layer clip rect is not empty, we use the clip rect to update the
  193. // shadow's contents bounds. Otherwise, we use the layer bounds.
  194. const auto* background_layer = background_view_->layer();
  195. const gfx::Rect& background_bounds = background_layer->bounds();
  196. const gfx::Rect& clip_rect = background_layer->clip_rect();
  197. const gfx::Rect& content_bounds =
  198. clip_rect.IsEmpty() ? background_bounds
  199. : clip_rect + background_bounds.OffsetFromOrigin();
  200. shadow_->SetContentBounds(content_bounds);
  201. shadow_->SetRoundedCornerRadius(
  202. background_layer->rounded_corner_radii().upper_left());
  203. }
  204. // Caches the productivity launcher feature flag.
  205. bool is_productivity_launcher_enabled_ = false;
  206. // True if opening the folder.
  207. const bool show_;
  208. bool is_animating_ = false;
  209. AppListFolderView* const folder_view_; // Not owned.
  210. views::View* const background_view_; // Not owned.
  211. SystemShadow* const shadow_; // Not owned.
  212. // Observes the rect clip change of background view.
  213. base::ScopedObservation<views::View, views::ViewObserver>
  214. background_view_observer_{this};
  215. base::OnceClosure completion_callback_;
  216. };
  217. // Decrease the opacity of the folder item's title when opening the folder.
  218. // Increase it when closing the folder.
  219. class FolderItemTitleAnimation : public AppListFolderView::Animation,
  220. public views::AnimationDelegateViews {
  221. public:
  222. FolderItemTitleAnimation(bool show,
  223. AppListFolderView* folder_view,
  224. AppListItemView* folder_item_view)
  225. : views::AnimationDelegateViews(folder_view),
  226. show_(show),
  227. animation_(this),
  228. folder_view_(folder_view),
  229. folder_item_view_(folder_item_view) {
  230. SkColor title_color = AppListColorProvider::Get()->GetAppListItemTextColor(
  231. /*is_in_folder=*/false);
  232. // Calculate the source and target states.
  233. from_color_ = show_ ? title_color : SK_ColorTRANSPARENT;
  234. to_color_ = show_ ? SK_ColorTRANSPARENT : title_color;
  235. animation_.SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN);
  236. animation_.SetSlideDuration(
  237. ui::ScopedAnimationDurationScaleMode::duration_multiplier() *
  238. kFolderTransitionDuration);
  239. }
  240. FolderItemTitleAnimation(const FolderItemTitleAnimation&) = delete;
  241. FolderItemTitleAnimation& operator=(const FolderItemTitleAnimation&) = delete;
  242. ~FolderItemTitleAnimation() override = default;
  243. private:
  244. // AppListFolderView::Animation:
  245. void ScheduleAnimation(base::OnceClosure completion_callback) override {
  246. DCHECK(!completion_callback_);
  247. completion_callback_ = std::move(completion_callback);
  248. animation_.Show();
  249. }
  250. bool IsAnimationRunning() override { return animation_.is_animating(); }
  251. // gfx::AnimationDelegate
  252. void AnimationProgressed(const gfx::Animation* animation) override {
  253. folder_item_view_->title()->SetEnabledColor(gfx::Tween::ColorValueBetween(
  254. animation->GetCurrentValue(), from_color_, to_color_));
  255. }
  256. void AnimationEnded(const gfx::Animation* animation) override {
  257. folder_item_view_->title()->SetEnabledColor(to_color_);
  258. folder_view_->RecordAnimationSmoothness();
  259. if (completion_callback_)
  260. std::move(completion_callback_).Run();
  261. }
  262. void AnimationCanceled(const gfx::Animation* animation) override {
  263. AnimationEnded(animation);
  264. }
  265. // True if opening the folder.
  266. const bool show_;
  267. // The source and target state of the title's color.
  268. SkColor from_color_;
  269. SkColor to_color_;
  270. gfx::SlideAnimation animation_;
  271. AppListFolderView* const folder_view_; // Not owned.
  272. // The app list item view with which the folder view is associated.
  273. // NOTE: Users of `FolderItemTitleAnimation` should ensure the animation does
  274. // not outlive the `folder_item_view_`.
  275. AppListItemView* const folder_item_view_;
  276. base::OnceClosure completion_callback_;
  277. };
  278. // Transit from the items within the folder item icon to the same items in the
  279. // opened folder when opening the folder. Transit the other way when closing the
  280. // folder.
  281. class TopIconAnimation : public AppListFolderView::Animation,
  282. public TopIconAnimationObserver {
  283. public:
  284. TopIconAnimation(bool show,
  285. AppListFolderView* folder_view,
  286. views::ScrollView* scroll_view,
  287. AppListItemView* folder_item_view)
  288. : show_(show),
  289. folder_view_(folder_view),
  290. scroll_view_(scroll_view),
  291. folder_item_view_(folder_item_view) {}
  292. TopIconAnimation(const TopIconAnimation&) = delete;
  293. TopIconAnimation& operator=(const TopIconAnimation&) = delete;
  294. ~TopIconAnimation() override {
  295. for (auto* view : top_icon_views_)
  296. view->RemoveObserver(this);
  297. top_icon_views_.clear();
  298. }
  299. // AppListFolderView::Animation
  300. void ScheduleAnimation(base::OnceClosure completion_callback) override {
  301. DCHECK(!completion_callback_);
  302. completion_callback_ = std::move(completion_callback);
  303. // Hide the original items in the folder until the animation ends.
  304. SetFirstPageItemViewsVisible(false);
  305. folder_item_view_->SetIconVisible(false);
  306. // Calculate the start and end bounds of the top item icons in the
  307. // animation.
  308. std::vector<gfx::Rect> top_item_views_bounds =
  309. GetTopItemViewsBoundsInFolderIcon();
  310. std::vector<gfx::Rect> first_page_item_views_bounds =
  311. GetFirstPageItemViewsBounds();
  312. top_icon_views_.clear();
  313. const AppListConfigType app_list_config_type =
  314. folder_view_->GetAppListConfig()->type();
  315. // Get top folder items that should be animated - note that item index in
  316. // the folder item list may not match the intended item bounds in
  317. // `first_page_item_views_bounds` if it's preceded by a drag item in the
  318. // item list.
  319. std::vector<const AppListItem*> top_items;
  320. const AppListItem* drag_item = folder_view_->items_grid_view()->drag_item();
  321. const AppListItemList* folder_items =
  322. folder_view_->folder_item()->item_list();
  323. for (size_t i = 0; i < folder_items->item_count(); ++i) {
  324. if (top_items.size() == first_page_item_views_bounds.size())
  325. break;
  326. const AppListItem* top_item = folder_items->item_at(i);
  327. if (top_item->GetIcon(app_list_config_type).isNull() ||
  328. top_item == drag_item) {
  329. // The item being dragged should be excluded.
  330. continue;
  331. }
  332. top_items.push_back(top_item);
  333. }
  334. for (size_t i = 0; i < top_items.size(); ++i) {
  335. const AppListItem* top_item = top_items[i];
  336. bool item_in_folder_icon = i < top_item_views_bounds.size();
  337. gfx::Rect scaled_rect = item_in_folder_icon
  338. ? top_item_views_bounds[i]
  339. : folder_view_->folder_item_icon_bounds();
  340. auto icon_view = std::make_unique<TopIconAnimationView>(
  341. folder_view_->items_grid_view(),
  342. top_item->GetIcon(app_list_config_type),
  343. base::UTF8ToUTF16(top_item->GetDisplayName()), scaled_rect, show_,
  344. item_in_folder_icon);
  345. auto* icon_view_ptr = icon_view.get();
  346. icon_view_ptr->AddObserver(this);
  347. // Add the transitional views into child views, and set its bounds to the
  348. // same location of the item in the folder list view.
  349. top_icon_views_.push_back(
  350. folder_view_->background_view()->AddChildView(std::move(icon_view)));
  351. icon_view_ptr->SetBoundsRect(first_page_item_views_bounds[i]);
  352. icon_view_ptr->TransformView(kFolderTransitionDuration);
  353. }
  354. if (top_icon_views_.empty())
  355. OnAnimationComplete();
  356. }
  357. bool IsAnimationRunning() override { return !top_icon_views_.empty(); }
  358. // TopIconAnimationObserver
  359. void OnTopIconAnimationsComplete(TopIconAnimationView* view) override {
  360. // Clean up the transitional view for which the animation completes.
  361. view->RemoveObserver(this);
  362. auto to_delete =
  363. std::find(top_icon_views_.begin(), top_icon_views_.end(), view);
  364. DCHECK(to_delete != top_icon_views_.end());
  365. top_icon_views_.erase(to_delete);
  366. folder_view_->RecordAnimationSmoothness();
  367. // An empty list indicates that all animations are done.
  368. if (top_icon_views_.empty())
  369. OnAnimationComplete();
  370. }
  371. // Called when all top icon animations complete.
  372. void OnAnimationComplete() {
  373. // Set top item views visible when opening the folder.
  374. if (show_)
  375. SetFirstPageItemViewsVisible(true);
  376. // Show the folder icon when closing the folder.
  377. if (!show_)
  378. folder_item_view_->SetIconVisible(true);
  379. if (completion_callback_)
  380. std::move(completion_callback_).Run();
  381. }
  382. private:
  383. std::vector<gfx::Rect> GetTopItemViewsBoundsInFolderIcon() {
  384. const AppListConfig* const app_list_config =
  385. folder_view_->GetAppListConfig();
  386. size_t effective_folder_size =
  387. folder_view_->folder_item()->ChildItemCount();
  388. // If a folder item is being dragged, it should be hidden from the folder
  389. // item icon, and top icons bounds should be calculated as if the item is
  390. // not in the folder.
  391. if (folder_view_->items_grid_view()->drag_item())
  392. effective_folder_size -= 1;
  393. std::vector<gfx::Rect> top_icons_bounds = FolderImage::GetTopIconsBounds(
  394. *app_list_config, folder_view_->folder_item_icon_bounds(),
  395. std::min(effective_folder_size, FolderImage::kNumFolderTopItems));
  396. std::vector<gfx::Rect> top_item_views_bounds;
  397. const int icon_dimension = app_list_config->grid_icon_dimension();
  398. const int icon_bottom_padding = app_list_config->grid_icon_bottom_padding();
  399. const int tile_width = app_list_config->grid_tile_width();
  400. const int tile_height = app_list_config->grid_tile_height();
  401. for (gfx::Rect bounds : top_icons_bounds) {
  402. // Calculate the item view's bounds based on the icon bounds.
  403. gfx::Rect item_bounds(
  404. (icon_dimension - tile_width) / 2,
  405. (icon_dimension + icon_bottom_padding - tile_height) / 2, tile_width,
  406. tile_height);
  407. item_bounds = gfx::ScaleToRoundedRect(
  408. item_bounds, bounds.width() / static_cast<float>(icon_dimension),
  409. bounds.height() / static_cast<float>(icon_dimension));
  410. item_bounds.Offset(bounds.x(), bounds.y());
  411. top_item_views_bounds.emplace_back(item_bounds);
  412. }
  413. return top_item_views_bounds;
  414. }
  415. void SetFirstPageItemViewsVisible(bool visible) {
  416. // Items grid view has to be visible in case an item is being reparented, so
  417. // only set the opacity here.
  418. folder_view_->items_grid_view()->layer()->SetOpacity(visible ? 1.0f : 0.0f);
  419. }
  420. // Get the bounds of the items in the first page of the opened folder relative
  421. // to AppListFolderView.
  422. std::vector<gfx::Rect> GetFirstPageItemViewsBounds() {
  423. std::vector<gfx::Rect> items_bounds;
  424. // Go over items in the folder, and collect bounds of items that fit within
  425. // the bounds of the first "page" of apps.
  426. const size_t count = folder_view_->folder_item()->ChildItemCount();
  427. views::View* container =
  428. features::IsProductivityLauncherEnabled()
  429. ? static_cast<views::View*>(scroll_view_)
  430. : static_cast<views::View*>(folder_view_->items_grid_view());
  431. const gfx::RectF container_bounds(container->GetLocalBounds());
  432. for (size_t i = 0; i < count; ++i) {
  433. views::View* item = folder_view_->items_grid_view()->GetItemViewAt(i);
  434. if (folder_view_->items_grid_view()->IsViewHiddenForDrag(item))
  435. continue;
  436. // Stop if the item bounds are not within the container bounds - assumes
  437. // that subsequent item bounds would not be within the container view
  438. // either.
  439. gfx::RectF bounds_in_container(item->GetLocalBounds());
  440. views::View::ConvertRectToTarget(item, container, &bounds_in_container);
  441. if (!container_bounds.Contains(bounds_in_container))
  442. break;
  443. // Return the item bounds in AppListFolderView coordinates.
  444. gfx::RectF bounds_in_folder(item->GetLocalBounds());
  445. views::View::ConvertRectToTarget(item, folder_view_, &bounds_in_folder);
  446. items_bounds.emplace_back(
  447. folder_view_->GetMirroredRect(gfx::ToRoundedRect(bounds_in_folder)));
  448. }
  449. return items_bounds;
  450. }
  451. // True if opening the folder.
  452. const bool show_;
  453. AppListFolderView* const folder_view_; // Not owned.
  454. // The scroll view that contains the apps grid. Used with
  455. // ProductivityLauncher.
  456. views::ScrollView* const scroll_view_;
  457. // The app list item view with which the folder view is associated.
  458. // NOTE: Users of `TopIconAnimation` should ensure the animation does
  459. // not outlive the `folder_item_view_`.
  460. AppListItemView* const folder_item_view_;
  461. std::vector<TopIconAnimationView*> top_icon_views_;
  462. base::OnceClosure completion_callback_;
  463. };
  464. // Transit from the bounds of the folder item icon to the opened folder's
  465. // bounds and transit opacity from 0 to 1 when opening the folder. Transit the
  466. // other way when closing the folder.
  467. class ContentsContainerAnimation : public AppListFolderView::Animation,
  468. public ui::ImplicitAnimationObserver {
  469. public:
  470. ContentsContainerAnimation(bool show,
  471. bool hide_for_reparent,
  472. AppListFolderView* folder_view)
  473. : show_(show),
  474. hide_for_reparent_(hide_for_reparent),
  475. folder_view_(folder_view) {}
  476. ContentsContainerAnimation(const ContentsContainerAnimation&) = delete;
  477. ContentsContainerAnimation& operator=(const ContentsContainerAnimation&) =
  478. delete;
  479. ~ContentsContainerAnimation() override { StopObservingImplicitAnimations(); }
  480. // AppListFolderView::Animation
  481. void ScheduleAnimation(base::OnceClosure completion_callback) override {
  482. DCHECK(!completion_callback_);
  483. completion_callback_ = std::move(completion_callback);
  484. // Transform used to scale the folder's contents container from the bounds
  485. // of the folder icon to that of the opened folder.
  486. gfx::Transform transform;
  487. const gfx::Rect scaled_rect(folder_view_->folder_item_icon_bounds());
  488. const gfx::Rect rect(folder_view_->contents_container()->bounds());
  489. ui::Layer* layer = folder_view_->contents_container()->layer();
  490. transform.Translate(scaled_rect.x() - rect.x(), scaled_rect.y() - rect.y());
  491. transform.Scale(static_cast<double>(scaled_rect.width()) / rect.width(),
  492. static_cast<double>(scaled_rect.height()) / rect.height());
  493. if (show_)
  494. layer->SetTransform(transform);
  495. layer->SetOpacity(show_ ? 0.0f : 1.0f);
  496. // The folder should be set visible only after it is scaled down and
  497. // transparent to prevent the flash of the view right before the animation.
  498. folder_view_->SetVisible(true);
  499. ui::ScopedLayerAnimationSettings animation(layer->GetAnimator());
  500. animation.SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN);
  501. animation.AddObserver(this);
  502. animation.SetTransitionDuration(kFolderTransitionDuration);
  503. layer->SetTransform(show_ ? gfx::Transform() : transform);
  504. layer->SetOpacity(show_ ? 1.0f : 0.0f);
  505. is_animation_running_ = true;
  506. }
  507. bool IsAnimationRunning() override { return is_animation_running_; }
  508. // ui::ImplicitAnimationObserver
  509. void OnImplicitAnimationsCompleted() override {
  510. is_animation_running_ = false;
  511. // If the view is hidden for reparenting a folder item, it has to be
  512. // visible, so that drag_view_ can keep receiving mouse events.
  513. if (!show_ && !hide_for_reparent_)
  514. folder_view_->SetVisible(false);
  515. // Set the view bounds offscreen, so that it won't overlap the root level
  516. // apps grid view during folder item reparenting transitional period.
  517. // Keeping the same width and height avoids re-layout and ensures that
  518. // AppListItemView continues to receive events. The view will be set
  519. // invisible at the end of the drag.
  520. if (hide_for_reparent_) {
  521. const gfx::Rect& bounds = folder_view_->bounds();
  522. folder_view_->SetPosition(gfx::Point(-bounds.width(), -bounds.height()));
  523. }
  524. // Reset the transform after animation so that the following folder's
  525. // preferred bounds is calculated correctly.
  526. folder_view_->contents_container()->layer()->SetTransform(gfx::Transform());
  527. folder_view_->RecordAnimationSmoothness();
  528. if (completion_callback_)
  529. std::move(completion_callback_).Run();
  530. }
  531. private:
  532. // True if opening the folder.
  533. const bool show_;
  534. // True if an item in the folder is being reparented to root grid view.
  535. const bool hide_for_reparent_;
  536. AppListFolderView* const folder_view_;
  537. bool is_animation_running_ = false;
  538. base::OnceClosure completion_callback_;
  539. };
  540. // ScrollViewWithMaxHeight limits its preferred size to a maximum height that
  541. // shows 4 apps grid rows.
  542. class ScrollViewWithMaxHeight : public views::ScrollView {
  543. public:
  544. explicit ScrollViewWithMaxHeight(AppListFolderView* folder_view)
  545. : views::ScrollView(views::ScrollView::ScrollWithLayers::kEnabled),
  546. folder_view_(folder_view) {}
  547. ScrollViewWithMaxHeight(const ScrollViewWithMaxHeight&) = delete;
  548. ScrollViewWithMaxHeight& operator=(const ScrollViewWithMaxHeight&) = delete;
  549. ~ScrollViewWithMaxHeight() override = default;
  550. // views::View:
  551. gfx::Size CalculatePreferredSize() const override {
  552. gfx::Size size = views::ScrollView::CalculatePreferredSize();
  553. const int tile_height =
  554. folder_view_->items_grid_view()->GetTotalTileSize(/*page=*/0).height();
  555. // Show a maximum of 4 full rows, plus a little bit of the next row to make
  556. // it obvious the view can scroll.
  557. const int max_height = (tile_height * 4) + (tile_height / 4);
  558. size.set_height(std::min(size.height(), max_height));
  559. return size;
  560. }
  561. private:
  562. AppListFolderView* const folder_view_;
  563. };
  564. } // namespace
  565. AppListFolderView::AppListFolderView(AppListFolderController* folder_controller,
  566. AppsGridView* root_apps_grid_view,
  567. ContentsView* contents_view,
  568. AppListA11yAnnouncer* a11y_announcer,
  569. AppListViewDelegate* view_delegate)
  570. : is_productivity_launcher_enabled_(
  571. features::IsProductivityLauncherEnabled()),
  572. folder_controller_(folder_controller),
  573. root_apps_grid_view_(root_apps_grid_view),
  574. a11y_announcer_(a11y_announcer),
  575. view_delegate_(view_delegate) {
  576. DCHECK(folder_controller_);
  577. DCHECK(root_apps_grid_view_);
  578. DCHECK(a11y_announcer_);
  579. DCHECK(view_delegate_);
  580. SetLayoutManager(std::make_unique<views::FillLayout>());
  581. // The background's corner radius cannot be changed in the same layer of the
  582. // contents container using layer animation, so use another layer to perform
  583. // such changes.
  584. background_view_ = AddChildView(std::make_unique<views::View>());
  585. background_view_->SetPaintToLayer(is_productivity_launcher_enabled_
  586. ? ui::LAYER_TEXTURED
  587. : ui::LAYER_SOLID_COLOR);
  588. background_view_->layer()->SetFillsBoundsOpaquely(false);
  589. background_view_->layer()->SetBackgroundBlur(
  590. ColorProvider::kBackgroundBlurSigma);
  591. background_view_->layer()->SetBackdropFilterQuality(
  592. ColorProvider::kBackgroundBlurQuality);
  593. contents_container_ = AddChildView(std::make_unique<views::View>());
  594. contents_container_->SetPaintToLayer(ui::LAYER_NOT_DRAWN);
  595. if (is_productivity_launcher_enabled_)
  596. CreateScrollableAppsGrid();
  597. else
  598. CreatePagedAppsGrid(contents_view);
  599. // Create a shadow under `background_view_`.
  600. shadow_ = SystemShadow::CreateShadowOnNinePatchLayer(
  601. SystemShadow::Type::kElevation8);
  602. background_view_->AddLayerBeneathView(shadow_->GetLayer());
  603. shadow_->GetLayer()->SetVisible(false);
  604. AppListModelProvider::Get()->AddObserver(this);
  605. }
  606. void AppListFolderView::CreatePagedAppsGrid(ContentsView* contents_view) {
  607. DCHECK(contents_view);
  608. // Cache typed apps grid view pointer to perform setup specific to
  609. // PagedAppsGridView (e.g. `SetMaxRows()`) without requiring static cast.
  610. PagedAppsGridView* items_grid_view =
  611. contents_container_->AddChildView(std::make_unique<PagedAppsGridView>(
  612. contents_view, a11y_announcer_, this, /*folder_controller=*/nullptr,
  613. /*container_delegate=*/this, /*keyboard_controller=*/nullptr));
  614. contents_container_->layer()->SetMasksToBounds(true);
  615. items_grid_view_ = items_grid_view;
  616. items_grid_view_->Init();
  617. items_grid_view->SetMaxColumnsAndRows(
  618. kMaxFolderColumns,
  619. /*max_rows_on_first_page=*/kMaxPagedFolderRows,
  620. /*max_rows=*/kMaxPagedFolderRows);
  621. items_grid_view->SetFixedTilePadding(kTileSpacingInFolder / 2,
  622. kTileSpacingInFolder / 2);
  623. folder_header_view_ = contents_container_->AddChildView(
  624. std::make_unique<FolderHeaderView>(this));
  625. folder_header_view_->SetProperty(views::kMarginsKey,
  626. gfx::Insets::VH(kFolderHeaderPadding, 0));
  627. page_switcher_ =
  628. contents_container_->AddChildView(std::make_unique<PageSwitcher>(
  629. items_grid_view->pagination_model(), false /* vertical */,
  630. view_delegate_->IsInTabletMode(),
  631. AppListColorProvider::Get()->GetFolderBackgroundColor()));
  632. contents_container_->SetLayoutManager(std::make_unique<views::FlexLayout>())
  633. ->SetOrientation(views::LayoutOrientation::kVertical)
  634. .SetInteriorMargin(gfx::Insets(kTileSpacingInFolder))
  635. .SetCollapseMargins(true)
  636. .SetChildViewIgnoredByLayout(page_switcher_, true);
  637. }
  638. void AppListFolderView::CreateScrollableAppsGrid() {
  639. // The top part of the folder contents is a scrollable apps grid.
  640. scroll_view_ = contents_container_->AddChildView(
  641. std::make_unique<ScrollViewWithMaxHeight>(this));
  642. scroll_view_->ClipHeightTo(0, std::numeric_limits<int>::max());
  643. scroll_view_->SetDrawOverflowIndicator(false);
  644. // Don't paint a background. The folder already has one.
  645. scroll_view_->SetBackgroundColor(absl::nullopt);
  646. // Arrow keys are used to select app icons.
  647. scroll_view_->SetAllowKeyboardScrolling(false);
  648. // Set up fade in/fade out gradients at top/bottom of scroll view.
  649. scroll_view_->SetPaintToLayer(ui::LAYER_NOT_DRAWN);
  650. gradient_helper_ = std::make_unique<ScrollViewGradientHelper>(
  651. scroll_view_, kScrollViewGradientSize);
  652. // Set up scroll bars.
  653. scroll_view_->SetHorizontalScrollBarMode(
  654. views::ScrollView::ScrollBarMode::kDisabled);
  655. auto vertical_scroll =
  656. std::make_unique<RoundedScrollBar>(/*horizontal=*/false);
  657. vertical_scroll->SetInsets(kVerticalScrollInsets);
  658. scroll_view_->SetVerticalScrollBar(std::move(vertical_scroll));
  659. // Add margins inside the scroll contents.
  660. auto scroll_contents = std::make_unique<views::View>();
  661. scroll_contents->SetLayoutManager(std::make_unique<views::FlexLayout>())
  662. ->SetOrientation(views::LayoutOrientation::kVertical)
  663. .SetInteriorMargin(gfx::Insets(kTileSpacingInFolder))
  664. .SetCollapseMargins(true);
  665. // Create the apps grid.
  666. auto* items_grid_view =
  667. scroll_contents->AddChildView(std::make_unique<ScrollableAppsGridView>(
  668. a11y_announcer_, view_delegate_, this, scroll_view_,
  669. /*folder_controller=*/nullptr, /*keyboard_controller=*/nullptr));
  670. items_grid_view_ = items_grid_view;
  671. items_grid_view->Init();
  672. items_grid_view->SetMaxColumns(kMaxFolderColumns);
  673. items_grid_view->SetFixedTilePadding(kTileSpacingInFolder / 2,
  674. kTileSpacingInFolder / 2);
  675. scroll_view_->SetContents(std::move(scroll_contents));
  676. // In the common case, the parent view is large and the folder has a small
  677. // number of apps, so the scroll view's size will be limited by the apps grid
  678. // view's preferred size. However, if the parent view is small, the scroll
  679. // view will scale down, so there is enough space for the header view.
  680. scroll_view_->SetProperty(
  681. views::kFlexBehaviorKey,
  682. views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToZero,
  683. views::MaximumFlexSizeRule::kPreferred));
  684. folder_header_view_ = contents_container_->AddChildView(
  685. std::make_unique<FolderHeaderView>(this));
  686. folder_header_view_->SetProperty(views::kMarginsKey,
  687. gfx::Insets::VH(kFolderHeaderPadding, 0));
  688. // No margins on `contents_container_` because the scroll view needs to fully
  689. // extend to the parent's edges.
  690. contents_container_->SetLayoutManager(std::make_unique<views::FlexLayout>())
  691. ->SetOrientation(views::LayoutOrientation::kVertical);
  692. }
  693. AppListFolderView::~AppListFolderView() {
  694. AppListModelProvider::Get()->RemoveObserver(this);
  695. // This prevents the AppsGridView's destructor from calling the now-deleted
  696. // AppListFolderView's methods if a drag is in progress at the time.
  697. items_grid_view_->set_folder_delegate(nullptr);
  698. // Make sure |page_switcher_| is deleted before |items_grid_view_| because
  699. // |page_switcher_| uses the PaginationModel owned by |items_grid_view_|.
  700. delete page_switcher_;
  701. }
  702. void AppListFolderView::UpdateAppListConfig(const AppListConfig* config) {
  703. items_grid_view_->UpdateAppListConfig(config);
  704. ShrinkGridTileMarginsWhenNeeded();
  705. }
  706. void AppListFolderView::ConfigureForFolderItemView(
  707. AppListItemView* folder_item_view,
  708. base::OnceClosure hide_callback) {
  709. DCHECK(folder_item_view->is_folder());
  710. DCHECK(folder_item_view->item());
  711. DCHECK(items_grid_view_->app_list_config());
  712. // Clear any remaining state from the last time the folder was shown. E.g.
  713. // cancel any pending hide animations.
  714. ResetState(/*restore_folder_item_view_state=*/true);
  715. hide_callback_ = std::move(hide_callback);
  716. folder_item_view_ = folder_item_view;
  717. folder_item_view_observer_.Observe(folder_item_view);
  718. folder_item_ = static_cast<AppListFolderItem*>(folder_item_view->item());
  719. AppListModel* const model = AppListModelProvider::Get()->model();
  720. items_grid_view_->SetModel(model);
  721. items_grid_view_->SetItemList(folder_item_->item_list());
  722. folder_header_view_->SetFolderItem(folder_item_);
  723. model_observation_.Observe(model);
  724. UpdatePreferredBounds();
  725. }
  726. void AppListFolderView::ScheduleShowHideAnimation(bool show,
  727. bool hide_for_reparent) {
  728. show_hide_metrics_tracker_ =
  729. GetWidget()->GetCompositor()->RequestNewThroughputTracker();
  730. show_hide_metrics_tracker_->Start(
  731. metrics_util::ForSmoothness(base::BindRepeating([](int smoothness) {
  732. UMA_HISTOGRAM_PERCENTAGE(
  733. "Apps.AppListFolder.ShowHide.AnimationSmoothness", smoothness);
  734. })));
  735. if (!is_productivity_launcher_enabled_) {
  736. static_cast<PagedAppsGridView*>(items_grid_view_)
  737. ->pagination_model()
  738. ->SelectPage(0, false);
  739. }
  740. folder_visibility_animations_.clear();
  741. shown_ = show;
  742. if (show) {
  743. GetViewAccessibility().OverrideName(folder_item_view_->GetAccessibleName());
  744. }
  745. NotifyAccessibilityEvent(ax::mojom::Event::kStateChanged, true);
  746. // Animate the background corner radius, opacity and bounds.
  747. folder_visibility_animations_.push_back(std::make_unique<BackgroundAnimation>(
  748. is_productivity_launcher_enabled_, show, this, background_view_));
  749. // Animate the folder item's title's opacity.
  750. folder_visibility_animations_.push_back(
  751. std::make_unique<FolderItemTitleAnimation>(show, this,
  752. folder_item_view_));
  753. // Animate the bounds and opacity of items in the first page of the opened
  754. // folder.
  755. folder_visibility_animations_.push_back(std::make_unique<TopIconAnimation>(
  756. show, this, scroll_view_, folder_item_view_));
  757. // Animate the bounds and opacity of the contents container.
  758. folder_visibility_animations_.push_back(
  759. std::make_unique<ContentsContainerAnimation>(show, hide_for_reparent,
  760. this));
  761. base::RepeatingClosure animation_completion_callback;
  762. if (!show) {
  763. animation_completion_callback = base::BarrierClosure(
  764. folder_visibility_animations_.size(),
  765. base::BindOnce(&AppListFolderView::OnHideAnimationDone,
  766. weak_ptr_factory_.GetWeakPtr(), hide_for_reparent));
  767. } else {
  768. animation_completion_callback = base::BarrierClosure(
  769. folder_visibility_animations_.size(),
  770. base::BindOnce(&AppListFolderView::OnShowAnimationDone,
  771. weak_ptr_factory_.GetWeakPtr()));
  772. }
  773. for (auto& animation : folder_visibility_animations_)
  774. animation->ScheduleAnimation(animation_completion_callback);
  775. }
  776. void AppListFolderView::Layout() {
  777. views::View::Layout();
  778. if (gradient_helper_)
  779. gradient_helper_->UpdateGradientZone();
  780. // Position page switcher independently of the layout manager, as its
  781. // position does not fit with vertical layout alignment (it's expected to
  782. // float over the header view in the bottom right corner).
  783. if (page_switcher_) {
  784. const gfx::Size page_switcher_size = page_switcher_->GetPreferredSize();
  785. const gfx::Rect folder_header_bounds = folder_header_view_->bounds();
  786. const int page_switcher_x =
  787. folder_header_bounds.right() - page_switcher_size.width();
  788. // The page switcher has a different height than the folder header, but it
  789. // still needs to be aligned with it.
  790. const int page_switcher_y =
  791. folder_header_bounds.y() -
  792. (page_switcher_size.height() - folder_header_bounds.height()) / 2;
  793. page_switcher_->SetBoundsRect(gfx::Rect(
  794. gfx::Point(page_switcher_x, page_switcher_y), page_switcher_size));
  795. }
  796. // `BackgroundAnimation` animates the clip rect during open/close.
  797. if (!IsAnimationRunning()) {
  798. // The folder view can change size due to app install/uninstall. Ensure the
  799. // rounded corners have the correct position. https://crbug.com/993282
  800. background_view_->layer()->SetClipRect(background_view_->GetLocalBounds());
  801. shadow_->SetContentBounds(background_view_->layer()->bounds());
  802. }
  803. }
  804. void AppListFolderView::ChildPreferredSizeChanged(views::View* child) {
  805. UpdatePreferredBounds();
  806. PreferredSizeChanged();
  807. }
  808. void AppListFolderView::OnActiveAppListModelsChanged(
  809. AppListModel* model,
  810. SearchModel* search_model) {
  811. // If the active model changed, close the folder view, as the backing app list
  812. // item is about to go away.
  813. if (folder_item_) {
  814. ResetState(/*restore_folder_item_view_state=*/false);
  815. folder_controller_->ShowApps(/*folder_item_view=*/nullptr,
  816. /*select_folder=*/false);
  817. }
  818. }
  819. void AppListFolderView::OnViewIsDeleting(views::View* view) {
  820. DCHECK_EQ(view, folder_item_view_);
  821. // If the original view got removed, clear any references to it, this includes
  822. // animations that may try to access the view to update its visibility.
  823. folder_visibility_animations_.clear();
  824. folder_item_view_observer_.Reset();
  825. folder_item_view_ = nullptr;
  826. }
  827. void AppListFolderView::OnAppListItemWillBeDeleted(AppListItem* item) {
  828. if (item == folder_item_) {
  829. ResetState(/*restore_folder_item_view_state=*/true);
  830. // If the folder item associated with this view is removed from the model,
  831. // (e.g. the last item in the folder was deleted), reset the view and signal
  832. // the container view to show the app list instead.
  833. // Pass nullptr to ShowApps() to avoid triggering animation from the deleted
  834. // folder.
  835. folder_controller_->ShowApps(/*folder_item_view=*/nullptr,
  836. /*select_folder=*/false);
  837. }
  838. }
  839. void AppListFolderView::ResetState(bool restore_folder_item_view_state) {
  840. DVLOG(1) << __FUNCTION__;
  841. if (hide_callback_)
  842. std::move(hide_callback_).Run();
  843. if (folder_item_) {
  844. items_grid_view_->ClearSelectedView();
  845. items_grid_view_->SetItemList(nullptr);
  846. items_grid_view_->SetModel(nullptr);
  847. folder_header_view_->SetFolderItem(nullptr);
  848. folder_item_ = nullptr;
  849. }
  850. model_observation_.Reset();
  851. show_hide_metrics_tracker_.reset();
  852. // Clear in-progress animations, as they may depend on the
  853. // `folder_item_view_`.
  854. folder_visibility_animations_.clear();
  855. // Transition all the states immediately to the end of folder closing
  856. // animation.
  857. SetBackgroundViewColor(background_view_, SK_ColorTRANSPARENT,
  858. is_productivity_launcher_enabled_);
  859. if (restore_folder_item_view_state && folder_item_view_) {
  860. folder_item_view_->SetIconVisible(true);
  861. folder_item_view_->title()->SetEnabledColor(
  862. AppListColorProvider::Get()->GetAppListItemTextColor(
  863. /*is_in_folder=*/false));
  864. }
  865. folder_item_view_observer_.Reset();
  866. folder_item_view_ = nullptr;
  867. preferred_bounds_ = gfx::Rect();
  868. folder_item_icon_bounds_ = gfx::Rect();
  869. }
  870. void AppListFolderView::OnShowAnimationDone() {
  871. if (animation_done_test_callback_)
  872. std::move(animation_done_test_callback_).Run();
  873. }
  874. void AppListFolderView::OnHideAnimationDone(bool hide_for_reparent) {
  875. a11y_announcer_->AnnounceFolderClosed();
  876. // If the folder view is hiding for folder closure, reset the
  877. // folder state when the animations complete. Not resetting state
  878. // immediately so the folder view keeps tracking folder item
  879. // view's liveness (so it can reset animations if the folder item
  880. // view gets deleted).
  881. // If the view is hidden for reparent, the state will be cleared
  882. // when the reparent drag ends - close callback still needs to be called so
  883. // the root apps grid knows to update its state.
  884. if (!hide_for_reparent) {
  885. ResetState(
  886. /*reset_folder_item_view_state=*/true);
  887. } else {
  888. if (hide_callback_)
  889. std::move(hide_callback_).Run();
  890. }
  891. if (animation_done_test_callback_)
  892. std::move(animation_done_test_callback_).Run();
  893. }
  894. void AppListFolderView::UpdateHighlightBorder(bool show) {
  895. if (!show) {
  896. background_view_->SetBorder(nullptr);
  897. return;
  898. }
  899. background_view_->SetBorder(std::make_unique<views::HighlightBorder>(
  900. GetAppListConfig()->folder_background_radius(),
  901. views::HighlightBorder::Type::kHighlightBorder1,
  902. /*use_light_colors=*/!features::IsDarkLightModeEnabled()));
  903. }
  904. void AppListFolderView::UpdatePreferredBounds() {
  905. if (!folder_item_view_)
  906. return;
  907. // Calculate the folder icon's bounds relative to our parent.
  908. gfx::RectF rect(folder_item_view_->GetIconBounds());
  909. ConvertRectToTarget(folder_item_view_, parent(), &rect);
  910. gfx::Rect icon_bounds_in_container =
  911. parent()->GetMirroredRect(gfx::ToEnclosingRect(rect));
  912. // The opened folder view's center should try to overlap with the folder
  913. // item's center while it must fit within the bounds of the parent.
  914. preferred_bounds_ = gfx::Rect(GetPreferredSize());
  915. preferred_bounds_ += (icon_bounds_in_container.CenterPoint() -
  916. preferred_bounds_.CenterPoint());
  917. if (!bounding_box_.IsEmpty())
  918. preferred_bounds_.AdjustToFit(bounding_box_);
  919. // Calculate the folder icon's bounds relative to this view.
  920. folder_item_icon_bounds_ =
  921. icon_bounds_in_container - preferred_bounds_.OffsetFromOrigin();
  922. // Adjust folder item icon bounds for RTL (cannot use GetMirroredRect(), as
  923. // the current view bounds might not match the preferred bounds).
  924. if (base::i18n::IsRTL()) {
  925. folder_item_icon_bounds_.set_x(preferred_bounds_.width() -
  926. folder_item_icon_bounds_.x() -
  927. folder_item_icon_bounds_.width());
  928. }
  929. }
  930. void AppListFolderView::UpdateShadowForVirtualKeyboard() {
  931. shadow_->SetContentBounds(background_view_->layer()->bounds());
  932. }
  933. int AppListFolderView::GetYOffsetForFolder() {
  934. auto* const keyboard_controller = keyboard::KeyboardUIController::Get();
  935. if (!keyboard_controller->IsEnabled())
  936. return 0;
  937. // This view should be on top of on-screen keyboard to prevent the folder
  938. // title from being blocked.
  939. const gfx::Rect occluded_bounds =
  940. keyboard_controller->GetWorkspaceOccludedBoundsInScreen();
  941. if (!occluded_bounds.IsEmpty()) {
  942. gfx::Point keyboard_top_right = occluded_bounds.top_right();
  943. ConvertPointFromScreen(parent(), &keyboard_top_right);
  944. // Our final Y-Offset is determined by combining the space from the bottom
  945. // of the folder to the top of the keyboard, and the padding that should
  946. // exist between the keyboard and the folder bottom.
  947. // std::min() is used so that positive offsets are ignored.
  948. return std::min(keyboard_top_right.y() - kOnscreenKeyboardTopPadding -
  949. preferred_bounds_.bottom(),
  950. 0);
  951. }
  952. // If no offset is calculated above, then we need none.
  953. return 0;
  954. }
  955. bool AppListFolderView::IsAnimationRunning() const {
  956. for (auto& animation : folder_visibility_animations_) {
  957. if (animation->IsAnimationRunning())
  958. return true;
  959. }
  960. return false;
  961. }
  962. void AppListFolderView::SetBoundingBox(const gfx::Rect& bounding_box) {
  963. bounding_box_ = bounding_box;
  964. ShrinkGridTileMarginsWhenNeeded();
  965. }
  966. void AppListFolderView::SetAnimationDoneTestCallback(
  967. base::OnceClosure animation_done_callback) {
  968. DCHECK(!animation_done_callback || !animation_done_test_callback_);
  969. animation_done_test_callback_ = std::move(animation_done_callback);
  970. }
  971. void AppListFolderView::RecordAnimationSmoothness() {
  972. // RecordAnimationSmoothness is called when ContentsContainerAnimation
  973. // ends as well. Do not record show/hide metrics for that.
  974. if (show_hide_metrics_tracker_) {
  975. show_hide_metrics_tracker_->Stop();
  976. show_hide_metrics_tracker_.reset();
  977. }
  978. }
  979. void AppListFolderView::OnTabletModeChanged(bool started) {
  980. folder_header_view()->set_tablet_mode(started);
  981. if (page_switcher_)
  982. page_switcher_->set_is_tablet_mode(started);
  983. }
  984. void AppListFolderView::OnScrollEvent(ui::ScrollEvent* event) {
  985. items_grid_view_->HandleScrollFromParentView(
  986. gfx::Vector2d(event->x_offset(), event->y_offset()), event->type());
  987. event->SetHandled();
  988. }
  989. void AppListFolderView::OnMouseEvent(ui::MouseEvent* event) {
  990. if (event->type() == ui::ET_MOUSEWHEEL) {
  991. items_grid_view_->HandleScrollFromParentView(
  992. event->AsMouseWheelEvent()->offset(), ui::ET_MOUSEWHEEL);
  993. event->SetHandled();
  994. }
  995. }
  996. bool AppListFolderView::IsDragPointOutsideOfFolder(
  997. const gfx::Point& drag_point) {
  998. // Wait for the folder bound to stabilize before starting reparent drag.
  999. if (IsAnimationRunning())
  1000. return false;
  1001. gfx::Point drag_point_in_folder = drag_point;
  1002. views::View::ConvertPointToTarget(items_grid_view_, this,
  1003. &drag_point_in_folder);
  1004. return !GetLocalBounds().Contains(drag_point_in_folder);
  1005. }
  1006. // When user drags a folder item out of the folder boundary ink bubble, the
  1007. // folder view UI will be hidden, and switch back to top level AppsGridView.
  1008. // The dragged item will seamlessly move on the top level AppsGridView.
  1009. // In order to achieve the above, we keep the folder view and its child grid
  1010. // view visible with opacity 0, so that the drag_view_ on the hidden grid view
  1011. // will keep receiving mouse event. At the same time, we initiated a new
  1012. // drag_view_ in the top level grid view, and keep it moving with the hidden
  1013. // grid view's drag_view_, so that the dragged item can be engaged in drag and
  1014. // drop flow in the top level grid view. During the reparenting process, the
  1015. // drag_view_ in hidden grid view will dispatch the drag and drop event to
  1016. // the top level grid view, until the drag ends.
  1017. void AppListFolderView::ReparentItem(
  1018. AppsGridView::Pointer pointer,
  1019. AppListItemView* original_drag_view,
  1020. const gfx::Point& drag_point_in_folder_grid) {
  1021. // Convert the drag point relative to the root level AppsGridView.
  1022. gfx::Point to_root_level_grid = drag_point_in_folder_grid;
  1023. ConvertPointToTarget(items_grid_view_, root_apps_grid_view_,
  1024. &to_root_level_grid);
  1025. // Ensures the icon updates to reflect that the icon has been removed during
  1026. // the drag
  1027. folder_item_->NotifyOfDraggedItem(original_drag_view->item());
  1028. root_apps_grid_view_->InitiateDragFromReparentItemInRootLevelGridView(
  1029. pointer, original_drag_view, to_root_level_grid,
  1030. base::BindOnce(&AppListFolderView::CancelReparentDragFromRootGrid,
  1031. weak_ptr_factory_.GetWeakPtr()));
  1032. folder_controller_->ReparentFolderItemTransit(folder_item_);
  1033. }
  1034. void AppListFolderView::DispatchDragEventForReparent(
  1035. AppsGridView::Pointer pointer,
  1036. const gfx::Point& drag_point_in_folder_grid) {
  1037. gfx::Point drag_point_in_root_grid = drag_point_in_folder_grid;
  1038. // Temporarily reset the transform of the contents container so that the point
  1039. // can be correctly converted to the root grid's coordinates.
  1040. gfx::Transform original_transform = contents_container_->GetTransform();
  1041. contents_container_->SetTransform(gfx::Transform());
  1042. ConvertPointToTarget(items_grid_view_, root_apps_grid_view_,
  1043. &drag_point_in_root_grid);
  1044. contents_container_->SetTransform(original_transform);
  1045. root_apps_grid_view_->UpdateDragFromReparentItem(pointer,
  1046. drag_point_in_root_grid);
  1047. }
  1048. void AppListFolderView::DispatchEndDragEventForReparent(
  1049. bool events_forwarded_to_drag_drop_host,
  1050. bool cancel_drag,
  1051. std::unique_ptr<AppDragIconProxy> drag_icon_proxy) {
  1052. folder_item_->NotifyOfDraggedItem(nullptr);
  1053. folder_controller_->ReparentDragEnded();
  1054. // Cache `folder_item_view_`, as it will get reset in `HideViewImmediately()`.
  1055. AppListItemView* const folder_item_view = folder_item_view_;
  1056. // The view was not hidden in order to keeping receiving mouse events. Hide it
  1057. // now as the reparenting ended.
  1058. HideViewImmediately();
  1059. root_apps_grid_view_->EndDragFromReparentItemInRootLevel(
  1060. folder_item_view, events_forwarded_to_drag_drop_host, cancel_drag,
  1061. std::move(drag_icon_proxy));
  1062. }
  1063. void AppListFolderView::HideViewImmediately() {
  1064. SetVisible(false);
  1065. ResetState(/*restore_folder_item_view_state=*/true);
  1066. }
  1067. void AppListFolderView::ResetItemsGridForClose() {
  1068. if (items_grid_view()->has_dragged_item())
  1069. items_grid_view()->CancelDragWithNoDropAnimation();
  1070. items_grid_view()->ClearSelectedView();
  1071. }
  1072. void AppListFolderView::CloseFolderPage() {
  1073. DVLOG(1) << __FUNCTION__;
  1074. // When a folder closes only show the selection highlight if there was already
  1075. // one showing, or if the user is using ChromeVox (spoken feedback). In the
  1076. // latter case it makes the close folder announcement more natural.
  1077. const bool select_folder =
  1078. items_grid_view()->has_selected_view() || IsSpokenFeedbackEnabled();
  1079. ResetItemsGridForClose();
  1080. folder_controller_->ShowApps(folder_item_view_, select_folder);
  1081. }
  1082. void AppListFolderView::FocusNameInput() {
  1083. folder_header_view_->SetTextFocus();
  1084. }
  1085. void AppListFolderView::FocusFirstItem(bool silent) {
  1086. DVLOG(1) << __FUNCTION__;
  1087. AppListItemView* first_item_view =
  1088. items_grid_view()->view_model()->view_at(0);
  1089. if (silent) {
  1090. first_item_view->SilentlyRequestFocus();
  1091. } else {
  1092. first_item_view->RequestFocus();
  1093. }
  1094. }
  1095. bool AppListFolderView::IsOEMFolder() const {
  1096. return folder_item_->folder_type() == AppListFolderItem::FOLDER_TYPE_OEM;
  1097. }
  1098. void AppListFolderView::HandleKeyboardReparent(AppListItemView* reparented_view,
  1099. ui::KeyboardCode key_code) {
  1100. folder_controller_->ReparentFolderItemTransit(folder_item_);
  1101. // Notify the root apps grid that folder is closing before handling keyboard
  1102. // reparent, to match general flow during drag reparent (where close callback
  1103. // gets called before reparenting the dragged view). This ensures that items
  1104. // are in their ideal locations when the item gets reparented (i.e. that the
  1105. // folder item's slot is not locked to the folder item's initial location).
  1106. if (hide_callback_)
  1107. std::move(hide_callback_).Run();
  1108. root_apps_grid_view_->HandleKeyboardReparent(reparented_view,
  1109. folder_item_view_, key_code);
  1110. folder_controller_->ReparentDragEnded();
  1111. HideViewImmediately();
  1112. }
  1113. bool AppListFolderView::IsPointWithinPageFlipBuffer(
  1114. const gfx::Point& point) const {
  1115. // The page flip buffer is anywhere within the bounds of the
  1116. // |contents_container_|.
  1117. gfx::Point point_in_parent = point;
  1118. ConvertPointToTarget(items_grid_view_, contents_container_, &point_in_parent);
  1119. return GetContentsBounds().Contains(point_in_parent);
  1120. }
  1121. bool AppListFolderView::IsPointWithinBottomDragBuffer(
  1122. const gfx::Point& point,
  1123. int page_flip_zone_size) const {
  1124. // Folders page horizontally and do not have a bottom drag buffer.
  1125. return false;
  1126. }
  1127. void AppListFolderView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
  1128. node_data->role = ax::mojom::Role::kGenericContainer;
  1129. if (shown_) {
  1130. node_data->AddState(ax::mojom::State::kExpanded);
  1131. } else {
  1132. node_data->AddState(ax::mojom::State::kCollapsed);
  1133. }
  1134. }
  1135. void AppListFolderView::OnGestureEvent(ui::GestureEvent* event) {
  1136. // Capture scroll events so they don't bubble up to the apps container, where
  1137. // they may cause the root apps grid view to scroll, or get translated into
  1138. // apps grid view drag.
  1139. if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN)
  1140. event->SetHandled();
  1141. }
  1142. void AppListFolderView::SetItemName(AppListFolderItem* item,
  1143. const std::string& name) {
  1144. AppListModelProvider::Get()->model()->delegate()->RequestFolderRename(
  1145. folder_item_->id(), name);
  1146. }
  1147. const AppListConfig* AppListFolderView::GetAppListConfig() const {
  1148. return items_grid_view_->app_list_config();
  1149. }
  1150. ui::Compositor* AppListFolderView::GetCompositor() {
  1151. return GetWidget()->GetCompositor();
  1152. }
  1153. void AppListFolderView::CancelReparentDragFromRootGrid() {
  1154. items_grid_view_->EndDrag(/*cancel=*/true);
  1155. }
  1156. void AppListFolderView::ShrinkGridTileMarginsWhenNeeded() {
  1157. // Productivity launcher uses scrollable grid for folders, which handles the
  1158. // case where the items grid does not fit into bounds provided by the folder
  1159. // bounding box.
  1160. if (is_productivity_launcher_enabled_)
  1161. return;
  1162. if (bounding_box_.IsEmpty() || !GetAppListConfig())
  1163. return;
  1164. // Calculate the expected folder height when it has the max possible number of
  1165. // rows. The margins should be shrunk if this height does not fit within
  1166. // the bounding box.
  1167. const int max_folder_height =
  1168. folder_header_view_->GetPreferredSize().height() + kFolderHeaderPadding +
  1169. GetAppListConfig()->grid_tile_height() * kMaxPagedFolderRows +
  1170. (kMaxPagedFolderRows - 1) * kTileSpacingInFolder;
  1171. const bool shrink_margins = max_folder_height > bounding_box_.height();
  1172. items_grid_view_->SetFixedTilePadding(
  1173. (shrink_margins ? 0 : kTileSpacingInFolder) / 2,
  1174. (shrink_margins ? 0 : kTileSpacingInFolder) / 2);
  1175. }
  1176. BEGIN_METADATA(AppListFolderView, views::View)
  1177. END_METADATA
  1178. } // namespace ash