search_result_suggestion_chip_view.cc 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. // Copyright 2018 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/search_result_suggestion_chip_view.h"
  5. #include <algorithm>
  6. #include <utility>
  7. #include "ash/app_list/app_list_metrics.h"
  8. #include "ash/app_list/app_list_view_delegate.h"
  9. #include "ash/app_list/model/search/search_result.h"
  10. #include "ash/constants/ash_features.h"
  11. #include "ash/public/cpp/app_list/app_list_color_provider.h"
  12. #include "ash/public/cpp/app_list/app_list_config.h"
  13. #include "ash/public/cpp/app_list/app_list_types.h"
  14. #include "ash/public/cpp/app_list/internal_app_id_constants.h"
  15. #include "ash/strings/grit/ash_strings.h"
  16. #include "base/bind.h"
  17. #include "base/metrics/histogram_functions.h"
  18. #include "base/metrics/histogram_macros.h"
  19. #include "base/metrics/user_metrics.h"
  20. #include "ui/accessibility/ax_enums.mojom.h"
  21. #include "ui/accessibility/ax_node_data.h"
  22. #include "ui/base/l10n/l10n_util.h"
  23. #include "ui/color/color_id.h"
  24. #include "ui/gfx/canvas.h"
  25. #include "ui/gfx/color_palette.h"
  26. #include "ui/views/accessibility/accessibility_paint_checks.h"
  27. #include "ui/views/animation/flood_fill_ink_drop_ripple.h"
  28. #include "ui/views/animation/ink_drop.h"
  29. #include "ui/views/animation/ink_drop_impl.h"
  30. #include "ui/views/controls/focus_ring.h"
  31. #include "ui/views/controls/highlight_path_generator.h"
  32. #include "ui/views/controls/image_view.h"
  33. #include "ui/views/controls/label.h"
  34. #include "ui/views/layout/box_layout.h"
  35. namespace ash {
  36. namespace {
  37. constexpr int kMaxTextWidth = 192;
  38. constexpr int kBlurRadius = 5;
  39. constexpr int kIconMarginDip = 8;
  40. constexpr int kPaddingDip = 16;
  41. constexpr int kPreferredHeightDip = 32;
  42. // Records an app being launched.
  43. void LogAppLaunch(int index_in_container) {
  44. DCHECK_GE(index_in_container, 0);
  45. base::UmaHistogramSparse("Apps.AppListSuggestedChipLaunched",
  46. index_in_container);
  47. base::RecordAction(base::UserMetricsAction("AppList_OpenSuggestedApp"));
  48. }
  49. // Copied from AppListColorProvider.
  50. bool ShouldUseDarkLightColors() {
  51. return features::IsDarkLightModeEnabled() ||
  52. features::IsProductivityLauncherEnabled();
  53. }
  54. } // namespace
  55. SearchResultSuggestionChipView::SearchResultSuggestionChipView(
  56. AppListViewDelegate* view_delegate)
  57. : focus_ring_color_(ShouldUseDarkLightColors()
  58. ? ui::kColorAshFocusRing
  59. : ui::kColorAshAppListFocusRingCompat),
  60. view_delegate_(view_delegate) {
  61. SetFocusBehavior(FocusBehavior::ALWAYS);
  62. // TODO(crbug.com/1218186): Remove this, this is in place temporarily to be
  63. // able to submit accessibility checks, but this focusable View needs to
  64. // add a name so that the screen reader knows what to announce.
  65. SetProperty(views::kSkipAccessibilityPaintChecks, true);
  66. SetCallback(
  67. base::BindRepeating(&SearchResultSuggestionChipView::OnButtonPressed,
  68. base::Unretained(this)));
  69. SetInstallFocusRingOnFocus(true);
  70. views::FocusRing::Get(this)->SetColorId(focus_ring_color_);
  71. views::InkDrop::Get(this)->SetMode(views::InkDropHost::InkDropMode::ON);
  72. views::InstallPillHighlightPathGenerator(this);
  73. views::InkDrop::UseInkDropWithoutAutoHighlight(views::InkDrop::Get(this),
  74. /*highlight_on_hover=*/false);
  75. views::InkDrop::Get(this)->SetCreateRippleCallback(base::BindRepeating(
  76. [](Button* host) -> std::unique_ptr<views::InkDropRipple> {
  77. const gfx::Point center = host->GetLocalBounds().CenterPoint();
  78. const int ripple_radius = host->width() / 2;
  79. const gfx::Rect bounds(center.x() - ripple_radius,
  80. center.y() - ripple_radius, 2 * ripple_radius,
  81. 2 * ripple_radius);
  82. const AppListColorProvider* const color_provider =
  83. AppListColorProvider::Get();
  84. const SkColor bg_color = color_provider->GetSearchBoxBackgroundColor();
  85. return std::make_unique<views::FloodFillInkDropRipple>(
  86. host->size(), host->GetLocalBounds().InsetsFrom(bounds),
  87. views::InkDrop::Get(host)->GetInkDropCenterBasedOnLastEvent(),
  88. color_provider->GetInkDropBaseColor(bg_color),
  89. color_provider->GetInkDropOpacity(bg_color));
  90. },
  91. this));
  92. InitLayout();
  93. }
  94. SearchResultSuggestionChipView::~SearchResultSuggestionChipView() = default;
  95. void SearchResultSuggestionChipView::SetBackgroundBlurEnabled(bool enabled) {
  96. // Background blur is enabled if and only if layer exists.
  97. if (!!layer() == enabled)
  98. return;
  99. if (!enabled) {
  100. DestroyLayer();
  101. return;
  102. }
  103. SetPaintToLayer();
  104. layer()->SetFillsBoundsOpaquely(false);
  105. layer()->SetBackgroundBlur(kBlurRadius);
  106. layer()->SetName("launcher/SearchResultSuggestionChip");
  107. SetRoundedCornersForLayer(kPreferredHeightDip / 2);
  108. }
  109. void SearchResultSuggestionChipView::OnResultChanged() {
  110. SetVisible(!!result());
  111. UpdateSuggestionChipView();
  112. }
  113. void SearchResultSuggestionChipView::OnMetadataChanged() {
  114. UpdateSuggestionChipView();
  115. }
  116. const char* SearchResultSuggestionChipView::GetClassName() const {
  117. return "SearchResultSuggestionChipView";
  118. }
  119. void SearchResultSuggestionChipView::ChildVisibilityChanged(
  120. views::View* child) {
  121. // When icon visibility is modified we need to update layout padding.
  122. if (child == icon_view_) {
  123. const int padding_left_dip =
  124. icon_view_->GetVisible() ? kIconMarginDip : kPaddingDip;
  125. layout_manager_->set_inside_border_insets(
  126. gfx::Insets::TLBR(0, padding_left_dip, 0, kPaddingDip));
  127. }
  128. PreferredSizeChanged();
  129. }
  130. void SearchResultSuggestionChipView::OnPaintBackground(gfx::Canvas* canvas) {
  131. cc::PaintFlags flags;
  132. flags.setAntiAlias(true);
  133. gfx::Rect bounds = GetContentsBounds();
  134. // Background.
  135. flags.setColor(
  136. AppListColorProvider::Get()->GetSuggestionChipBackgroundColor());
  137. canvas->DrawRoundRect(bounds, height() / 2, flags);
  138. // Focus Ring should only be visible when keyboard traversal is occurring.
  139. views::FocusRing::Get(this)->SetColorId(
  140. view_delegate_->KeyboardTraversalEngaged()
  141. ? focus_ring_color_
  142. : ui::kColorAshAppListFocusRingNoKeyboard);
  143. }
  144. void SearchResultSuggestionChipView::OnFocus() {
  145. SchedulePaint();
  146. SearchResultBaseView::OnFocus();
  147. }
  148. void SearchResultSuggestionChipView::OnBlur() {
  149. SchedulePaint();
  150. }
  151. bool SearchResultSuggestionChipView::OnKeyPressed(const ui::KeyEvent& event) {
  152. if (event.key_code() == ui::VKEY_SPACE)
  153. return false;
  154. return Button::OnKeyPressed(event);
  155. }
  156. void SearchResultSuggestionChipView::OnThemeChanged() {
  157. views::View::OnThemeChanged();
  158. text_view_->SetEnabledColor(
  159. AppListColorProvider::Get()->GetSuggestionChipTextColor());
  160. SchedulePaint();
  161. }
  162. std::unique_ptr<ui::Layer> SearchResultSuggestionChipView::RecreateLayer() {
  163. std::unique_ptr<ui::Layer> old_layer = views::View::RecreateLayer();
  164. if (layer())
  165. SetRoundedCornersForLayer(kPreferredHeightDip / 2);
  166. return old_layer;
  167. }
  168. void SearchResultSuggestionChipView::SetIcon(const gfx::ImageSkia& icon) {
  169. icon_view_->SetImage(icon);
  170. icon_view_->SetVisible(true);
  171. }
  172. void SearchResultSuggestionChipView::SetText(const std::u16string& text) {
  173. text_view_->SetText(text);
  174. gfx::Size size = text_view_->CalculatePreferredSize();
  175. size.set_width(std::min(kMaxTextWidth, size.width()));
  176. text_view_->SetPreferredSize(size);
  177. }
  178. const std::u16string& SearchResultSuggestionChipView::GetText() const {
  179. return text_view_->GetText();
  180. }
  181. void SearchResultSuggestionChipView::UpdateSuggestionChipView() {
  182. if (!result()) {
  183. SetIcon(gfx::ImageSkia());
  184. if (!GetText().empty())
  185. SetText(std::u16string());
  186. SetAccessibleName(std::u16string());
  187. return;
  188. }
  189. SetIcon(result()->chip_icon());
  190. SetText(result()->title());
  191. std::u16string accessible_name = result()->title();
  192. if (result()->id() == kInternalAppIdContinueReading) {
  193. accessible_name = l10n_util::GetStringFUTF16(
  194. IDS_APP_LIST_CONTINUE_READING_ACCESSIBILE_NAME, accessible_name);
  195. }
  196. SetAccessibleName(accessible_name);
  197. }
  198. void SearchResultSuggestionChipView::InitLayout() {
  199. layout_manager_ = SetLayoutManager(std::make_unique<views::BoxLayout>(
  200. views::BoxLayout::Orientation::kHorizontal,
  201. gfx::Insets::TLBR(0, kPaddingDip, 0, kPaddingDip), kIconMarginDip));
  202. layout_manager_->set_cross_axis_alignment(
  203. views::BoxLayout::CrossAxisAlignment::kCenter);
  204. // Icon.
  205. const int icon_size =
  206. SharedAppListConfig::instance().suggestion_chip_icon_dimension();
  207. icon_view_ = AddChildView(std::make_unique<views::ImageView>());
  208. icon_view_->SetImageSize(gfx::Size(icon_size, icon_size));
  209. icon_view_->SetPreferredSize(gfx::Size(icon_size, icon_size));
  210. icon_view_->SetVisible(false);
  211. // Text.
  212. text_view_ = AddChildView(std::make_unique<views::Label>());
  213. text_view_->SetAutoColorReadabilityEnabled(false);
  214. text_view_->SetSubpixelRenderingEnabled(false);
  215. text_view_->SetFontList(SharedAppListConfig::instance()
  216. .search_result_recommendation_title_font());
  217. SetText(std::u16string());
  218. text_view_->SetEnabledColor(
  219. AppListColorProvider::Get()->GetSuggestionChipTextColor());
  220. }
  221. void SearchResultSuggestionChipView::OnButtonPressed(const ui::Event& event) {
  222. DCHECK(result());
  223. LogAppLaunch(index_in_container());
  224. RecordSearchResultOpenSource(result(), view_delegate_->GetAppListViewState(),
  225. view_delegate_->IsInTabletMode());
  226. view_delegate_->OpenSearchResult(
  227. result()->id(), event.flags(),
  228. AppListLaunchedFrom::kLaunchedFromSuggestionChip,
  229. AppListLaunchType::kAppSearchResult, index_in_container(),
  230. false /* launch_as_default */);
  231. }
  232. void SearchResultSuggestionChipView::SetRoundedCornersForLayer(
  233. float corner_radius) {
  234. layer()->SetRoundedCornerRadius(
  235. {corner_radius, corner_radius, corner_radius, corner_radius});
  236. layer()->SetIsFastRoundedCorner(true);
  237. }
  238. } // namespace ash