chromium/ash/picker/views/picker_section_list_view.cc

// 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.

#include "ash/picker/views/picker_section_list_view.h"

#include <iterator>
#include <memory>
#include <utility>

#include "ash/picker/picker_asset_fetcher.h"
#include "ash/picker/views/picker_item_view.h"
#include "ash/picker/views/picker_section_view.h"
#include "base/containers/adapters.h"
#include "base/ranges/algorithm.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/layout_types.h"
#include "ui/views/view.h"
#include "ui/views/view_utils.h"

namespace ash {

PickerSectionListView::PickerSectionListView(
    int section_width,
    PickerAssetFetcher* asset_fetcher,
    PickerSubmenuController* submenu_controller)
    : section_width_(section_width),
      asset_fetcher_(asset_fetcher),
      submenu_controller_(submenu_controller) {
  SetLayoutManager(
      std::make_unique<views::BoxLayout>(views::LayoutOrientation::kVertical))
      ->set_cross_axis_alignment(views::LayoutAlignment::kStretch);
}

PickerSectionListView::~PickerSectionListView() = default;

views::View* PickerSectionListView::GetTopItem() {
  for (views::View* section : children()) {
    if (views::View* top_item =
            views::AsViewClass<PickerSectionView>(section)->GetTopItem()) {
      return top_item;
    }
  }
  return nullptr;
}

views::View* PickerSectionListView::GetBottomItem() {
  for (views::View* section : base::Reversed(children())) {
    if (views::View* bottom_item =
            views::AsViewClass<PickerSectionView>(section)->GetBottomItem()) {
      return bottom_item;
    }
  }
  return nullptr;
}

views::View* PickerSectionListView::GetItemAbove(views::View* item) {
  PickerSectionView* section = GetSectionContaining(item);
  if (section == nullptr) {
    return nullptr;
  }

  // First check if there is an item above in the same section.
  if (views::View* item_below = section->GetItemAbove(item)) {
    return item_below;
  }

  // Otherwise, return the bottom item in a previous non-empty section if there
  // is one.
  for (auto section_it =
           std::make_reverse_iterator(base::ranges::find(children(), section));
       section_it != children().rend(); section_it = std::next(section_it)) {
    if (views::View* prev_section_bottom_item =
            views::AsViewClass<PickerSectionView>(section_it->get())
                ->GetBottomItem()) {
      return prev_section_bottom_item;
    }
  }

  return nullptr;
}

views::View* PickerSectionListView::GetItemBelow(views::View* item) {
  PickerSectionView* section = GetSectionContaining(item);
  if (section == nullptr) {
    return nullptr;
  }

  // First check if there is an item below in the same section.
  if (views::View* item_below = section->GetItemBelow(item)) {
    return item_below;
  }

  // Otherwise, return the top item in the next non-empty section if there is
  // one.
  for (auto section_it = std::next(base::ranges::find(children(), section));
       section_it != children().end(); section_it = std::next(section_it)) {
    if (views::View* next_section_top_item =
            views::AsViewClass<PickerSectionView>(section_it->get())
                ->GetTopItem()) {
      return next_section_top_item;
    }
  }
  return nullptr;
}

views::View* PickerSectionListView::GetItemLeftOf(views::View* item) {
  PickerSectionView* section = GetSectionContaining(item);
  return section != nullptr ? section->GetItemLeftOf(item) : nullptr;
}

views::View* PickerSectionListView::GetItemRightOf(views::View* item) {
  PickerSectionView* section = GetSectionContaining(item);
  return section != nullptr ? section->GetItemRightOf(item) : nullptr;
}

PickerSectionView* PickerSectionListView::AddSection() {
  return AddChildView(std::make_unique<PickerSectionView>(
      section_width_, asset_fetcher_, submenu_controller_));
}

PickerSectionView* PickerSectionListView::AddSectionAt(size_t index) {
  return AddChildViewAt(
      std::make_unique<PickerSectionView>(section_width_, asset_fetcher_,
                                          submenu_controller_),
      index);
}

void PickerSectionListView::ClearSectionList() {
  RemoveAllChildViews();
}

PickerSectionView* PickerSectionListView::GetSectionContaining(
    views::View* item) {
  for (views::View* view = item->parent(); view != nullptr;
       view = view->parent()) {
    if (views::IsViewClass<PickerSectionView>(view) && view->parent() == this) {
      return views::AsViewClass<PickerSectionView>(view);
    }
  }
  return nullptr;
}

BEGIN_METADATA(PickerSectionListView)
END_METADATA

}  // namespace ash