pinned_files_section.cc 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. // Copyright 2020 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/system/holding_space/pinned_files_section.h"
  5. #include "ash/bubble/bubble_utils.h"
  6. #include "ash/bubble/simple_grid_layout.h"
  7. #include "ash/public/cpp/holding_space/holding_space_client.h"
  8. #include "ash/public/cpp/holding_space/holding_space_constants.h"
  9. #include "ash/public/cpp/holding_space/holding_space_controller.h"
  10. #include "ash/public/cpp/holding_space/holding_space_item.h"
  11. #include "ash/public/cpp/holding_space/holding_space_metrics.h"
  12. #include "ash/public/cpp/holding_space/holding_space_prefs.h"
  13. #include "ash/resources/vector_icons/vector_icons.h"
  14. #include "ash/session/session_controller_impl.h"
  15. #include "ash/shell.h"
  16. #include "ash/strings/grit/ash_strings.h"
  17. #include "ash/style/ash_color_provider.h"
  18. #include "ash/style/style_util.h"
  19. #include "ash/system/holding_space/holding_space_item_chip_view.h"
  20. #include "ash/system/holding_space/holding_space_view_delegate.h"
  21. #include "base/bind.h"
  22. #include "base/callback_helpers.h"
  23. #include "ui/base/l10n/l10n_util.h"
  24. #include "ui/color/color_id.h"
  25. #include "ui/compositor/layer.h"
  26. #include "ui/gfx/paint_vector_icon.h"
  27. #include "ui/views/accessibility/view_accessibility.h"
  28. #include "ui/views/animation/ink_drop.h"
  29. #include "ui/views/background.h"
  30. #include "ui/views/controls/button/button.h"
  31. #include "ui/views/controls/focus_ring.h"
  32. #include "ui/views/controls/highlight_path_generator.h"
  33. #include "ui/views/controls/image_view.h"
  34. #include "ui/views/controls/label.h"
  35. #include "ui/views/layout/box_layout.h"
  36. namespace ash {
  37. namespace {
  38. // Appearance.
  39. constexpr int kFilesAppChipChildSpacing = 8;
  40. constexpr int kFilesAppChipHeight = 32;
  41. constexpr int kFilesAppChipIconSize = 20;
  42. constexpr auto kFilesAppChipInsets = gfx::Insets::TLBR(0, 8, 0, 16);
  43. constexpr int kPlaceholderChildSpacing = 16;
  44. // FilesAppChip ----------------------------------------------------------------
  45. class FilesAppChip : public views::Button {
  46. public:
  47. explicit FilesAppChip(views::Button::PressedCallback pressed_callback)
  48. : views::Button(std::move(pressed_callback)) {
  49. Init();
  50. }
  51. FilesAppChip(const FilesAppChip&) = delete;
  52. FilesAppChip& operator=(const FilesAppChip&) = delete;
  53. ~FilesAppChip() override = default;
  54. private:
  55. // views::Button:
  56. gfx::Size CalculatePreferredSize() const override {
  57. const int width = views::Button::CalculatePreferredSize().width();
  58. return gfx::Size(width, GetHeightForWidth(width));
  59. }
  60. int GetHeightForWidth(int width) const override {
  61. return kFilesAppChipHeight;
  62. }
  63. void OnThemeChanged() override {
  64. views::Button::OnThemeChanged();
  65. AshColorProvider* const ash_color_provider = AshColorProvider::Get();
  66. // Background.
  67. SetBackground(views::CreateRoundedRectBackground(
  68. ash_color_provider->GetControlsLayerColor(
  69. AshColorProvider::ControlsLayerType::
  70. kControlBackgroundColorInactive),
  71. kFilesAppChipHeight / 2));
  72. // Focus ring.
  73. views::FocusRing::Get(this)->SetColorId(ui::kColorAshFocusRing);
  74. // Ink drop.
  75. StyleUtil::ConfigureInkDropAttributes(
  76. this, StyleUtil::kBaseColor | StyleUtil::kInkDropOpacity |
  77. StyleUtil::kHighlightOpacity);
  78. }
  79. void Init() {
  80. SetAccessibleName(l10n_util::GetStringUTF16(
  81. IDS_ASH_HOLDING_SPACE_PINNED_FILES_APP_CHIP_TEXT));
  82. SetID(kHoldingSpaceFilesAppChipId);
  83. // Ink drop.
  84. views::InkDrop::Get(this)->SetMode(views::InkDropHost::InkDropMode::ON);
  85. views::InstallRoundRectHighlightPathGenerator(this, gfx::Insets(),
  86. kFilesAppChipHeight / 2);
  87. // Layout.
  88. auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
  89. views::BoxLayout::Orientation::kHorizontal, kFilesAppChipInsets,
  90. kFilesAppChipChildSpacing));
  91. layout->set_cross_axis_alignment(
  92. views::BoxLayout::CrossAxisAlignment::kCenter);
  93. // Icon.
  94. auto* icon = AddChildView(std::make_unique<views::ImageView>());
  95. icon->SetImage(gfx::CreateVectorIcon(kFilesAppIcon, kFilesAppChipIconSize,
  96. gfx::kPlaceholderColor));
  97. // Label.
  98. auto* label = AddChildView(
  99. bubble_utils::CreateLabel(bubble_utils::LabelStyle::kChipTitle));
  100. label->SetText(l10n_util::GetStringUTF16(
  101. IDS_ASH_HOLDING_SPACE_PINNED_FILES_APP_CHIP_TEXT));
  102. layout->SetFlexForView(label, 1);
  103. }
  104. };
  105. } // namespace
  106. // PinnedFilesSection ----------------------------------------------------------
  107. PinnedFilesSection::PinnedFilesSection(HoldingSpaceViewDelegate* delegate)
  108. : HoldingSpaceItemViewsSection(delegate,
  109. /*supported_types=*/
  110. {HoldingSpaceItem::Type::kPinnedFile},
  111. /*max_count=*/absl::nullopt) {}
  112. PinnedFilesSection::~PinnedFilesSection() = default;
  113. // static
  114. bool PinnedFilesSection::ShouldShowPlaceholder(PrefService* prefs) {
  115. // The placeholder should only be shown if:
  116. // * a holding space item has been added at some point in time,
  117. // * a holding space item has *never* been pinned, and
  118. // * the user has never pressed the Files app chip in the placeholder.
  119. return holding_space_prefs::GetTimeOfFirstAdd(prefs) &&
  120. !holding_space_prefs::GetTimeOfFirstPin(prefs) &&
  121. !holding_space_prefs::GetTimeOfFirstFilesAppChipPress(prefs);
  122. }
  123. const char* PinnedFilesSection::GetClassName() const {
  124. return "PinnedFilesSection";
  125. }
  126. gfx::Size PinnedFilesSection::GetMinimumSize() const {
  127. // The pinned files section is scrollable so can be laid out smaller than its
  128. // preferred size if there is insufficient layout space available.
  129. return gfx::Size();
  130. }
  131. std::unique_ptr<views::View> PinnedFilesSection::CreateHeader() {
  132. auto header = bubble_utils::CreateLabel(
  133. bubble_utils::LabelStyle::kHeader,
  134. l10n_util::GetStringUTF16(IDS_ASH_HOLDING_SPACE_PINNED_TITLE));
  135. header->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
  136. header->SetPaintToLayer();
  137. header->layer()->SetFillsBoundsOpaquely(false);
  138. return header;
  139. }
  140. std::unique_ptr<views::View> PinnedFilesSection::CreateContainer() {
  141. auto container = std::make_unique<views::View>();
  142. container->SetLayoutManager(std::make_unique<SimpleGridLayout>(
  143. kHoldingSpaceChipCountPerRow,
  144. /*column_spacing=*/kHoldingSpaceSectionContainerChildSpacing,
  145. /*row_spacing=*/kHoldingSpaceSectionContainerChildSpacing));
  146. return container;
  147. }
  148. std::unique_ptr<HoldingSpaceItemView> PinnedFilesSection::CreateView(
  149. const HoldingSpaceItem* item) {
  150. // When `PinnedFilesSection::CreateView()` is called it implies that the user
  151. // has at some point in time pinned a file to holding space. That being the
  152. // case, the placeholder is no longer relevant and can be destroyed.
  153. DestroyPlaceholder();
  154. return std::make_unique<HoldingSpaceItemChipView>(delegate(), item);
  155. }
  156. std::unique_ptr<views::View> PinnedFilesSection::CreatePlaceholder() {
  157. auto* prefs = Shell::Get()->session_controller()->GetActivePrefService();
  158. if (!PinnedFilesSection::ShouldShowPlaceholder(prefs))
  159. return nullptr;
  160. auto placeholder = std::make_unique<views::View>();
  161. placeholder->SetPaintToLayer();
  162. placeholder->layer()->SetFillsBoundsOpaquely(false);
  163. auto* layout =
  164. placeholder->SetLayoutManager(std::make_unique<views::BoxLayout>(
  165. views::BoxLayout::Orientation::kVertical, gfx::Insets(),
  166. kPlaceholderChildSpacing));
  167. layout->set_cross_axis_alignment(
  168. views::BoxLayout::CrossAxisAlignment::kStart);
  169. // Prompt.
  170. auto* prompt = placeholder->AddChildView(bubble_utils::CreateLabel(
  171. bubble_utils::LabelStyle::kBody,
  172. l10n_util::GetStringUTF16(IDS_ASH_HOLDING_SPACE_PINNED_EMPTY_PROMPT)));
  173. prompt->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
  174. prompt->SetMultiLine(true);
  175. // Files app chip.
  176. placeholder->AddChildView(std::make_unique<FilesAppChip>(base::BindRepeating(
  177. &PinnedFilesSection::OnFilesAppChipPressed, base::Unretained(this))));
  178. return placeholder;
  179. }
  180. void PinnedFilesSection::OnFilesAppChipPressed(const ui::Event& event) {
  181. holding_space_metrics::RecordFilesAppChipAction(
  182. holding_space_metrics::FilesAppChipAction::kClick);
  183. // NOTE: This no-ops if the Files app chip was previously pressed.
  184. holding_space_prefs::MarkTimeOfFirstFilesAppChipPress(
  185. Shell::Get()->session_controller()->GetActivePrefService());
  186. HoldingSpaceController::Get()->client()->OpenMyFiles(base::DoNothing());
  187. // Once the user has pressed the Files app chip, the placeholder should no
  188. // longer be displayed. This is accomplished by destroying it. If the holding
  189. // space model is empty, the holding space tray will also need to update its
  190. // visibility to become hidden.
  191. DestroyPlaceholder();
  192. delegate()->UpdateTrayVisibility();
  193. }
  194. } // namespace ash