chromium/ash/app_list/views/search_result_container_view.h

// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef ASH_APP_LIST_VIEWS_SEARCH_RESULT_CONTAINER_VIEW_H_
#define ASH_APP_LIST_VIEWS_SEARCH_RESULT_CONTAINER_VIEW_H_

#include <stddef.h>

#include <string>
#include <vector>

#include "ash/app_list/app_list_view_delegate.h"
#include "ash/app_list/model/app_list_model.h"
#include "ash/app_list/model/search/search_model.h"
#include "ash/app_list/views/search_result_base_view.h"
#include "ash/ash_export.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_multi_source_observation.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/views/view.h"
#include "ui/views/view_observer.h"

namespace ash {

// SearchResultContainerView is a base class for views that contain multiple
// search results. SearchPageView holds these in a list and manages which one is
// selected. There can be one result within one SearchResultContainerView
// selected at a time; moving off the end of one container view selects the
// first element of the next container view, and vice versa
class ASH_EXPORT SearchResultContainerView : public views::View,
                                             public views::ViewObserver,
                                             public ui::ListModelObserver {
  METADATA_HEADER(SearchResultContainerView, views::View)

 public:
  class Delegate {
   public:
    // Called whenever results in the container start changing, i.e. during
    // ScheduleUpdate(). It will be followed up with
    // OnSearchResultContainerResultsChanged() when the update completes.
    virtual void OnSearchResultContainerResultsChanging() = 0;

    // Called whenever results in the container change, i.e. during |Update()|.
    virtual void OnSearchResultContainerResultsChanged() = 0;
  };
  explicit SearchResultContainerView(AppListViewDelegate* view_delegate);

  SearchResultContainerView(const SearchResultContainerView&) = delete;
  SearchResultContainerView& operator=(const SearchResultContainerView&) =
      delete;

  ~SearchResultContainerView() override;

  void set_delegate(Delegate* delegate) { delegate_ = delegate; }

  // Sets the search results to listen to.
  void SetResults(SearchModel::SearchResults* results);
  SearchModel::SearchResults* results() { return results_; }

  size_t num_results() const { return num_results_; }

  virtual SearchResultBaseView* GetResultViewAt(size_t index) = 0;

  // Activates or deactivates results container - when not active, the container
  // will not react to search model updates. Generally, container will be active
  // when search is in progress in the launcher UI that owns the results
  // container.
  // Setting the container as inactive will not clear results, so results remain
  // shown during search results UI hide animation. To clear the container when
  // the search UI gets hidden, call `ResetAndHide()`.
  void SetActive(bool active);

  // Clears all results in the container, and hides the container view.
  void ResetAndHide();

  // Information needed to configure search result visibility animations when
  // result updates are animated.
  struct ResultsAnimationInfo {
    // Total number of visible views (either title or result views).
    int total_views = 0;

    // Total number of visible result views.
    int total_result_views = 0;

    // The index of the first result view that should be animated.
    int first_animated_result_view_index = 0;

    // The number of views that are animating (either title or result views).
    int animating_views = 0;

    // Whether fast search result update animations should be used.
    bool use_short_animations = false;
  };

  // Information needed to determine if a search result shuold have an updated
  // animation.
  struct SearchResultAimationMetadata {
    // The ID of the search result.
    std::string result_id;

    // Whether animations should be skipped for this search result.
    bool skip_animations = false;
  };

  // Schedules animations for result list updates. Expected to be implemented
  // for search result containers that animate result updates.
  // `aggregate_animation_info` The aggregated animation information for all
  // search result containers that appear in the search results UI before this
  // container.
  // Returns the animation info for this container.
  virtual std::optional<ResultsAnimationInfo> ScheduleResultAnimations(
      const ResultsAnimationInfo& aggregate_animation_info);

  // Appends search result IDs of the search results shown by the container
  // view into 'result_ids_'
  virtual void AppendShownResultMetadata(
      std::vector<SearchResultAimationMetadata>* result_metadata_);

  // Returns whether the container view has any animating child views.
  virtual bool HasAnimatingChildView();

  bool horizontally_traversable() const { return horizontally_traversable_; }

  // Allows a container to define its traversal behavior
  void set_horizontally_traversable(bool horizontally_traversable) {
    horizontally_traversable_ = horizontally_traversable;
  }

  // Called when the result selection controller updates its selected result.
  virtual void OnSelectedResultChanged();

  // Batching method that actually performs the update and updates layout.
  void Update();

  // Returns whether an update is currently scheduled for this container.
  bool UpdateScheduled();

  // Functions to allow derivative classes to add/remove observed result views.
  void AddObservedResultView(SearchResultBaseView* result_view);
  void RemoveObservedResultView(SearchResultBaseView* result_view);

  // Overridden from ui::ListModelObserver:
  void ListItemsAdded(size_t start, size_t count) override;
  void ListItemsRemoved(size_t start, size_t count) override;
  void ListItemMoved(size_t index, size_t target_index) override;
  void ListItemsChanged(size_t start, size_t count) override;

  // Returns the first result in the container view. Returns nullptr if it does
  // not exist.
  SearchResultBaseView* GetFirstResultView();

  AppListViewDelegate* view_delegate() const { return view_delegate_; }

  // Runs scheduled update for the view. Returns whether the update was
  // actually run (i.e. whether an update was scheduled).
  bool RunScheduledUpdateForTest();

 protected:
  // Fades the view in and animates a vertical transform based on the view's
  // position in the overall search container view.
  static void ShowViewWithAnimation(views::View* result_view,
                                    int position,
                                    bool use_short_animations);

  // Updates the visibility of this container view and its children result
  // views. Force hiding all views if `force_hide` is set to true.
  virtual void UpdateResultsVisibility(bool force_hide) = 0;

  // Returns the title label if there is one, otherwise returns nullptr.
  virtual views::View* GetTitleLabel() = 0;

  // Returns the views in the container that will be animated to become visible.
  virtual std::vector<views::View*> GetViewsToAnimate() = 0;

  // A search result list view may be disabled if there are fewer search result
  // categories than there are search result list views in the
  // 'productivity_launcher_search_view_'. A disabled view does not query the
  // search model.
  bool enabled_ = true;

 private:
  // Schedules an Update call using |update_factory_|. Do nothing if there is a
  // pending call.
  void ScheduleUpdate();

  // Updates UI with model. Returns the number of visible results.
  virtual int DoUpdate() = 0;

  raw_ptr<Delegate> delegate_ = nullptr;

  size_t num_results_ = 0;

  // If true, left/right key events will traverse this container
  bool horizontally_traversable_ = false;

  raw_ptr<SearchModel::SearchResults> results_ =
      nullptr;  // Owned by SearchModel.

  // view delegate for notifications.
  const raw_ptr<AppListViewDelegate> view_delegate_;

  // Whether the container is observing search result model, and updating when
  // results in the model change.
  bool active_ = false;

  base::ScopedMultiSourceObservation<views::View, views::ViewObserver>
      result_view_observations_{this};

  // The factory that consolidates multiple Update calls into one.
  base::WeakPtrFactory<SearchResultContainerView> update_factory_{this};
};

}  // namespace ash

#endif  // ASH_APP_LIST_VIEWS_SEARCH_RESULT_CONTAINER_VIEW_H_