search_result_list_view.cc 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  1. // Copyright (c) 2012 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_list_view.h"
  5. #include <algorithm>
  6. #include <memory>
  7. #include <string>
  8. #include <type_traits>
  9. #include <utility>
  10. #include <vector>
  11. #include "ash/app_list/app_list_metrics.h"
  12. #include "ash/app_list/app_list_model_provider.h"
  13. #include "ash/app_list/app_list_view_delegate.h"
  14. #include "ash/app_list/model/search/search_result.h"
  15. #include "ash/app_list/views/app_list_main_view.h"
  16. #include "ash/app_list/views/search_box_view.h"
  17. #include "ash/constants/ash_features.h"
  18. #include "ash/public/cpp/app_list/app_list_color_provider.h"
  19. #include "ash/public/cpp/app_list/app_list_config.h"
  20. #include "ash/public/cpp/app_list/app_list_features.h"
  21. #include "ash/public/cpp/app_list/app_list_metrics.h"
  22. #include "ash/public/cpp/app_list/app_list_notifier.h"
  23. #include "ash/public/cpp/app_list/vector_icons/vector_icons.h"
  24. #include "ash/public/cpp/ash_typography.h"
  25. #include "ash/resources/vector_icons/vector_icons.h"
  26. #include "ash/strings/grit/ash_strings.h"
  27. #include "base/bind.h"
  28. #include "base/dcheck_is_on.h"
  29. #include "base/time/time.h"
  30. #include "ui/accessibility/ax_enums.mojom.h"
  31. #include "ui/accessibility/ax_node_data.h"
  32. #include "ui/base/l10n/l10n_util.h"
  33. #include "ui/compositor/layer.h"
  34. #include "ui/compositor/layer_animator.h"
  35. #include "ui/compositor/scoped_layer_animation_settings.h"
  36. #include "ui/events/event.h"
  37. #include "ui/gfx/animation/linear_animation.h"
  38. #include "ui/gfx/geometry/insets.h"
  39. #include "ui/gfx/paint_vector_icon.h"
  40. #include "ui/views/accessibility/view_accessibility.h"
  41. #include "ui/views/animation/animation_builder.h"
  42. #include "ui/views/background.h"
  43. #include "ui/views/border.h"
  44. #include "ui/views/controls/label.h"
  45. #include "ui/views/controls/textfield/textfield.h"
  46. #include "ui/views/layout/flex_layout.h"
  47. #include "ui/views/widget/widget.h"
  48. namespace ash {
  49. namespace {
  50. constexpr int kPreferredTitleHorizontalMargins = 16;
  51. constexpr int kPreferredTitleTopMargins = 12;
  52. constexpr int kPreferredTitleBottomMargins = 4;
  53. constexpr static base::TimeDelta kFadeInDuration = base::Milliseconds(100);
  54. constexpr static base::TimeDelta kIdentityTranslationDuration =
  55. base::Milliseconds(200);
  56. constexpr static base::TimeDelta kFastFadeInDuration = base::Milliseconds(0);
  57. // TODO(crbug.com/1199206): Move this into SharedAppListConfig once the UI for
  58. // categories is more developed.
  59. constexpr size_t kMaxResultsWithCategoricalSearch = 3;
  60. constexpr int kAnswerCardMaxResults = 1;
  61. // Show animations for search result views and titles have a translation
  62. // distance of 'kAnimatedOffsetMultiplier' * i where i is the position of the
  63. // view in the 'ProductivityLauncherSearchView'.
  64. constexpr int kAnimatedOffsetMultiplier = 4;
  65. size_t GetMaxSearchResultListItems() {
  66. if (app_list_features::IsCategoricalSearchEnabled())
  67. return kMaxResultsWithCategoricalSearch;
  68. return SharedAppListConfig::instance().max_search_result_list_items();
  69. }
  70. // Maps 'AppListSearchResultCategory' to 'SearchResultListType'.
  71. SearchResultListView::SearchResultListType CategoryToListType(
  72. ash::AppListSearchResultCategory category) {
  73. switch (category) {
  74. case ash::AppListSearchResultCategory::kApps:
  75. return SearchResultListView::SearchResultListType::kApps;
  76. case ash::AppListSearchResultCategory::kAppShortcuts:
  77. return SearchResultListView::SearchResultListType::kAppShortcuts;
  78. case ash::AppListSearchResultCategory::kWeb:
  79. return SearchResultListView::SearchResultListType::kWeb;
  80. case ash::AppListSearchResultCategory::kFiles:
  81. return SearchResultListView::SearchResultListType::kFiles;
  82. case ash::AppListSearchResultCategory::kSettings:
  83. return SearchResultListView::SearchResultListType::kSettings;
  84. case ash::AppListSearchResultCategory::kHelp:
  85. return SearchResultListView::SearchResultListType::kHelp;
  86. case ash::AppListSearchResultCategory::kPlayStore:
  87. return SearchResultListView::SearchResultListType::kPlayStore;
  88. case ash::AppListSearchResultCategory::kSearchAndAssistant:
  89. return SearchResultListView::SearchResultListType::kSearchAndAssistant;
  90. case ash::AppListSearchResultCategory::kGames:
  91. return SearchResultListView::SearchResultListType::kGames;
  92. case ash::AppListSearchResultCategory::kUnknown:
  93. NOTREACHED();
  94. return SearchResultListView::SearchResultListType::kUnified;
  95. }
  96. }
  97. } // namespace
  98. SearchResultListView::SearchResultListView(
  99. AppListMainView* main_view,
  100. AppListViewDelegate* view_delegate,
  101. SearchResultPageDialogController* dialog_controller,
  102. SearchResultView::SearchResultViewType search_result_view_type,
  103. bool animates_result_updates,
  104. absl::optional<size_t> productivity_launcher_index)
  105. : SearchResultContainerView(view_delegate),
  106. main_view_(main_view),
  107. view_delegate_(view_delegate),
  108. animates_result_updates_(animates_result_updates),
  109. results_container_(new views::View),
  110. productivity_launcher_index_(productivity_launcher_index),
  111. search_result_view_type_(search_result_view_type) {
  112. auto* layout = results_container_->SetLayoutManager(
  113. std::make_unique<views::FlexLayout>());
  114. layout->SetOrientation(views::LayoutOrientation::kVertical);
  115. title_label_ = AddChildView(std::make_unique<views::Label>(
  116. u"", CONTEXT_SEARCH_RESULT_CATEGORY_LABEL, STYLE_PRODUCTIVITY_LAUNCHER));
  117. title_label_->SetBackgroundColor(SK_ColorTRANSPARENT);
  118. title_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
  119. title_label_->SetBorder(views::CreateEmptyBorder(gfx::Insets::TLBR(
  120. kPreferredTitleTopMargins, kPreferredTitleHorizontalMargins,
  121. kPreferredTitleBottomMargins, kPreferredTitleHorizontalMargins)));
  122. title_label_->SetVisible(false);
  123. title_label_->SetPaintToLayer();
  124. title_label_->layer()->SetFillsBoundsOpaquely(false);
  125. results_container_->AddChildView(title_label_);
  126. size_t result_count =
  127. GetMaxSearchResultListItems() +
  128. SharedAppListConfig::instance().max_assistant_search_result_list_items();
  129. for (size_t i = 0; i < result_count; ++i) {
  130. search_result_views_.emplace_back(new SearchResultView(
  131. this, view_delegate_, dialog_controller, search_result_view_type_));
  132. search_result_views_.back()->set_index_in_container(i);
  133. search_result_views_.back()->SetPaintToLayer();
  134. search_result_views_.back()->layer()->SetFillsBoundsOpaquely(false);
  135. results_container_->AddChildView(search_result_views_.back());
  136. AddObservedResultView(search_result_views_.back());
  137. }
  138. AddChildView(results_container_);
  139. }
  140. SearchResultListView::~SearchResultListView() = default;
  141. void SearchResultListView::SetListType(SearchResultListType list_type) {
  142. if (list_type_ != list_type)
  143. removed_results_.clear();
  144. list_type_ = list_type;
  145. switch (list_type_.value()) {
  146. case SearchResultListType::kUnified:
  147. case SearchResultListType::kAnswerCard:
  148. // kUnified and kAnswerCard SearchResultListView do not have labels.
  149. title_label_->SetText(u"");
  150. break;
  151. case SearchResultListType::kBestMatch:
  152. title_label_->SetText(l10n_util::GetStringUTF16(
  153. IDS_ASH_SEARCH_RESULT_CATEGORY_LABEL_BEST_MATCH));
  154. break;
  155. case SearchResultListType::kApps:
  156. title_label_->SetText(
  157. l10n_util::GetStringUTF16(IDS_ASH_SEARCH_RESULT_CATEGORY_LABEL_APPS));
  158. break;
  159. case SearchResultListType::kAppShortcuts:
  160. title_label_->SetText(l10n_util::GetStringUTF16(
  161. IDS_ASH_SEARCH_RESULT_CATEGORY_LABEL_APP_SHORTCUTS));
  162. break;
  163. case SearchResultListType::kWeb:
  164. title_label_->SetText(
  165. l10n_util::GetStringUTF16(IDS_ASH_SEARCH_RESULT_CATEGORY_LABEL_WEB));
  166. break;
  167. case SearchResultListType::kFiles:
  168. title_label_->SetText(l10n_util::GetStringUTF16(
  169. IDS_ASH_SEARCH_RESULT_CATEGORY_LABEL_FILES));
  170. break;
  171. case SearchResultListType::kSettings:
  172. title_label_->SetText(l10n_util::GetStringUTF16(
  173. IDS_ASH_SEARCH_RESULT_CATEGORY_LABEL_SETTINGS));
  174. break;
  175. case SearchResultListType::kHelp:
  176. title_label_->SetText(
  177. l10n_util::GetStringUTF16(IDS_ASH_SEARCH_RESULT_CATEGORY_LABEL_HELP));
  178. break;
  179. case SearchResultListType::kPlayStore:
  180. title_label_->SetText(l10n_util::GetStringUTF16(
  181. IDS_ASH_SEARCH_RESULT_CATEGORY_LABEL_PLAY_STORE));
  182. break;
  183. case SearchResultListType::kSearchAndAssistant:
  184. title_label_->SetText(l10n_util::GetStringUTF16(
  185. IDS_ASH_SEARCH_RESULT_CATEGORY_LABEL_SEARCH_AND_ASSISTANT));
  186. break;
  187. case SearchResultListType::kGames:
  188. title_label_->SetText(l10n_util::GetStringUTF16(
  189. IDS_ASH_SEARCH_RESULT_CATEGORY_LABEL_GAMES));
  190. break;
  191. }
  192. switch (list_type_.value()) {
  193. case SearchResultListType::kUnified:
  194. case SearchResultListType::kAnswerCard:
  195. // Classic SearchResultListView and Productivity Launcher Answer Card do
  196. // not have category labels.
  197. title_label_->SetVisible(false);
  198. break;
  199. case SearchResultListType::kBestMatch:
  200. case SearchResultListType::kApps:
  201. case SearchResultListType::kAppShortcuts:
  202. case SearchResultListType::kWeb:
  203. case SearchResultListType::kFiles:
  204. case SearchResultListType::kSettings:
  205. case SearchResultListType::kHelp:
  206. case SearchResultListType::kPlayStore:
  207. case SearchResultListType::kSearchAndAssistant:
  208. case SearchResultListType::kGames:
  209. title_label_->SetVisible(true);
  210. break;
  211. }
  212. GetViewAccessibility().OverrideName(l10n_util::GetStringFUTF16(
  213. IDS_ASH_SEARCH_RESULT_CATEGORY_LABEL_ACCESSIBLE_NAME,
  214. title_label_->GetText()));
  215. #if DCHECK_IS_ON()
  216. switch (list_type_.value()) {
  217. case SearchResultListType::kAnswerCard:
  218. DCHECK(search_result_view_type_ ==
  219. SearchResultView::SearchResultViewType::kAnswerCard);
  220. break;
  221. case SearchResultListType::kUnified:
  222. DCHECK(search_result_view_type_ ==
  223. SearchResultView::SearchResultViewType::kClassic);
  224. break;
  225. case SearchResultListType::kBestMatch:
  226. case SearchResultListType::kApps:
  227. case SearchResultListType::kAppShortcuts:
  228. case SearchResultListType::kWeb:
  229. case SearchResultListType::kFiles:
  230. case SearchResultListType::kSettings:
  231. case SearchResultListType::kHelp:
  232. case SearchResultListType::kPlayStore:
  233. case SearchResultListType::kSearchAndAssistant:
  234. case SearchResultListType::kGames:
  235. DCHECK(search_result_view_type_ ==
  236. SearchResultView::SearchResultViewType::kDefault);
  237. break;
  238. }
  239. #endif
  240. }
  241. SearchResultView* SearchResultListView::GetResultViewAt(size_t index) {
  242. DCHECK(index >= 0 && index < search_result_views_.size());
  243. return search_result_views_[index];
  244. }
  245. std::vector<SearchResultListView::SearchResultListType>
  246. SearchResultListView::GetAllListTypesForCategoricalSearch() {
  247. static const std::vector<SearchResultListType> categorical_search_types = {
  248. SearchResultListType::kAnswerCard,
  249. SearchResultListType::kBestMatch,
  250. SearchResultListType::kApps,
  251. SearchResultListType::kAppShortcuts,
  252. SearchResultListType::kWeb,
  253. SearchResultListType::kFiles,
  254. SearchResultListType::kSettings,
  255. SearchResultListType::kHelp,
  256. SearchResultListType::kPlayStore,
  257. SearchResultListType::kSearchAndAssistant,
  258. SearchResultListType::kGames};
  259. return categorical_search_types;
  260. }
  261. absl::optional<SearchResultContainerView::ResultsAnimationInfo>
  262. SearchResultListView::ScheduleResultAnimations(
  263. const ResultsAnimationInfo& aggregate_animation_info) {
  264. DCHECK(animates_result_updates_);
  265. // Collect current container animation info.
  266. ResultsAnimationInfo current_animation_info;
  267. if (num_results_ < 1 || !enabled_) {
  268. SetVisible(false);
  269. for (auto* result_view : search_result_views_)
  270. result_view->SetVisible(false);
  271. return current_animation_info;
  272. }
  273. // All views should be animated if
  274. // * the container is being shown, or
  275. // * any of the result views that precede the container in the search UI are
  276. // animating, or
  277. // * if.the first animating result view is in a preceding container.
  278. bool force_animation =
  279. !GetVisible() || aggregate_animation_info.animating_views > 0 ||
  280. aggregate_animation_info.first_animated_result_view_index <=
  281. aggregate_animation_info.total_result_views;
  282. SetVisible(true);
  283. current_animation_info.use_short_animations =
  284. aggregate_animation_info.use_short_animations;
  285. auto schedule_animation = [this, &current_animation_info,
  286. &aggregate_animation_info](views::View* view) {
  287. ShowViewWithAnimation(view,
  288. current_animation_info.total_views +
  289. aggregate_animation_info.total_views,
  290. current_animation_info.use_short_animations);
  291. ++current_animation_info.animating_views;
  292. };
  293. if (title_label_->GetVisible()) {
  294. if (force_animation)
  295. schedule_animation(title_label_);
  296. ++current_animation_info.total_views;
  297. }
  298. for (size_t i = 0; i < search_result_views_.size(); ++i) {
  299. SearchResultView* result_view = GetResultViewAt(i);
  300. result_view->SetVisible(i < num_results_);
  301. if (i < num_results_) {
  302. // Checks whether the index of the current result view is greater than
  303. // or equal to the index of the first result view that should be animated.
  304. // Force animations if true.
  305. if (aggregate_animation_info.total_result_views +
  306. current_animation_info.total_result_views >=
  307. aggregate_animation_info.first_animated_result_view_index) {
  308. force_animation = true;
  309. }
  310. if (force_animation)
  311. schedule_animation(result_view);
  312. ++current_animation_info.total_views;
  313. ++current_animation_info.total_result_views;
  314. }
  315. }
  316. return current_animation_info;
  317. }
  318. void SearchResultListView::AppendShownResultMetadata(
  319. std::vector<SearchResultAimationMetadata>* result_metadata_) {
  320. for (size_t i = 0; i < search_result_views_.size(); ++i) {
  321. SearchResultView* result_view = GetResultViewAt(i);
  322. if (i >= num_results_ || !result_view->result())
  323. return;
  324. SearchResultAimationMetadata metadata;
  325. metadata.result_id = result_view->result()->id();
  326. metadata.skip_animations = result_view->result()->skip_update_animation();
  327. result_metadata_->push_back(std::move(metadata));
  328. }
  329. }
  330. bool SearchResultListView::HasAnimatingChildView() {
  331. auto is_animating = [](views::View* view) {
  332. return (view->GetVisible() && view->layer() &&
  333. view->layer()->GetAnimator() &&
  334. view->layer()->GetAnimator()->is_animating());
  335. };
  336. if (is_animating(title_label_))
  337. return true;
  338. for (size_t i = 0; i < search_result_views_.size(); ++i) {
  339. if (is_animating(GetResultViewAt(i)))
  340. return true;
  341. }
  342. return false;
  343. }
  344. void SearchResultListView::ShowViewWithAnimation(views::View* view,
  345. int position,
  346. bool use_short_animations) {
  347. DCHECK(view->layer()->GetAnimator());
  348. // Abort any in-progress layer animation.
  349. view->layer()->GetAnimator()->AbortAllAnimations();
  350. // Animation spec:
  351. //
  352. // Y Position: Down (offset) → End position
  353. // offset: position * kAnimatedOffsetMultiplier px
  354. // Duration: 200ms
  355. // Ease: (0.00, 0.00, 0.20, 1.00)
  356. // Opacity: 0% -> 100%
  357. // Duration: 100 ms
  358. // Ease: Linear
  359. gfx::Transform translate_down;
  360. translate_down.Translate(0, position * kAnimatedOffsetMultiplier);
  361. views::AnimationBuilder()
  362. .SetPreemptionStrategy(
  363. ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
  364. .Once()
  365. .SetOpacity(view, 0.0f)
  366. .SetTransform(view, translate_down)
  367. .Then()
  368. .SetOpacity(view, 1.0f, gfx::Tween::LINEAR)
  369. .SetDuration(use_short_animations ? kFastFadeInDuration : kFadeInDuration)
  370. .At(base::TimeDelta())
  371. .SetDuration(
  372. use_short_animations
  373. ? app_list_features::DynamicSearchUpdateAnimationDuration()
  374. : kIdentityTranslationDuration)
  375. .SetTransform(view, gfx::Transform(), gfx::Tween::LINEAR_OUT_SLOW_IN);
  376. }
  377. void SearchResultListView::OnSelectedResultChanged() {
  378. for (SearchResultView* view : search_result_views_)
  379. view->OnSelectedResultChanged();
  380. }
  381. int SearchResultListView::DoUpdate() {
  382. if (productivity_launcher_index_.has_value()) {
  383. std::vector<ash::AppListSearchResultCategory>* ordered_categories =
  384. AppListModelProvider::Get()->search_model()->ordered_categories();
  385. if (productivity_launcher_index_ < ordered_categories->size()) {
  386. enabled_ = true;
  387. SetListType(CategoryToListType(
  388. (*ordered_categories)[productivity_launcher_index_.value()]));
  389. } else {
  390. enabled_ = false;
  391. list_type_.reset();
  392. }
  393. }
  394. if (!enabled_ || !GetWidget() || !GetWidget()->IsVisible()) {
  395. SetVisible(false);
  396. for (auto* result_view : search_result_views_) {
  397. result_view->SetResult(nullptr);
  398. result_view->SetVisible(false);
  399. }
  400. return 0;
  401. }
  402. std::vector<SearchResult*> displayed_results = UpdateResultViews();
  403. NotifyAccessibilityEvent(ax::mojom::Event::kChildrenChanged, false);
  404. auto* notifier = view_delegate_->GetNotifier();
  405. // TODO(crbug/1216097): replace metrics with something more meaningful.
  406. if (notifier) {
  407. std::vector<AppListNotifier::Result> notifier_results;
  408. for (const auto* result : displayed_results)
  409. notifier_results.emplace_back(result->id(), result->metrics_type());
  410. notifier->NotifyResultsUpdated(SearchResultDisplayType::kList,
  411. notifier_results);
  412. }
  413. return displayed_results.size();
  414. }
  415. void SearchResultListView::Layout() {
  416. results_container_->SetBoundsRect(GetLocalBounds());
  417. }
  418. gfx::Size SearchResultListView::CalculatePreferredSize() const {
  419. return results_container_->GetPreferredSize();
  420. }
  421. const char* SearchResultListView::GetClassName() const {
  422. return "SearchResultListView";
  423. }
  424. void SearchResultListView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
  425. // With productivity launcher disabled, the parent search result page view
  426. // will have the list box role.
  427. if (ash::features::IsProductivityLauncherEnabled())
  428. node_data->role = ax::mojom::Role::kListBox;
  429. }
  430. int SearchResultListView::GetHeightForWidth(int w) const {
  431. return results_container_->GetHeightForWidth(w);
  432. }
  433. void SearchResultListView::OnThemeChanged() {
  434. SearchResultContainerView::OnThemeChanged();
  435. title_label_->SetEnabledColor(
  436. AppListColorProvider::Get()->GetSearchBoxSecondaryTextColor(
  437. kDeprecatedSearchBoxTextDefaultColor));
  438. }
  439. void SearchResultListView::SearchResultActivated(SearchResultView* view,
  440. int event_flags,
  441. bool by_button_press) {
  442. if (!view_delegate_ || !view || !view->result())
  443. return;
  444. auto* result = view->result();
  445. RecordSearchResultOpenSource(result, view_delegate_->GetAppListViewState(),
  446. view_delegate_->IsInTabletMode());
  447. AppListLaunchType launch_type =
  448. IsAppListSearchResultAnApp(result->result_type())
  449. ? AppListLaunchType::kAppSearchResult
  450. : AppListLaunchType::kSearchResult;
  451. view_delegate_->OpenSearchResult(
  452. result->id(), event_flags, AppListLaunchedFrom::kLaunchedFromSearchBox,
  453. launch_type, -1 /* suggestion_index */,
  454. !by_button_press && view->is_default_result() /* launch_as_default */);
  455. }
  456. void SearchResultListView::SearchResultActionActivated(
  457. SearchResultView* view,
  458. SearchResultActionType action) {
  459. if (view_delegate_ && view->result()) {
  460. switch (action) {
  461. case SearchResultActionType::kRemove: {
  462. const std::string result_id = view->result()->id();
  463. removed_results_.insert(result_id);
  464. view_delegate_->InvokeSearchResultAction(result_id, action);
  465. Update();
  466. break;
  467. }
  468. case SearchResultActionType::kAppend:
  469. main_view_->search_box_view()->UpdateQuery(view->result()->title());
  470. break;
  471. case SearchResultActionType::kSearchResultActionTypeMax:
  472. NOTREACHED();
  473. }
  474. }
  475. }
  476. std::vector<SearchResult*> SearchResultListView::GetAssistantResults() {
  477. // Only show Assistant results if there are no tiles. There is not enough
  478. // room in launcher to display Assistant results if there are tiles visible.
  479. bool visible_tiles = !SearchModel::FilterSearchResultsByDisplayType(
  480. results(), SearchResult::DisplayType::kTile,
  481. /*excludes=*/{}, /*max_results=*/1)
  482. .empty();
  483. if (visible_tiles)
  484. return std::vector<SearchResult*>();
  485. return SearchModel::FilterSearchResultsByFunction(
  486. results(),
  487. base::BindRepeating(&SearchResultListView::FilterResultsForUnifiedList,
  488. base::Unretained(this),
  489. /*for_assistant_results=*/true),
  490. /*max_results=*/
  491. SharedAppListConfig::instance().max_assistant_search_result_list_items());
  492. }
  493. std::vector<SearchResult*> SearchResultListView::GetUnifiedSearchResults() {
  494. std::vector<SearchResult*> search_results =
  495. SearchModel::FilterSearchResultsByFunction(
  496. results(),
  497. base::BindRepeating(
  498. &SearchResultListView::FilterResultsForUnifiedList,
  499. base::Unretained(this), /*for_assistant_results=*/false),
  500. GetMaxSearchResultListItems());
  501. std::vector<SearchResult*> assistant_results = GetAssistantResults();
  502. search_results.insert(search_results.end(), assistant_results.begin(),
  503. assistant_results.end());
  504. return search_results;
  505. }
  506. SearchResult::Category SearchResultListView::GetSearchCategory() {
  507. DCHECK(list_type_.has_value());
  508. switch (list_type_.value()) {
  509. case SearchResultListType::kUnified:
  510. case SearchResultListType::kBestMatch:
  511. case SearchResultListType::kAnswerCard:
  512. // Categories are undefined for |kUnified|, |KBestMatch|, and
  513. // |kAnswerCard| list types.
  514. NOTREACHED();
  515. return SearchResult::Category::kUnknown;
  516. case SearchResultListType::kApps:
  517. return SearchResult::Category::kApps;
  518. case SearchResultListType::kAppShortcuts:
  519. return SearchResult::Category::kAppShortcuts;
  520. case SearchResultListType::kWeb:
  521. return SearchResult::Category::kWeb;
  522. case SearchResultListType::kFiles:
  523. return SearchResult::Category::kFiles;
  524. case SearchResultListType::kSettings:
  525. return SearchResult::Category::kSettings;
  526. case SearchResultListType::kHelp:
  527. return SearchResult::Category::kHelp;
  528. case SearchResultListType::kPlayStore:
  529. return SearchResult::Category::kPlayStore;
  530. case SearchResultListType::kSearchAndAssistant:
  531. return SearchResult::Category::kSearchAndAssistant;
  532. case SearchResultListType::kGames:
  533. return SearchResult::Category::kGames;
  534. }
  535. }
  536. std::vector<SearchResult*> SearchResultListView::GetCategorizedSearchResults() {
  537. DCHECK(enabled_ && list_type_.has_value());
  538. switch (list_type_.value()) {
  539. case SearchResultListType::kUnified:
  540. // Use classic search results for the kUnified list view.
  541. return GetUnifiedSearchResults();
  542. case SearchResultListType::kAnswerCard:
  543. return SearchModel::FilterSearchResultsByFunction(
  544. results(), base::BindRepeating([](const SearchResult& result) {
  545. return result.display_type() ==
  546. SearchResultDisplayType::kAnswerCard;
  547. }),
  548. kAnswerCardMaxResults);
  549. case SearchResultListType::kBestMatch:
  550. // Filter results based on whether they have the best_match label.
  551. return SearchModel::FilterSearchResultsByFunction(
  552. results(),
  553. base::BindRepeating(&SearchResultListView::FilterBestMatches,
  554. base::Unretained(this)),
  555. GetMaxSearchResultListItems());
  556. case SearchResultListType::kApps:
  557. case SearchResultListType::kAppShortcuts:
  558. case SearchResultListType::kWeb:
  559. case SearchResultListType::kFiles:
  560. case SearchResultListType::kSettings:
  561. case SearchResultListType::kHelp:
  562. case SearchResultListType::kPlayStore:
  563. case SearchResultListType::kSearchAndAssistant:
  564. case SearchResultListType::kGames:
  565. SearchResult::Category search_category = GetSearchCategory();
  566. return SearchModel::FilterSearchResultsByFunction(
  567. results(),
  568. base::BindRepeating(
  569. &SearchResultListView::FilterSearchResultsByCategory,
  570. base::Unretained(this), search_category),
  571. GetMaxSearchResultListItems());
  572. }
  573. }
  574. std::vector<SearchResult*> SearchResultListView::UpdateResultViews() {
  575. std::vector<SearchResult*> display_results = GetCategorizedSearchResults();
  576. size_t num_results = display_results.size();
  577. num_results_ = num_results;
  578. for (size_t i = 0; i < search_result_views_.size(); ++i) {
  579. SearchResultView* result_view = GetResultViewAt(i);
  580. if (i < num_results) {
  581. result_view->SetResult(display_results[i]);
  582. result_view->SizeToPreferredSize();
  583. } else {
  584. result_view->SetResult(nullptr);
  585. }
  586. // If result updates are animated, the result visibility will be updated in
  587. // `ScheduleResultAnimations()`
  588. if (!animates_result_updates_)
  589. result_view->SetVisible(i < num_results);
  590. }
  591. // If result updates are animated, the container visibility will be updated in
  592. // `ScheduleResultAnimations()`
  593. if (!animates_result_updates_)
  594. SetVisible(num_results > 0);
  595. return display_results;
  596. }
  597. bool SearchResultListView::FilterResultsForUnifiedList(
  598. bool for_assistant_results,
  599. const SearchResult& result) const {
  600. // Filter out results that have been removed from the list by the user.
  601. if (removed_results_.count(result.id()))
  602. return false;
  603. const bool is_assistant_result =
  604. result.result_type() == AppListSearchResultType::kAssistantText;
  605. return result.display_type() == SearchResultDisplayType::kList &&
  606. for_assistant_results == is_assistant_result;
  607. }
  608. bool SearchResultListView::FilterBestMatches(const SearchResult& result) const {
  609. // Filter out results that have been removed from the list by the user.
  610. if (removed_results_.count(result.id()))
  611. return false;
  612. return result.best_match() &&
  613. result.display_type() == SearchResultDisplayType::kList;
  614. }
  615. bool SearchResultListView::FilterSearchResultsByCategory(
  616. const SearchResult::Category& category,
  617. const SearchResult& result) const {
  618. // Filter out results that have been removed from the list by the user.
  619. if (removed_results_.count(result.id()))
  620. return false;
  621. // Filter out best match items to avoid
  622. // duplication between different types of search_result_list_views.
  623. return result.category() == category && !result.best_match() &&
  624. result.display_type() == SearchResultDisplayType::kList;
  625. }
  626. } // namespace ash