chromium/ash/components/arc/compat_mode/resize_confirmation_dialog_view.cc

// Copyright 2021 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/components/arc/compat_mode/resize_confirmation_dialog_view.h"

#include <memory>

#include "ash/components/arc/compat_mode/overlay_dialog.h"
#include "ash/components/arc/compat_mode/style/arc_color_provider.h"
#include "ash/frame/non_client_frame_view_ash.h"
#include "ash/style/ash_color_id.h"
#include "ash/style/checkbox_group.h"
#include "ash/style/pill_button.h"
#include "ash/style/typography.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/scoped_observation.h"
#include "chromeos/constants/chromeos_features.h"
#include "components/strings/grit/components_strings.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/mojom/dialog_button.mojom.h"
#include "ui/color/color_id.h"
#include "ui/color/color_provider.h"
#include "ui/strings/grit/ui_strings.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/bubble/bubble_border.h"
#include "ui/views/bubble/bubble_dialog_delegate_view.h"
#include "ui/views/controls/button/checkbox.h"
#include "ui/views/controls/button/md_text_button.h"
#include "ui/views/layout/layout_provider.h"
#include "ui/views/style/typography.h"
#include "ui/views/style/typography_provider.h"
#include "ui/views/view_class_properties.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_observer.h"

namespace arc {

void ResizeConfirmationDialogView::TestApi::SelectDoNotAskCheckbox() {
  view_->do_not_ask_checkbox_jelly_->SetSelected(true);
}

ResizeConfirmationDialogView::ResizeConfirmationDialogView(
    views::Widget* parent,
    ResizeConfirmationCallback callback)
    : callback_(std::move(callback)) {
  // Setup delegate.
  SetArrow(views::BubbleBorder::Arrow::FLOAT);
  SetButtons(static_cast<int>(ui::mojom::DialogButton::kNone));
  set_parent_window(parent->GetNativeWindow());
  set_title_margins(gfx::Insets());
  SetTitle(
      l10n_util::GetStringUTF16(IDS_ASH_ARC_APP_COMPAT_RESIZE_CONFIRM_TITLE));
  SetShowTitle(false);
  set_margins(gfx::Insets());
  SetAnchorView(parent->GetContentsView());
  SetAccessibleWindowRole(ax::mojom::Role::kDialog);
  set_adjust_if_offscreen(false);
  set_close_on_deactivate(true);

  // Setup view.
  auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>());
  layout->SetOrientation(views::BoxLayout::Orientation::kVertical);
  layout->set_main_axis_alignment(views::BoxLayout::MainAxisAlignment::kStart);
  layout->set_inside_border_insets(gfx::Insets::TLBR(32, 32, 28, 32));
  layout->set_between_child_spacing(16);

  const raw_ptr<views::Label> title = AddChildView(
      views::Builder<views::Label>()
          .SetText(l10n_util::GetStringUTF16(
              IDS_ASH_ARC_APP_COMPAT_RESIZE_CONFIRM_TITLE))
          .SetTextContext(views::style::CONTEXT_DIALOG_TITLE)
          .SetMultiLine(true)
          .SetHorizontalAlignment(gfx::ALIGN_LEFT)
          .SetAllowCharacterBreak(true)
          .SetFontList(
              views::TypographyProvider::Get()
                  .GetFont(views::style::TextContext::CONTEXT_DIALOG_TITLE,
                           views::style::TextStyle::STYLE_PRIMARY)
                  .DeriveWithWeight(gfx::Font::Weight::MEDIUM))
          .Build());
  ash::TypographyProvider::Get()->StyleLabel(
      ash::TypographyToken::kCrosDisplay7, *title);
  title->SetEnabledColorId(cros_tokens::kCrosSysOnSurface);

  AddChildView(MakeContentsView());
  AddChildView(MakeButtonsView());
}

ResizeConfirmationDialogView::~ResizeConfirmationDialogView() = default;

gfx::Size ResizeConfirmationDialogView::CalculatePreferredSize(
    const views::SizeBounds& available_size) const {
  views::LayoutProvider* provider = views::LayoutProvider::Get();
  int width = provider->GetDistanceMetric(
      views::DistanceMetric::DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH);

  const int kHorizontalMarginDp = 36;
  const auto* widget = GetWidget();
  if (widget && widget->parent()) {
    width =
        std::min(width, widget->parent()->GetWindowBoundsInScreen().width() -
                            kHorizontalMarginDp * 2);
  }
  return gfx::Size(width,
                   GetLayoutManager()->GetPreferredHeightForWidth(this, width));
}

void ResizeConfirmationDialogView::AddedToWidget() {
  const int kCornerRadius = 20;
  auto* const frame = GetBubbleFrameView();
  if (frame) {
    frame->SetCornerRadius(kCornerRadius);
  }

  widget_observation_.Observe(GetWidget());
}

void ResizeConfirmationDialogView::OnThemeChanged() {
  views::BubbleDialogDelegateView::OnThemeChanged();
}

void ResizeConfirmationDialogView::OnWidgetClosing(views::Widget* widget) {
  if (!callback_) {
    return;
  }
  std::move(callback_).Run(/*accept=*/false, /*do_not_ask_again=*/false);
}

std::unique_ptr<views::View> ResizeConfirmationDialogView::MakeContentsView() {
  auto contents_view =
      views::Builder<views::BoxLayoutView>()
          .SetOrientation(views::BoxLayout::Orientation::kVertical)
          .SetBetweenChildSpacing(16)
          .Build();

  const raw_ptr<views::Label> body = contents_view->AddChildView(
      views::Builder<views::Label>()
          .SetText(l10n_util::GetStringUTF16(
              IDS_ASH_ARC_APP_COMPAT_RESIZE_CONFIRM_BODY))
          .SetTextContext(views::style::CONTEXT_DIALOG_BODY_TEXT)
          .SetTextStyle(views::style::STYLE_SECONDARY)
          .SetHorizontalAlignment(gfx::ALIGN_LEFT)
          .SetMultiLine(true)
          .Build());
  ash::TypographyProvider::Get()->StyleLabel(ash::TypographyToken::kCrosBody1,
                                             *body);
  body->SetEnabledColorId(cros_tokens::kCrosSysOnSurfaceVariant);

  const raw_ptr<ash::CheckboxGroup> checkbox_group =
      contents_view->AddChildView(std::make_unique<ash::CheckboxGroup>(
          bounds().width() - 32 * 2, gfx::Insets::TLBR(0, 0, 8, 0), 0,
          gfx::Insets(), ash::Checkbox::kImageLabelSpacingDP));
  do_not_ask_checkbox_jelly_ = checkbox_group->AddButton(
      ash::OptionButtonBase::PressedCallback(),
      l10n_util::GetStringUTF16(
          IDS_ASH_ARC_APP_COMPAT_RESIZE_CONFIRM_DONT_ASK_ME));
  do_not_ask_checkbox_jelly_->SetLabelStyle(ash::TypographyToken::kCrosButton2);
  do_not_ask_checkbox_jelly_->SetLabelColorId(cros_tokens::kCrosSysOnSurface);
  return contents_view;
}

std::unique_ptr<views::View> ResizeConfirmationDialogView::MakeButtonsView() {
  views::LayoutProvider* provider = views::LayoutProvider::Get();
  auto builder =
      views::Builder<views::BoxLayoutView>()
          .SetOrientation(views::BoxLayout::Orientation::kHorizontal)
          .SetMainAxisAlignment(views::BoxLayout::MainAxisAlignment::kEnd)
          .SetBetweenChildSpacing(provider->GetDistanceMetric(
              views::DistanceMetric::DISTANCE_RELATED_BUTTON_HORIZONTAL));

  builder.AddChildren(
      views::Builder<ash::PillButton>()  // Cancel button.
          .CopyAddressTo(&cancel_button_)
          .SetCallback(base::BindRepeating(
              &ResizeConfirmationDialogView::OnButtonClicked,
              base::Unretained(this), false,
              views::Widget::ClosedReason::kCancelButtonClicked))
          .SetText(l10n_util::GetStringUTF16(IDS_APP_CANCEL))
          .SetIsDefault(false)
          .SetPillButtonType(ash::PillButton::kSecondaryLargeWithoutIcon),
      views::Builder<ash::PillButton>()  // Accept button.
          .CopyAddressTo(&accept_button_)
          .SetCallback(base::BindRepeating(
              &ResizeConfirmationDialogView::OnButtonClicked,
              base::Unretained(this), true,
              views::Widget::ClosedReason::kCancelButtonClicked))
          .SetText(l10n_util::GetStringUTF16(
              IDS_ASH_ARC_APP_COMPAT_RESIZE_CONFIRM_ACCEPT))
          .SetIsDefault(true)
          .SetPillButtonType(ash::PillButton::kPrimaryLargeWithoutIcon));
  return std::move(builder).Build();
}

void ResizeConfirmationDialogView::OnButtonClicked(
    bool accept,
    views::Widget::ClosedReason close_reason) {
  if (!callback_)
    return;
  std::move(callback_).Run(accept, do_not_ask_checkbox_jelly_->selected());

  auto* const widget = GetWidget();
  if (widget) {
    widget->CloseWithReason(close_reason);
  }
}

void ResizeConfirmationDialogView::Show(views::Widget* parent,
                                        ResizeConfirmationCallback callback) {
  auto remove_overlay = base::BindOnce(
      &OverlayDialog::CloseIfAny, base::Unretained(parent->GetNativeWindow()));

  auto dialog_view = std::make_unique<ResizeConfirmationDialogView>(
      parent, std::move(callback).Then(std::move(remove_overlay)));

  OverlayDialog::Show(
      parent->GetNativeWindow(),
      base::BindOnce(&ResizeConfirmationDialogView::OnButtonClicked,
                     base::Unretained(dialog_view.get()), /*accept=*/false,
                     views::Widget::ClosedReason::kUnspecified),
      /*dialog_view=*/nullptr);

  views::BubbleDialogDelegateView::CreateBubble(std::move(dialog_view))->Show();
}

BEGIN_METADATA(ResizeConfirmationDialogView)
END_METADATA

}  // namespace arc