chromium/ash/picker/views/picker_main_container_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_main_container_view.h"

#include <memory>
#include <utility>

#include "ash/picker/views/picker_contents_view.h"
#include "ash/picker/views/picker_page_view.h"
#include "ash/picker/views/picker_pseudo_focus.h"
#include "ash/picker/views/picker_search_field_view.h"
#include "ash/picker/views/picker_style.h"
#include "ash/public/cpp/style/color_provider.h"
#include "ash/style/system_shadow.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
#include "ui/compositor/layer.h"
#include "ui/gfx/geometry/rounded_corners_f.h"
#include "ui/views/background.h"
#include "ui/views/controls/separator.h"
#include "ui/views/highlight_border.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/view.h"
#include "ui/views/view_class_properties.h"

namespace ash {
namespace {

constexpr int kMainContainerMaxHeight = 300;

std::unique_ptr<views::Separator> CreateSeparator() {
  return views::Builder<views::Separator>()
      .SetOrientation(views::Separator::Orientation::kHorizontal)
      .SetColorId(cros_tokens::kCrosSysSeparator)
      .Build();
}

}  // namespace

PickerMainContainerView::PickerMainContainerView() {
  SetPaintToLayer();
  layer()->SetRoundedCornerRadius(
      gfx::RoundedCornersF{kPickerContainerBorderRadius});
  layer()->SetFillsBoundsOpaquely(false);
  layer()->SetIsFastRoundedCorner(true);
  // We set background blur even though the main container background is opaque,
  // to avoid a flickering issue related to the container's scroll view
  // gradient. See b/351051291.
  layer()->SetBackgroundBlur(ColorProvider::kBackgroundBlurSigma);
  layer()->SetBackdropFilterQuality(ColorProvider::kBackgroundBlurQuality);

  SetBackground(views::CreateThemedRoundedRectBackground(
      kPickerContainerBackgroundColor, kPickerContainerBorderRadius));
  SetBorder(std::make_unique<views::HighlightBorder>(
      kPickerContainerBorderRadius,
      views::HighlightBorder::Type::kHighlightBorderOnShadow));
  shadow_ = SystemShadow::CreateShadowOnNinePatchLayerForView(
      this, kPickerContainerShadowType);
  shadow_->SetRoundedCornerRadius(kPickerContainerBorderRadius);

  SetLayoutManager(std::make_unique<views::BoxLayout>())
      ->SetOrientation(views::LayoutOrientation::kVertical);
}

PickerMainContainerView::~PickerMainContainerView() = default;

gfx::Size PickerMainContainerView::CalculatePreferredSize(
    const views::SizeBounds& available_size) const {
  const int preferred_height =
      views::View::CalculatePreferredSize(available_size).height();
  return gfx::Size(kPickerViewWidth,
                   std::min(preferred_height, kMainContainerMaxHeight));
}

views::View* PickerMainContainerView::GetTopItem() {
  return active_page_->GetTopItem();
}

views::View* PickerMainContainerView::GetBottomItem() {
  return active_page_->GetBottomItem();
}

views::View* PickerMainContainerView::GetItemAbove(views::View* item) {
  if (search_field_view_->Contains(item)) {
    views::View* prev_item = GetNextPickerPseudoFocusableView(
        item, PickerPseudoFocusDirection::kBackward, /*should_loop=*/false);
    return Contains(prev_item) ? prev_item : nullptr;
  }
  // Try to get an item above `item`, skipping items outside of the active page
  // (such as search field buttons).
  return active_page_->GetItemAbove(item);
}

views::View* PickerMainContainerView::GetItemBelow(views::View* item) {
  if (search_field_view_->Contains(item)) {
    views::View* next_item = GetNextPickerPseudoFocusableView(
        item, PickerPseudoFocusDirection::kForward, /*should_loop=*/false);
    return Contains(next_item) ? next_item : nullptr;
  }
  // Try to get an item below `item`, skipping items outside of the active page
  // (such as search field buttons).
  return active_page_->GetItemBelow(item);
}

views::View* PickerMainContainerView::GetItemLeftOf(views::View* item) {
  return active_page_->GetItemLeftOf(item);
}

views::View* PickerMainContainerView::GetItemRightOf(views::View* item) {
  return active_page_->GetItemRightOf(item);
}

bool PickerMainContainerView::ContainsItem(views::View* item) {
  return Contains(item);
}

PickerSearchFieldView* PickerMainContainerView::AddSearchFieldView(
    std::unique_ptr<PickerSearchFieldView> search_field_view) {
  search_field_view_ = AddChildView(std::move(search_field_view));
  return search_field_view_;
}

PickerContentsView* PickerMainContainerView::AddContentsView(
    PickerLayoutType layout_type) {
  switch (layout_type) {
    case PickerLayoutType::kMainResultsBelowSearchField:
      AddChildView(CreateSeparator());
      contents_view_ =
          AddChildView(std::make_unique<PickerContentsView>(layout_type));
      break;
    case PickerLayoutType::kMainResultsAboveSearchField:
      contents_view_ =
          AddChildViewAt(std::make_unique<PickerContentsView>(layout_type), 0);
      AddChildViewAt(CreateSeparator(), 1);
      break;
  }

  contents_view_->SetProperty(
      views::kBoxLayoutFlexKey,
      views::BoxLayoutFlexSpecification().WithWeight(1));

  return contents_view_;
}

void PickerMainContainerView::SetActivePage(PickerPageView* page_view) {
  contents_view_->SetActivePage(page_view);
  active_page_ = page_view;
}

BEGIN_METADATA(PickerMainContainerView)
END_METADATA

}  // namespace ash