blob: 452e4920a818414eccf05ddc417b18d7f13b367f [file] [log] [blame]
// Copyright 2023 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/controls/subpage_view.h"
#include "chrome/browser/ui/layout_constants.h"
#include "chrome/browser/ui/views/chrome_layout_provider.h"
#include "components/strings/grit/components_strings.h"
#include "components/vector_icons/vector_icons.h"
#include "ui/base/interaction/element_identifier.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/ui_base_features.h"
#include "ui/views/bubble/bubble_frame_view.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/controls/button/image_button_factory.h"
#include "ui/views/controls/highlight_path_generator.h"
#include "ui/views/controls/separator.h"
#include "ui/views/layout/box_layout_view.h"
#include "ui/views/layout/flex_layout_view.h"
namespace {
constexpr int kSeparatorBottomMargin = 16;
constexpr int kBackIconSize = 20;
} // namespace
DEFINE_ELEMENT_IDENTIFIER_VALUE(kSubpageViewId);
DEFINE_ELEMENT_IDENTIFIER_VALUE(kSubpageBackButtonElementId);
SubpageView::SubpageView(views::Button::PressedCallback callback,
views::BubbleFrameView* bubble_frame_view)
: bubble_frame_view_(bubble_frame_view) {
SetProperty(views::kElementIdentifierKey, kSubpageViewId);
SetUpSubpageTitle(std::move(callback));
SetLayoutManager(std::make_unique<views::FlexLayout>())
->SetOrientation(views::LayoutOrientation::kVertical);
}
SubpageView::~SubpageView() {
if (title_) {
title_->RemoveObserver(this);
}
}
void SubpageView::SetTitle(const std::u16string& title) {
title_->SetText(title);
}
void SubpageView::SetUpSubpageTitle(views::Button::PressedCallback callback) {
const auto* layout_provider = ChromeLayoutProvider::Get();
auto title_view = std::make_unique<views::BoxLayoutView>();
title_view->SetBetweenChildSpacing(
layout_provider->GetDistanceMetric(
views::DISTANCE_RELATED_CONTROL_HORIZONTAL) -
layout_provider->GetInsetsMetric(views::INSETS_VECTOR_IMAGE_BUTTON)
.right());
auto back_button = views::CreateVectorImageButtonWithNativeTheme(
std::move(callback), vector_icons::kArrowBackChromeRefreshIcon,
kBackIconSize);
back_button->SetID(VIEW_ID_SUBPAGE_BACK_BUTTON);
back_button->SetTooltipText(l10n_util::GetStringUTF16(IDS_ACCNAME_BACK));
back_button->SetProperty(views::kInternalPaddingKey,
back_button->GetInsets());
back_button->SetProperty(views::kElementIdentifierKey,
kSubpageBackButtonElementId);
views::InstallCircleHighlightPathGenerator(back_button.get());
title_view->AddChildView(std::move(back_button));
title_ = title_view->AddChildView(
views::Builder<views::Label>()
.SetTextStyle(views::style::STYLE_HEADLINE_4)
.SetTextContext(views::style::CONTEXT_DIALOG_TITLE)
.SetHorizontalAlignment(gfx::ALIGN_LEFT)
.Build());
title_->SetMultiLine(true);
// This limits the SubpageView only works for standard the preferred width
// bubble.
int title_width =
layout_provider->GetDistanceMetric(
views::DISTANCE_BUBBLE_PREFERRED_WIDTH) -
layout_provider->GetInsetsMetric(views::INSETS_DIALOG).width() -
layout_provider->GetInsetsMetric(views::INSETS_DIALOG_TITLE).width();
views::Button* close_button = bubble_frame_view_->close_button();
if (close_button && close_button->GetVisible()) {
int close_button_width =
bubble_frame_view_->close_button()->width() +
layout_provider->GetDistanceMetric(views::DISTANCE_CLOSE_BUTTON_MARGIN);
title_width -= close_button_width;
}
title_->SetMaximumWidth(title_width);
// We need to observe the `title_` view for destruction in order to clear the
// raw_ptr to prevent a dangling reference. This is because the `title_` is
// owned by a view other than this view. That other view is destroyed prior
// to the destruction of this view.
title_->AddObserver(this);
bubble_frame_view_->SetTitleView(std::move(title_view));
}
void SubpageView::SetContentView(std::unique_ptr<views::View> content) {
CHECK(content);
if (!content_view_) {
auto* separator = AddChildView(std::make_unique<views::Separator>());
separator->SetProperty(views::kMarginsKey,
gfx::Insets::TLBR(0, 0, kSeparatorBottomMargin, 0));
} else {
RemoveChildViewT(content_view_.ExtractAsDangling());
}
content_view_ = AddChildView(std::move(content));
const int dialog_inset = views::LayoutProvider::Get()
->GetInsetsMetric(views::INSETS_DIALOG)
.left();
content_view_->SetProperty(views::kMarginsKey,
gfx::Insets::VH(0, dialog_inset));
}
void SubpageView::SetHeaderView(std::unique_ptr<views::View> header_view) {
bubble_frame_view_->SetHeaderView(std::move(header_view));
}
void SubpageView::SetFootnoteView(std::unique_ptr<views::View> footnote_view) {
bubble_frame_view_->SetFootnoteView(std::move(footnote_view));
}
void SubpageView::OnViewIsDeleting(views::View* view) {
if (view == title_.get()) {
title_->RemoveObserver(this);
title_ = nullptr;
}
}
BEGIN_METADATA(SubpageView)
END_METADATA