chromium/ash/system/pods_overflow_tray.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/system/pods_overflow_tray.h"

#include <memory>

#include "ash/accessibility/accessibility_controller.h"
#include "ash/constants/ash_features.h"
#include "ash/constants/tray_background_view_catalog.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/shelf/shelf.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_id.h"
#include "ash/system/tray/tray_background_view.h"
#include "ash/system/tray/tray_container.h"
#include "ash/system/tray/tray_utils.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/models/image_model.h"
#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
#include "ui/compositor/layer.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/layout/box_layout_view.h"

namespace ash {

namespace {

constexpr gfx::Insets kPodsBubbleInteriorMargin = gfx::Insets(8);
constexpr int kPodsBubbleBetweenChildSpacing = 8;
constexpr int kPodsBubbleCornerRadius = 24;

ui::ImageModel GetTrayContainerImage(bool is_active) {
  return ui::ImageModel::FromVectorIcon(
      /*vector_icon=*/kShelfOverflowHorizontalDotsIcon,
      /*color_id=*/is_active ? cros_tokens::kCrosSysSystemOnPrimaryContainer
                             : cros_tokens::kCrosSysOnSurface);
}

}  // namespace

PodsOverflowTray::PodsOverflowTray(Shelf* shelf)
    : TrayBackgroundView(shelf, TrayBackgroundViewCatalogName::kPodsOverflow) {
  CHECK(features::IsScalableShelfPodsEnabled());
  SetCallback(base::BindRepeating(&PodsOverflowTray::OnTrayButtonPressed,
                                  weak_ptr_factory_.GetWeakPtr()));

  // TODO(b/337965177): Implement pods overflow tray container view.
  tray_icon_ = tray_container()->AddChildView(
      views::Builder<views::ImageView>()
          .SetPreferredSize(gfx::Size(kTrayItemSize, kTrayItemSize))
          .SetImage(GetTrayContainerImage(is_active()))
          .Build());

  // TODO(b/337925759): Make visible only when pods overflow on the shelf.
  TrayBackgroundView::SetVisiblePreferred(true);
}

PodsOverflowTray::~PodsOverflowTray() {
  if (bubble_) {
    bubble_->bubble_view()->ResetDelegate();
  }
}

std::u16string PodsOverflowTray::GetAccessibleNameForTray() {
  // TODO(b/337963043): Update a11y strings.
  return u"Pods overflow tray";
}

void PodsOverflowTray::HandleLocaleChange() {
  TooltipTextChanged();
}

void PodsOverflowTray::HideBubbleWithView(const TrayBubbleView* bubble_view) {
  if (bubble_->bubble_view() == bubble_view) {
    CloseBubble();
  }
}

void PodsOverflowTray::ClickedOutsideBubble(const ui::LocatedEvent& event) {
  CloseBubble();
}

void PodsOverflowTray::UpdateTrayItemColor(bool is_active) {
  tray_icon_->SetImage(GetTrayContainerImage(is_active));
}

void PodsOverflowTray::CloseBubbleInternal() {
  pods_container_ = nullptr;
  bubble_.reset();
  SetIsActive(false);
  shelf()->UpdateAutoHideState();
}

void PodsOverflowTray::ShowBubble() {
  TrayBubbleView::InitParams init_params = CreateInitParamsForTrayBubble(this);
  init_params.corner_radius = kPodsBubbleCornerRadius;
  std::unique_ptr<TrayBubbleView> bubble_view =
      std::make_unique<TrayBubbleView>(init_params);

  pods_container_ = bubble_view->AddChildView(
      views::Builder<views::BoxLayoutView>()
          .SetInsideBorderInsets(kPodsBubbleInteriorMargin)
          .SetBetweenChildSpacing(kPodsBubbleBetweenChildSpacing)
          .Build());

  // TODO(b/337925727): Replace with real tray buttons.
  // Currently four views will be added that mock shelf pod buttons.
  for (int i = 0; i < 4; i++) {
    pods_container_->AddChildView(
        views::Builder<views::View>()
            .SetPreferredSize(gfx::Size(kTrayItemSize, kTrayItemSize))
            .SetBackground(views::CreateThemedRoundedRectBackground(
                cros_tokens::kCrosSysSystemOnBase, kPodsBubbleCornerRadius))
            .Build());
  }

  bubble_view->SetPreferredWidth(pods_container_->GetPreferredSize().width());

  bubble_ = std::make_unique<TrayBubbleWrapper>(this);
  bubble_->ShowBubble(std::move(bubble_view));
  SetIsActive(true);
}

TrayBubbleView* PodsOverflowTray::GetBubbleView() {
  return bubble_ ? bubble_->GetBubbleView() : nullptr;
}

views::Widget* PodsOverflowTray::GetBubbleWidget() const {
  return bubble_ ? bubble_->GetBubbleWidget() : nullptr;
}

std::u16string PodsOverflowTray::GetAccessibleNameForBubble() {
  // TODO(b/337963043): Update a11y strings.
  return u"Pods overflow bubble";
}

bool PodsOverflowTray::ShouldEnableExtraKeyboardAccessibility() {
  return Shell::Get()->accessibility_controller()->spoken_feedback().enabled();
}

void PodsOverflowTray::HideBubble(const TrayBubbleView* bubble_view) {
  HideBubbleWithView(bubble_view);
}

void PodsOverflowTray::OnTrayButtonPressed() {
  if (GetBubbleWidget()) {
    CloseBubble();
    return;
  }

  ShowBubble();
}

BEGIN_METADATA(PodsOverflowTray)
END_METADATA

}  // namespace ash