continue_task_view.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. // Copyright 2021 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/continue_task_view.h"
  5. #include <algorithm>
  6. #include <string>
  7. #include <utility>
  8. #include "ash/app_list/app_list_metrics.h"
  9. #include "ash/app_list/app_list_util.h"
  10. #include "ash/app_list/app_list_view_delegate.h"
  11. #include "ash/app_list/model/search/search_result.h"
  12. #include "ash/bubble/bubble_utils.h"
  13. #include "ash/constants/ash_features.h"
  14. #include "ash/public/cpp/app_list/app_list_features.h"
  15. #include "ash/public/cpp/style/color_provider.h"
  16. #include "ash/resources/vector_icons/vector_icons.h"
  17. #include "ash/session/session_controller_impl.h"
  18. #include "ash/shell.h"
  19. #include "ash/strings/grit/ash_strings.h"
  20. #include "ash/style/style_util.h"
  21. #include "base/bind.h"
  22. #include "base/strings/string_util.h"
  23. #include "extensions/common/constants.h"
  24. #include "ui/base/l10n/l10n_util.h"
  25. #include "ui/base/metadata/metadata_impl_macros.h"
  26. #include "ui/base/models/menu_separator_types.h"
  27. #include "ui/base/models/simple_menu_model.h"
  28. #include "ui/color/color_id.h"
  29. #include "ui/compositor/layer.h"
  30. #include "ui/gfx/image/image_skia_operations.h"
  31. #include "ui/views/accessibility/view_accessibility.h"
  32. #include "ui/views/animation/ink_drop.h"
  33. #include "ui/views/background.h"
  34. #include "ui/views/border.h"
  35. #include "ui/views/controls/highlight_path_generator.h"
  36. #include "ui/views/controls/image_view.h"
  37. #include "ui/views/controls/label.h"
  38. #include "ui/views/controls/menu/menu_runner.h"
  39. #include "ui/views/highlight_border.h"
  40. #include "ui/views/layout/box_layout.h"
  41. #include "ui/views/layout/flex_layout.h"
  42. #include "ui/views/vector_icons.h"
  43. namespace ash {
  44. namespace {
  45. constexpr int kIconSize = 20;
  46. constexpr int kCircleRadius = 18;
  47. constexpr int kBetweenChildPadding = 16;
  48. constexpr auto kInteriorMarginClamshell = gfx::Insets::TLBR(7, 8, 7, 16);
  49. constexpr auto kInteriorMarginTablet = gfx::Insets::TLBR(13, 16, 13, 20);
  50. constexpr int kViewCornerRadiusClamshell = 8;
  51. constexpr int kViewCornerRadiusTablet = 20;
  52. constexpr int kTaskMinWidth = 204;
  53. constexpr int kTaskMaxWidth = 264;
  54. gfx::ImageSkia CreateIconWithCircleBackground(
  55. const gfx::ImageSkia& icon,
  56. ColorProvider::ControlsLayerType color_id) {
  57. return gfx::ImageSkiaOperations::CreateImageWithCircleBackground(
  58. kCircleRadius, ColorProvider::Get()->GetControlsLayerColor(color_id),
  59. icon);
  60. }
  61. int GetCornerRadius(bool tablet_mode) {
  62. return tablet_mode ? kViewCornerRadiusTablet : kViewCornerRadiusClamshell;
  63. }
  64. } // namespace
  65. ContinueTaskView::ContinueTaskView(AppListViewDelegate* view_delegate,
  66. bool tablet_mode)
  67. : view_delegate_(view_delegate), is_tablet_mode_(tablet_mode) {
  68. SetPaintToLayer();
  69. layer()->SetFillsBoundsOpaquely(false);
  70. SetFocusBehavior(FocusBehavior::ALWAYS);
  71. SetCallback(base::BindRepeating(&ContinueTaskView::OnButtonPressed,
  72. base::Unretained(this)));
  73. auto ink_drop_highlight_path =
  74. std::make_unique<views::RoundRectHighlightPathGenerator>(
  75. gfx::Insets(), GetCornerRadius(tablet_mode));
  76. ink_drop_highlight_path->set_use_contents_bounds(true);
  77. ink_drop_highlight_path->set_use_mirrored_rect(true);
  78. views::HighlightPathGenerator::Install(this,
  79. std::move(ink_drop_highlight_path));
  80. SetInstallFocusRingOnFocus(true);
  81. views::FocusRing::Get(this)->SetColorId(ui::kColorAshFocusRing);
  82. SetFocusPainter(nullptr);
  83. views::InkDrop::Get(this)->SetMode(views::InkDropHost::InkDropMode::ON);
  84. views::InkDrop::Get(this)->GetInkDrop()->SetShowHighlightOnHover(false);
  85. SetHasInkDropActionOnClick(true);
  86. SetShowInkDropWhenHotTracked(false);
  87. StyleUtil::ConfigureInkDropAttributes(
  88. this, StyleUtil::kBaseColor | StyleUtil::kInkDropOpacity);
  89. UpdateStyleForTabletMode();
  90. auto* layout_manager = SetLayoutManager(std::make_unique<views::BoxLayout>(
  91. views::BoxLayout::Orientation::kHorizontal,
  92. tablet_mode ? kInteriorMarginTablet : kInteriorMarginClamshell,
  93. kBetweenChildPadding));
  94. layout_manager->set_cross_axis_alignment(
  95. views::BoxLayout::CrossAxisAlignment::kCenter);
  96. GetViewAccessibility().OverrideRole(ax::mojom::Role::kListItem);
  97. icon_ = AddChildView(std::make_unique<views::ImageView>());
  98. icon_->SetVerticalAlignment(views::ImageView::Alignment::kCenter);
  99. icon_->SetHorizontalAlignment(views::ImageView::Alignment::kCenter);
  100. auto* label_container = AddChildView(std::make_unique<views::View>());
  101. label_container->SetLayoutManager(std::make_unique<views::BoxLayout>(
  102. views::BoxLayout::Orientation::kVertical));
  103. title_ = label_container->AddChildView(
  104. std::make_unique<views::Label>(std::u16string()));
  105. title_->SetAccessibleName(std::u16string());
  106. title_->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
  107. title_->SetElideBehavior(gfx::ElideBehavior::ELIDE_TAIL);
  108. subtitle_ = label_container->AddChildView(
  109. std::make_unique<views::Label>(std::u16string()));
  110. subtitle_->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
  111. subtitle_->SetElideBehavior(gfx::ElideBehavior::ELIDE_MIDDLE);
  112. layout_manager->SetFlexForView(label_container, 1);
  113. UpdateResult();
  114. set_context_menu_controller(this);
  115. }
  116. ContinueTaskView::~ContinueTaskView() {}
  117. void ContinueTaskView::OnThemeChanged() {
  118. views::View::OnThemeChanged();
  119. bubble_utils::ApplyStyle(title_, bubble_utils::LabelStyle::kBody);
  120. bubble_utils::ApplyStyle(subtitle_, bubble_utils::LabelStyle::kSubtitle);
  121. UpdateIcon();
  122. UpdateStyleForTabletMode();
  123. }
  124. gfx::Size ContinueTaskView::GetMaximumSize() const {
  125. return gfx::Size(kTaskMaxWidth,
  126. GetLayoutManager()->GetPreferredSize(this).height());
  127. }
  128. gfx::Size ContinueTaskView::GetMinimumSize() const {
  129. return gfx::Size(kTaskMinWidth,
  130. GetLayoutManager()->GetPreferredSize(this).height());
  131. }
  132. gfx::Size ContinueTaskView::CalculatePreferredSize() const {
  133. return GetMinimumSize();
  134. }
  135. void ContinueTaskView::OnButtonPressed(const ui::Event& event) {
  136. views::InkDrop::Get(this)->GetInkDrop()->AnimateToState(
  137. views::InkDropState::ACTION_TRIGGERED);
  138. OpenResult(event.flags());
  139. }
  140. void ContinueTaskView::UpdateIcon() {
  141. if (!result()) {
  142. icon_->SetImage(gfx::ImageSkia());
  143. return;
  144. }
  145. const gfx::ImageSkia& icon = result()->chip_icon();
  146. icon_->SetImage(CreateIconWithCircleBackground(
  147. icon.size() == GetIconSize()
  148. ? icon
  149. : gfx::ImageSkiaOperations::CreateResizedImage(
  150. icon, skia::ImageOperations::RESIZE_BEST, GetIconSize()),
  151. result()->result_type() == AppListSearchResultType::kZeroStateHelpApp
  152. ? ColorProvider::ControlsLayerType::kControlBackgroundColorActive
  153. : ColorProvider::ControlsLayerType::kControlBackgroundColorInactive));
  154. }
  155. gfx::Size ContinueTaskView::GetIconSize() const {
  156. return gfx::Size(kIconSize, kIconSize);
  157. }
  158. void ContinueTaskView::OnMetadataChanged() {
  159. UpdateResult();
  160. }
  161. void ContinueTaskView::UpdateResult() {
  162. SetVisible(!!result());
  163. views::InkDrop::Get(this)->GetInkDrop()->AnimateToState(
  164. views::InkDropState::HIDDEN);
  165. CloseContextMenu();
  166. UpdateIcon();
  167. if (!result()) {
  168. title_->SetText(std::u16string());
  169. subtitle_->SetText(std::u16string());
  170. GetViewAccessibility().OverrideName(
  171. std::u16string(), ax::mojom::NameFrom::kAttributeExplicitlyEmpty);
  172. return;
  173. }
  174. title_->SetText(result()->title());
  175. subtitle_->SetText(result()->details());
  176. GetViewAccessibility().OverrideName(result()->title() + u" " +
  177. result()->details());
  178. }
  179. void ContinueTaskView::OnResultDestroying() {
  180. SetResult(nullptr);
  181. }
  182. void ContinueTaskView::SetResult(SearchResult* result) {
  183. if (result_ == result)
  184. return;
  185. search_result_observation_.Reset();
  186. result_ = result;
  187. if (result_) {
  188. search_result_observation_.Observe(result_);
  189. UpdateResult();
  190. }
  191. }
  192. void ContinueTaskView::ShowContextMenuForViewImpl(
  193. views::View* source,
  194. const gfx::Point& point,
  195. ui::MenuSourceType source_type) {
  196. // May be null if the result got reset, and the task view is animating out.
  197. if (!result())
  198. return;
  199. int run_types = views::MenuRunner::USE_ASH_SYS_UI_LAYOUT |
  200. views::MenuRunner::CONTEXT_MENU |
  201. views::MenuRunner::FIXED_ANCHOR;
  202. context_menu_runner_ =
  203. std::make_unique<views::MenuRunner>(BuildMenuModel(), run_types);
  204. context_menu_runner_->RunMenuAt(
  205. source->GetWidget(), nullptr /*button_controller*/,
  206. source->GetBoundsInScreen(), views::MenuAnchorPosition::kBubbleTopRight,
  207. source_type);
  208. views::InkDrop::Get(this)->GetInkDrop()->SnapToActivated();
  209. }
  210. void ContinueTaskView::ExecuteCommand(int command_id, int event_flags) {
  211. CloseContextMenu();
  212. switch (command_id) {
  213. case ContinueTaskCommandId::kOpenResult:
  214. OpenResult(event_flags);
  215. break;
  216. case ContinueTaskCommandId::kRemoveResult:
  217. RemoveResult();
  218. break;
  219. case ContinueTaskCommandId::kHideContinueSection:
  220. view_delegate_->SetHideContinueSection(true);
  221. break;
  222. default:
  223. NOTREACHED();
  224. }
  225. }
  226. ui::SimpleMenuModel* ContinueTaskView::BuildMenuModel() {
  227. context_menu_model_ = std::make_unique<ui::SimpleMenuModel>(this);
  228. context_menu_model_->AddItemWithIcon(
  229. ContinueTaskCommandId::kOpenResult,
  230. l10n_util::GetStringUTF16(
  231. IDS_ASH_LAUNCHER_CONTINUE_SECTION_CONTEXT_MENU_OPEN),
  232. ui::ImageModel::FromVectorIcon(kLaunchIcon,
  233. ui::kColorAshSystemUIMenuIcon));
  234. context_menu_model_->AddItemWithIcon(
  235. ContinueTaskCommandId::kRemoveResult,
  236. l10n_util::GetStringUTF16(
  237. IDS_ASH_LAUNCHER_CONTINUE_SECTION_CONTEXT_MENU_REMOVE),
  238. ui::ImageModel::FromVectorIcon(kRemoveOutlineIcon,
  239. ui::kColorAshSystemUIMenuIcon));
  240. if (features::IsLauncherHideContinueSectionEnabled() &&
  241. Shell::Get()->IsInTabletMode()) {
  242. context_menu_model_->AddSeparator(ui::NORMAL_SEPARATOR);
  243. context_menu_model_->AddItemWithIcon(
  244. ContinueTaskCommandId::kHideContinueSection,
  245. l10n_util::GetStringUTF16(IDS_ASH_LAUNCHER_HIDE_CONTINUE_SECTION),
  246. ui::ImageModel::FromVectorIcon(kLauncherHideContinueSectionIcon,
  247. ui::kColorAshSystemUIMenuIcon));
  248. }
  249. return context_menu_model_.get();
  250. }
  251. void ContinueTaskView::MenuClosed(ui::SimpleMenuModel* menu) {
  252. views::InkDrop::Get(this)->GetInkDrop()->AnimateToState(
  253. views::InkDropState::HIDDEN);
  254. }
  255. void ContinueTaskView::OpenResult(int event_flags) {
  256. // May be null if the result got reset, and the task view is animating out.
  257. if (!result())
  258. return;
  259. view_delegate_->OpenSearchResult(
  260. result()->id(), event_flags,
  261. AppListLaunchedFrom::kLaunchedFromContinueTask,
  262. AppListLaunchType::kSearchResult, index_in_container(),
  263. false /* launch_as_default */);
  264. }
  265. ContinueTaskView::TaskResultType ContinueTaskView::GetTaskResultType() {
  266. switch (result()->result_type()) {
  267. case AppListSearchResultType::kFileChip:
  268. case AppListSearchResultType::kZeroStateFile:
  269. return TaskResultType::kLocalFile;
  270. case AppListSearchResultType::kDriveChip:
  271. case AppListSearchResultType::kZeroStateDrive:
  272. return TaskResultType::kDriveFile;
  273. default:
  274. NOTREACHED();
  275. }
  276. return TaskResultType::kUnknown;
  277. }
  278. void ContinueTaskView::RemoveResult() {
  279. // May be null if the result got reset, and the task view is animating out.
  280. if (!result())
  281. return;
  282. LogMetricsOnResultRemoved();
  283. view_delegate_->InvokeSearchResultAction(result()->id(),
  284. SearchResultActionType::kRemove);
  285. }
  286. bool ContinueTaskView::IsMenuShowing() const {
  287. return context_menu_runner_ && context_menu_runner_->IsRunning();
  288. }
  289. void ContinueTaskView::CloseContextMenu() {
  290. if (!IsMenuShowing())
  291. return;
  292. context_menu_runner_->Cancel();
  293. }
  294. void ContinueTaskView::UpdateStyleForTabletMode() {
  295. // Do nothing if the view is not in tablet mode.
  296. if (!is_tablet_mode_)
  297. return;
  298. layer()->SetBackgroundBlur(ColorProvider::kBackgroundBlurSigma);
  299. layer()->SetBackdropFilterQuality(ColorProvider::kBackgroundBlurQuality);
  300. layer()->SetRoundedCornerRadius(
  301. gfx::RoundedCornersF(GetCornerRadius(/*tablet_mode=*/true)));
  302. SetBackground(
  303. views::CreateSolidBackground(ColorProvider::Get()->GetBaseLayerColor(
  304. ColorProvider::BaseLayerType::kTransparent60)));
  305. SetBorder(std::make_unique<views::HighlightBorder>(
  306. GetCornerRadius(/*tablet_mode=*/true),
  307. views::HighlightBorder::Type::kHighlightBorder2,
  308. /*use_light_colors=*/false));
  309. }
  310. void ContinueTaskView::LogMetricsOnResultRemoved() {
  311. RecordCumulativeContinueSectionResultRemovedNumber();
  312. base::UmaHistogramEnumeration("Apps.AppList.Search.ContinueResultRemoved",
  313. GetTaskResultType(), TaskResultType::kMaxValue);
  314. }
  315. BEGIN_METADATA(ContinueTaskView, views::View)
  316. END_METADATA
  317. } // namespace ash