blob: 6ced69dd1ed5ddfb503525942a68bc007d8a03b5 [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/infobars/confirm_infobar.h"
#include <memory>
#include <optional>
#include <utility>
#include "base/functional/bind.h"
#include "build/build_config.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/browser/ui/views/chrome_layout_provider.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/ui_base_features.h"
#include "ui/base/window_open_disposition.h"
#include "ui/views/controls/button/label_button.h"
#include "ui/views/controls/button/label_button_border.h"
#include "ui/views/controls/button/md_text_button.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/link.h"
#include "ui/views/layout/flex_layout.h"
#include "ui/views/style/platform_style.h"
#include "ui/views/view_class_properties.h"
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(ConfirmInfoBar, kOkButtonElementId);
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(ConfirmInfoBar, kCancelButtonElementId);
ConfirmInfoBar::ConfirmInfoBar(std::unique_ptr<ConfirmInfoBarDelegate> delegate)
: InfoBarView(std::move(delegate)) {
SetProperty(views::kElementIdentifierKey, kInfoBarElementId);
auto* delegate_ptr = GetDelegate();
// TODO: (457800852) Create hierarchy of views for Infobar.
// Set the layout on the content container for flex layout.
if (base::FeatureList::IsEnabled(features::kInfobarRefresh)) {
auto* layout = content_container()->SetLayoutManager(
std::make_unique<views::FlexLayout>());
layout->SetOrientation(views::LayoutOrientation::kHorizontal)
.SetCrossAxisAlignment(views::LayoutAlignment::kCenter);
}
// Create the label and set the eliding behaviour.
label_ = AddContentChildView(CreateLabel(delegate_ptr->GetMessageText()));
label_->SetElideBehavior(delegate_ptr->GetMessageElideBehavior());
// Set properties on the created label_ for flex layout.
if (base::FeatureList::IsEnabled(features::kInfobarRefresh)) {
int kHorizontalDistanceLabel =
ChromeLayoutProvider::Get()->GetDistanceMetric(
views::DISTANCE_UNRELATED_CONTROL_HORIZONTAL);
if (GetDelegate()->ShouldShowLinkBeforeButton()) {
kHorizontalDistanceLabel = 4;
}
// Set horizontal distance for label for flex layout.
label_->SetProperty(views::kMarginsKey,
std::make_unique<gfx::Insets>(gfx::Insets::TLBR(
0, 0, 0, kHorizontalDistanceLabel)));
label_->SetProperty(
views::kFlexBehaviorKey,
views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToZero,
views::MaximumFlexSizeRule::kPreferred)
.WithWeight(1));
}
// Create both the ok and cancel buttons.
const int buttons = delegate_ptr->GetButtons();
const auto create_button = [&](ConfirmInfoBarDelegate::InfoBarButton type,
void (ConfirmInfoBar::*click_function)()) {
auto button = std::make_unique<views::MdTextButton>(
base::BindRepeating(click_function, base::Unretained(this)),
GetDelegate()->GetButtonLabel(type));
auto* button_ptr = button.get();
// Set custom padding on the buttons.
if (base::FeatureList::IsEnabled(features::kInfobarRefresh)) {
button_ptr->SetCustomPadding(
gfx::Insets::VH(ChromeLayoutProvider::Get()->GetDistanceMetric(
DISTANCE_INFOBAR_BUTTON_VERTICAL_PADDING),
ChromeLayoutProvider::Get()->GetDistanceMetric(
DISTANCE_INFOBAR_BUTTON_HORIZONTAL_PADDING)));
}
button_ptr->SetProperty(
views::kMarginsKey,
gfx::Insets::VH(ChromeLayoutProvider::Get()->GetDistanceMetric(
DISTANCE_TOAST_CONTROL_VERTICAL),
0));
const bool is_default_button =
type == buttons || type == ConfirmInfoBarDelegate::BUTTON_OK;
button_ptr->SetStyle(is_default_button ? ui::ButtonStyle::kProminent
: ui::ButtonStyle::kTonal);
button_ptr->SetImageModel(views::Button::STATE_NORMAL,
delegate_ptr->GetButtonImage(type));
button_ptr->SetEnabled(delegate_ptr->GetButtonEnabled(type));
button_ptr->SetTooltipText(delegate_ptr->GetButtonTooltip(type));
AddContentChildView(std::move(button));
return button_ptr;
};
if (buttons & ConfirmInfoBarDelegate::BUTTON_OK) {
ok_button_ = create_button(ConfirmInfoBarDelegate::BUTTON_OK,
&ConfirmInfoBar::OkButtonPressed);
ok_button_->SetProperty(views::kElementIdentifierKey, kOkButtonElementId);
if (base::FeatureList::IsEnabled(features::kInfobarRefresh)) {
// Set the margin for FlexLayout for ok button.
ok_button_->SetProperty(
views::kMarginsKey,
std::make_unique<gfx::Insets>(gfx::Insets::TLBR(0, 0, 0, 0)));
}
}
if (buttons & ConfirmInfoBarDelegate::BUTTON_CANCEL) {
cancel_button_ = create_button(ConfirmInfoBarDelegate::BUTTON_CANCEL,
&ConfirmInfoBar::CancelButtonPressed);
cancel_button_->SetProperty(views::kElementIdentifierKey,
kCancelButtonElementId);
if (base::FeatureList::IsEnabled(features::kInfobarRefresh)) {
// Set the margin for FlexLayout for cancel button.
cancel_button_->SetProperty(
views::kMarginsKey,
std::make_unique<gfx::Insets>(
gfx::Insets::TLBR(0,
ChromeLayoutProvider::Get()->GetDistanceMetric(
views::DISTANCE_RELATED_BUTTON_HORIZONTAL),
0, 0)));
}
}
auto link_unique_ptr = CreateLink(delegate_ptr->GetLinkText(),
delegate_ptr->GetLinkAccessibleText());
if (!link_unique_ptr) {
return;
}
link_ = link_unique_ptr.get();
// Add the link to the infobar view and reorder it to be placed before the
// close button.
if (base::FeatureList::IsEnabled(features::kInfobarRefresh)) {
if (GetDelegate()->ShouldShowLinkBeforeButton()) {
AddContentChildView(std::move(link_unique_ptr));
} else {
AddViewBeforeCloseButton(std::move(link_unique_ptr));
}
// Add margins for spacing for flex layout.
link_->SetProperty(views::kMarginsKey, std::make_unique<gfx::Insets>(
gfx::Insets::TLBR(0, 0, 0, 0)));
} else {
AddContentChildView(std::move(link_unique_ptr));
}
}
ConfirmInfoBar::~ConfirmInfoBar() = default;
void ConfirmInfoBar::Layout(PassKey) {
if (GetLayoutManager()) {
// If using flex layout return as we are using layout manager.
LayoutSuperclass<InfoBarView>(this);
return;
}
LayoutSuperclass<InfoBarView>(this);
if (ok_button_) {
ok_button_->SizeToPreferredSize();
}
if (cancel_button_) {
cancel_button_->SizeToPreferredSize();
}
int x = GetStartX();
Views views;
views.push_back(label_.get());
views.push_back(link_.get());
AssignWidths(&views, std::max(0, GetEndX() - x - NonLabelWidth()));
ChromeLayoutProvider* layout_provider = ChromeLayoutProvider::Get();
label_->SetPosition(gfx::Point(x, OffsetY(label_)));
if (!label_->GetText().empty()) {
x = label_->bounds().right() +
layout_provider->GetDistanceMetric(
DISTANCE_INFOBAR_HORIZONTAL_ICON_LABEL_PADDING);
}
if (GetDelegate()->ShouldShowLinkBeforeButton()) {
const int link_spacing = layout_provider->GetDistanceMetric(
DISTANCE_SIDE_PANEL_HEADER_INTERIOR_MARGIN_HORIZONTAL);
link_->SetPosition(
gfx::Point(label_->bounds().right() + link_spacing, OffsetY(link_)));
if (!link_->GetText().empty()) {
x = link_->bounds().right() +
layout_provider->GetDistanceMetric(
views::DISTANCE_RELATED_LABEL_HORIZONTAL);
}
} else {
link_->SetPosition(gfx::Point(GetEndX() - link_->width(), OffsetY(link_)));
}
// Add buttons into a vector to be displayed in an ordered row.
// Depending on the PlatformStyle, reverse the vector so the ok button will be
// on the correct leading style.
std::vector<views::MdTextButton*> order_of_buttons;
if (ok_button_) {
order_of_buttons.push_back(ok_button_);
}
if (cancel_button_) {
order_of_buttons.push_back(cancel_button_);
}
if constexpr (!views::PlatformStyle::kIsOkButtonLeading) {
std::ranges::reverse(order_of_buttons);
}
for (views::MdTextButton* button : order_of_buttons) {
button->SetPosition(gfx::Point(x, OffsetY(button)));
x = button->bounds().right() +
layout_provider->GetDistanceMetric(
views::DISTANCE_RELATED_BUTTON_HORIZONTAL);
}
}
void ConfirmInfoBar::OkButtonPressed() {
if (!owner()) {
return; // We're closing; don't call anything, it might access the owner.
}
if (GetDelegate()->Accept()) {
RemoveSelf();
}
}
void ConfirmInfoBar::CancelButtonPressed() {
if (!owner()) {
return; // We're closing; don't call anything, it might access the owner.
}
if (GetDelegate()->Cancel()) {
RemoveSelf();
}
}
ConfirmInfoBarDelegate* ConfirmInfoBar::GetDelegate() {
return delegate()->AsConfirmInfoBarDelegate();
}
const ConfirmInfoBarDelegate* ConfirmInfoBar::GetDelegate() const {
return delegate()->AsConfirmInfoBarDelegate();
}
int ConfirmInfoBar::GetContentMinimumWidth() const {
if (base::FeatureList::IsEnabled(features::kInfobarRefresh)) {
// With using flex layout, no manual calculations are needed.
return 0;
}
return label_->GetMinimumSize().width() + link_->GetMinimumSize().width() +
NonLabelWidth();
}
int ConfirmInfoBar::GetContentPreferredWidth() const {
return label_->GetPreferredSize().width() +
link_->GetPreferredSize().width() + NonLabelWidth();
}
int ConfirmInfoBar::NonLabelWidth() const {
ChromeLayoutProvider* layout_provider = ChromeLayoutProvider::Get();
const bool should_show_link_before_button =
GetDelegate()->ShouldShowLinkBeforeButton();
// The link should be shown before the button if the custom layout is
// enabled. The spacing between the label and the link is different than the
// spacing between the link and the button.
const int label_spacing =
should_show_link_before_button
? layout_provider->GetDistanceMetric(
DISTANCE_INFOBAR_HORIZONTAL_ICON_LABEL_PADDING)
: layout_provider->GetDistanceMetric(
views::DISTANCE_RELATED_LABEL_HORIZONTAL);
const int button_spacing = layout_provider->GetDistanceMetric(
views::DISTANCE_RELATED_BUTTON_HORIZONTAL);
int spacing_from_previous = label_->GetText().empty() ? 0 : label_spacing;
int width = 0;
width += ok_button_ ? (std::exchange(spacing_from_previous, button_spacing) +
ok_button_->width())
: 0;
width +=
cancel_button_ ? (spacing_from_previous + cancel_button_->width()) : 0;
width += (width && !link_->GetText().empty()) ? label_spacing : 0;
return width;
}
BEGIN_METADATA(ConfirmInfoBar)
END_METADATA