chromium/ash/app_list/views/apps_collections_dismiss_dialog.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/app_list/views/apps_collections_dismiss_dialog.h"

#include <memory>
#include <utility>

#include "ash/public/cpp/ash_typography.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_provider.h"
#include "ash/style/dark_light_mode_controller_impl.h"
#include "ash/style/pill_button.h"
#include "ash/style/typography.h"
#include "base/functional/bind.h"
#include "base/functional/callback_forward.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/mojom/ui_base_types.mojom-shared.h"
#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
#include "ui/color/color_id.h"
#include "ui/compositor/layer.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/background.h"
#include "ui/views/controls/label.h"
#include "ui/views/highlight_border.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/style/typography.h"
#include "ui/views/view_class_properties.h"
#include "ui/views/view_shadow.h"
#include "ui/views/widget/widget.h"

namespace ash {

namespace {

constexpr int kDialogWidth = 360;

constexpr gfx::Insets kDialogContentInsets = gfx::Insets::VH(20, 24);
constexpr float kDialogRoundedCornerRadius = 16.0f;
constexpr int kDialogShadowElevation = 3;

constexpr int kMarginBetweenTitleAndBody = 8;
constexpr int kMarginBetweenBodyAndButtons = 20;
constexpr int kMarginBetweenButtons = 8;

}  // namespace

AppsCollectionsDismissDialog::AppsCollectionsDismissDialog(
    base::OnceClosure confirm_callback)
    : confirm_callback_(std::move(confirm_callback)) {
  SetModalType(ui::mojom::ModalType::kWindow);

  SetPaintToLayer();
  layer()->SetBackgroundBlur(ColorProvider::kBackgroundBlurSigma);
  layer()->SetBackdropFilterQuality(ColorProvider::kBackgroundBlurQuality);

  view_shadow_ =
      std::make_unique<views::ViewShadow>(this, kDialogShadowElevation);
  view_shadow_->SetRoundedCornerRadius(kDialogRoundedCornerRadius);

  SetLayoutManager(std::make_unique<views::BoxLayout>(
      views::BoxLayout::Orientation::kVertical, kDialogContentInsets));

  SetBackground(views::CreateThemedRoundedRectBackground(
      cros_tokens::kCrosSysBaseElevated, kDialogRoundedCornerRadius));

  SetBorder(std::make_unique<views::HighlightBorder>(
      kDialogRoundedCornerRadius,
      views::HighlightBorder::Type::kHighlightBorderOnShadow));

  // Add dialog title.
  title_ =
      AddChildView(std::make_unique<views::Label>(l10n_util::GetStringUTF16(
          IDS_ASH_LAUNCHER_APPS_COLLECTIONS_DISMISS_DIALOG_TITLE)));
  TypographyProvider::Get()->StyleLabel(TypographyToken::kCrosTitle1, *title_);
  title_->SetEnabledColorId(cros_tokens::kCrosSysOnSurface);
  title_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
  title_->SetAutoColorReadabilityEnabled(false);
  // Needs to paint to layer so it's stacked above `this` view.
  title_->SetPaintToLayer();
  title_->layer()->SetFillsBoundsOpaquely(false);
  // Ignore labels for accessibility - the accessible name is defined for the
  // whole dialog view.
  title_->GetViewAccessibility().SetIsIgnored(true);

  // Add dialog body.
  auto* body =
      AddChildView(std::make_unique<views::Label>(l10n_util::GetStringUTF16(
          IDS_ASH_LAUNCHER_APPS_COLLECTIONS_DISMISS_DIALOG_SUBTITLE)));
  body->SetProperty(views::kMarginsKey,
                    gfx::Insets::TLBR(kMarginBetweenTitleAndBody, 0,
                                      kMarginBetweenBodyAndButtons, 0));
  TypographyProvider::Get()->StyleLabel(TypographyToken::kCrosBody1, *body);
  body->SetEnabledColorId(cros_tokens::kCrosSysOnSurface);
  body->SetHorizontalAlignment(gfx::ALIGN_LEFT);
  body->SetMultiLine(true);
  body->SetAllowCharacterBreak(true);
  body->SetAutoColorReadabilityEnabled(false);
  // Needs to paint to layer so it's stacked above `this` view.
  body->SetPaintToLayer();
  body->layer()->SetFillsBoundsOpaquely(false);
  // Ignore labels for accessibility - the accessible name is defined for the
  // whole dialog view.
  body->GetViewAccessibility().SetIsIgnored(true);

  auto run_callback = [](AppsCollectionsDismissDialog* dialog, bool accept) {
    if (!dialog->confirm_callback_) {
      return;
    }
    if (accept) {
      std::move(dialog->confirm_callback_).Run();
    }

    dialog->GetWidget()->CloseWithReason(
        accept ? views::Widget::ClosedReason::kAcceptButtonClicked
               : views::Widget::ClosedReason::kCancelButtonClicked);
  };

  // Add button row.
  auto* button_row = AddChildView(std::make_unique<views::View>());
  button_row
      ->SetLayoutManager(std::make_unique<views::BoxLayout>(
          views::BoxLayout::Orientation::kHorizontal, gfx::Insets(),
          kMarginBetweenButtons))
      ->set_main_axis_alignment(views::BoxLayout::MainAxisAlignment::kEnd);

  cancel_button_ = button_row->AddChildView(std::make_unique<ash::PillButton>(
      views::Button::PressedCallback(
          base::BindRepeating(run_callback, base::Unretained(this), false)),
      l10n_util::GetStringUTF16(
          IDS_ASH_LAUNCHER_APPS_COLLECTIONS_DISMISS_DIALOG_CANCEL),
      PillButton::Type::kDefaultWithoutIcon, nullptr));
  accept_button_ = button_row->AddChildView(std::make_unique<ash::PillButton>(
      views::Button::PressedCallback(
          base::BindRepeating(run_callback, base::Unretained(this), true)),
      l10n_util::GetStringUTF16(
          IDS_ASH_LAUNCHER_APPS_COLLECTIONS_DISMISS_DIALOG_EXIT),
      PillButton::Type::kPrimaryWithoutIcon, nullptr));

  GetViewAccessibility().SetRole(ax::mojom::Role::kAlertDialog);
  GetViewAccessibility().SetName(base::JoinString(
      {l10n_util::GetStringUTF16(
           IDS_ASH_LAUNCHER_APPS_COLLECTIONS_DISMISS_DIALOG_TITLE),
       l10n_util::GetStringUTF16(
           IDS_ASH_LAUNCHER_APPS_COLLECTIONS_DISMISS_DIALOG_SUBTITLE)},
      u", "));
}

AppsCollectionsDismissDialog::~AppsCollectionsDismissDialog() {}

gfx::Size AppsCollectionsDismissDialog::CalculatePreferredSize(
    const views::SizeBounds& available_size) const {
  const int default_width = kDialogWidth;
  return gfx::Size(
      default_width,
      GetLayoutManager()->GetPreferredHeightForWidth(this, default_width));
}

BEGIN_METADATA(AppsCollectionsDismissDialog)
END_METADATA

}  // namespace ash