chromium/ash/picker/views/picker_section_view.h

// Copyright 2024 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_PICKER_VIEWS_PICKER_SECTION_VIEW_H_
#define ASH_PICKER_VIEWS_PICKER_SECTION_VIEW_H_

#include <memory>
#include <string>
#include <vector>

#include "ash/ash_export.h"
#include "ash/public/cpp/picker/picker_search_result.h"
#include "base/containers/span.h"
#include "base/memory/raw_ptr.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/views/controls/link.h"
#include "ui/views/view.h"

namespace views {
class BoxLayoutView;
class Label;
}  // namespace views

namespace ash {

class PickerAssetFetcher;
class PickerImageItemGridView;
class PickerImageItemView;
class PickerItemWithSubmenuView;
class PickerItemView;
class PickerListItemContainerView;
class PickerListItemView;
class PickerPreviewBubbleController;
class PickerSubmenuController;
class PickerTraversableItemContainer;
enum class PickerActionType;

// View for a Picker section with a title and related items.
class ASH_EXPORT PickerSectionView : public views::View {
  METADATA_HEADER(PickerSectionView, views::View)

 public:
  using SelectResultCallback = base::RepeatingClosure;

  // Describes the way local file results are visually presented.
  enum class LocalFileResultStyle {
    // Shown as list items with the name of the file as the label.
    kList,
    // Shown as a grid of thumbnail previews.
    kGrid,
  };

  explicit PickerSectionView(int section_width,
                             PickerAssetFetcher* asset_fetcher,
                             PickerSubmenuController* submenu_controller);
  PickerSectionView(const PickerSectionView&) = delete;
  PickerSectionView& operator=(const PickerSectionView&) = delete;
  ~PickerSectionView() override;

  // Creates an item based on `result` and adds it to the section view.
  // `preview_controller` can be null if previews are not needed.
  // `asset_fetcher` can be null for most result types.
  // Both `preview_controller` and `asset_fetcher` must outlive the return
  // value.
  static std::unique_ptr<PickerItemView> CreateItemFromResult(
      const PickerSearchResult& result,
      PickerPreviewBubbleController* preview_controller,
      PickerAssetFetcher* asset_fetcher,
      int available_width,
      LocalFileResultStyle local_file_result_style,
      SelectResultCallback select_result_callback);

  void AddTitleLabel(const std::u16string& title_text);
  void AddTitleTrailingLink(const std::u16string& link_text,
                            const std::u16string& accessible_name,
                            views::Link::ClickedCallback link_callback);

  // Adds a list item. These are displayed in a vertical list, each item
  // spanning the width of the section.
  PickerListItemView* AddListItem(
      std::unique_ptr<PickerListItemView> list_item);

  // Adds an image item to the section. These are displayed in a grid with two
  // columns.
  PickerImageItemView* AddImageGridItem(
      std::unique_ptr<PickerImageItemView> image_item);

  // Adds an item with submenu to the section.
  PickerItemWithSubmenuView* AddItemWithSubmenu(
      std::unique_ptr<PickerItemWithSubmenuView> item_with_submenu);

  // Same as `CreateItemFromResult`, but additionally adds the item to this
  // section.
  PickerItemView* AddResult(const PickerSearchResult& result,
                            PickerPreviewBubbleController* preview_controller,
                            LocalFileResultStyle local_file_result_style,
                            SelectResultCallback select_result_callback);

  void ClearItems();

  // Returns the item to highlight to when navigating to this section from the
  // top, or nullptr if the section is empty.
  views::View* GetTopItem();

  // Returns the item to highlight to when navigating to this section from the
  // bottom, or nullptr if the section is empty.
  views::View* GetBottomItem();

  // Returns the item directly above `item`, or nullptr if there is no such item
  // in the section.
  views::View* GetItemAbove(views::View* item);

  // Returns the item directly below `item`, or nullptr if there is no such item
  // in the section.
  views::View* GetItemBelow(views::View* item);

  // Returns the item directly to the left of `item`, or nullptr if there is no
  // such item in the section.
  views::View* GetItemLeftOf(views::View* item);

  // Returns the item directly to the right of `item`, or nullptr if there is no
  // such item in the section.
  views::View* GetItemRightOf(views::View* item);

  const views::Label* title_label_for_testing() const { return title_label_; }
  const views::Link* title_trailing_link_for_testing() const {
    return title_trailing_link_;
  }
  views::Link* title_trailing_link_for_testing() {
    return title_trailing_link_;
  }

  // TODO: b/322900302 - Figure out a nice way to access the item views for
  // keyboard navigation (e.g. how to handle grid items).
  base::span<const raw_ptr<PickerItemView>> item_views() const {
    return item_views_;
  }

  base::span<const raw_ptr<PickerItemView>> item_views_for_testing() const {
    return item_views_;
  }

 private:
  // Returns a non-null item container if the section has one, otherwise returns
  // nullptr.
  // TODO: b/322900302 - Determine whether sections can have multiple item
  // containers. If so, `GetItemContainer` will need to get the right item
  // container. If not, just track a single PickerTraversableItemContainer and
  // then we won't need this method anymore.
  PickerTraversableItemContainer* GetItemContainer();

  // Width available for laying out section items. This is needed to determine
  // row and column widths for grid items in the section.
  int section_width_ = 0;

  // Container for the section title contents, which can have a title label and
  // a trailing link.
  raw_ptr<views::BoxLayoutView> title_container_ = nullptr;
  raw_ptr<views::Label> title_label_ = nullptr;
  raw_ptr<views::Link> title_trailing_link_ = nullptr;

  raw_ptr<PickerListItemContainerView> list_item_container_ = nullptr;
  raw_ptr<PickerImageItemGridView> image_item_grid_ = nullptr;

  // The views for each result item.
  std::vector<raw_ptr<PickerItemView>> item_views_;

  // `asset_fetcher` outlives `this`.
  raw_ptr<PickerAssetFetcher> asset_fetcher_ = nullptr;

  // `submenu_controller` outlives `this`.
  raw_ptr<PickerSubmenuController> submenu_controller_ = nullptr;
};

}  // namespace ash

#endif  // ASH_PICKER_VIEWS_PICKER_SECTION_VIEW_H_