blob: 2720d085c2ba5e3e031b8f705dba747d09088e39 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// 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/webauthn/authenticator_request_sheet_view.h"
#include "chrome/browser/ui/views/accessibility/non_accessible_image_view.h"
#include "chrome/browser/ui/views/chrome_layout_provider.h"
#include "chrome/browser/ui/views/chrome_typography.h"
#include "chrome/browser/ui/webauthn/authenticator_request_sheet_model.h"
#include "chrome/browser/webauthn/authenticator_request_dialog_model.h"
#include "components/strings/grit/components_strings.h"
#include "components/vector_icons/vector_icons.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/native_theme/native_theme.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/controls/button/image_button_factory.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/progress_bar.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/fill_layout.h"
namespace {
// Fixed height of the illustration shown in the top half of the sheet.
constexpr int kIllustrationHeight = 148;
// Foreground/background color, and the height of the progress bar style
// activity indicator shown at the top of some sheets.
constexpr SkColor kActivityIndicateFgColor = SkColorSetRGB(0xf2, 0x99, 0x00);
constexpr SkColor kActivityIndicateBkColor = SkColorSetRGB(0xf6, 0xe6, 0xc8);
constexpr int kActivityIndicatorHeight = 4;
using ImageColorScheme = AuthenticatorRequestSheetModel::ImageColorScheme;
} // namespace
using views::BoxLayout;
AuthenticatorRequestSheetView::AuthenticatorRequestSheetView(
std::unique_ptr<AuthenticatorRequestSheetModel> model)
: model_(std::move(model)),
in_dark_mode_(
ui::NativeTheme::GetInstanceForNativeUi()->SystemDarkModeEnabled()) {}
AuthenticatorRequestSheetView::~AuthenticatorRequestSheetView() = default;
void AuthenticatorRequestSheetView::ReInitChildViews() {
RemoveAllChildViews(true /* delete_children */);
// No need to add further spacing between the upper and lower half. The image
// is designed to fill the dialog's top half without any border/margins, and
// the |lower_half| will already contain the standard dialog borders.
SetLayoutManager(std::make_unique<BoxLayout>(
BoxLayout::Orientation::kVertical, gfx::Insets(),
0 /* between_child_spacing */));
std::unique_ptr<views::View> upper_half = CreateIllustrationWithOverlays();
std::unique_ptr<views::View> lower_half = CreateContentsBelowIllustration();
AddChildView(upper_half.release());
AddChildView(lower_half.release());
InvalidateLayout();
}
views::View* AuthenticatorRequestSheetView::GetInitiallyFocusedView() {
return step_specific_content_;
}
std::unique_ptr<views::View>
AuthenticatorRequestSheetView::BuildStepSpecificContent() {
return nullptr;
}
void AuthenticatorRequestSheetView::ButtonPressed(views::Button* sender,
const ui::Event& event) {
DCHECK_EQ(sender, back_arrow_button_);
model()->OnBack();
}
std::unique_ptr<views::View>
AuthenticatorRequestSheetView::CreateIllustrationWithOverlays() {
const int illustration_width = ChromeLayoutProvider::Get()->GetDistanceMetric(
DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH);
const gfx::Size illustration_size(illustration_width, kIllustrationHeight);
// The container view has no layout, so its preferred size is hardcoded to
// match the size of the image, and all overlays are absolutely positioned.
auto image_with_overlays = std::make_unique<views::View>();
image_with_overlays->SetPreferredSize(illustration_size);
auto image_view = std::make_unique<NonAccessibleImageView>();
step_illustration_ = image_view.get();
UpdateIconImageFromModel();
image_view->SetSize(illustration_size);
image_view->SetVerticalAlignment(views::ImageView::Alignment::kLeading);
image_with_overlays->AddChildView(image_view.release());
if (model()->IsActivityIndicatorVisible()) {
auto activity_indicator = std::make_unique<views::ProgressBar>(
kActivityIndicatorHeight, false /* allow_round_corner */);
activity_indicator->SetValue(-1 /* inifinite animation */);
activity_indicator->set_foreground_color(kActivityIndicateFgColor);
activity_indicator->set_background_color(kActivityIndicateBkColor);
activity_indicator->SetPreferredSize(
gfx::Size(illustration_width, kActivityIndicatorHeight));
activity_indicator->SizeToPreferredSize();
image_with_overlays->AddChildView(activity_indicator.release());
}
if (model()->IsBackButtonVisible()) {
auto back_arrow = views::CreateVectorImageButton(this);
back_arrow->SetFocusForPlatform();
back_arrow->SetAccessibleName(l10n_util::GetStringUTF16(IDS_BACK_BUTTON));
// Position the back button so that there is the standard amount of padding
// between the top/left side of the back button and the dialog borders.
const gfx::Insets dialog_insets =
views::LayoutProvider::Get()->GetDialogInsetsForContentType(
views::CONTROL, views::CONTROL);
auto color_reference = std::make_unique<views::Label>(
base::string16(), views::style::CONTEXT_DIALOG_TITLE,
views::style::STYLE_PRIMARY);
views::SetImageFromVectorIcon(back_arrow.get(),
vector_icons::kBackArrowIcon,
color_utils::DeriveDefaultIconColor(
color_reference->GetEnabledColor()));
back_arrow->SizeToPreferredSize();
back_arrow->SetX(dialog_insets.left());
back_arrow->SetY(dialog_insets.top());
back_arrow_button_ =
image_with_overlays->AddChildView(std::move(back_arrow));
}
return image_with_overlays;
}
std::unique_ptr<views::View>
AuthenticatorRequestSheetView::CreateContentsBelowIllustration() {
auto contents = std::make_unique<views::View>();
BoxLayout* contents_layout =
contents->SetLayoutManager(std::make_unique<BoxLayout>(
BoxLayout::Orientation::kVertical, gfx::Insets(),
views::LayoutProvider::Get()->GetDistanceMetric(
views::DISTANCE_UNRELATED_CONTROL_VERTICAL)));
contents->SetBorder(views::CreateEmptyBorder(
views::LayoutProvider::Get()->GetDialogInsetsForContentType(
views::CONTROL, views::CONTROL)));
auto label_container = std::make_unique<views::View>();
label_container->SetLayoutManager(std::make_unique<BoxLayout>(
BoxLayout::Orientation::kVertical, gfx::Insets(),
views::LayoutProvider::Get()->GetDistanceMetric(
views::DISTANCE_RELATED_CONTROL_VERTICAL)));
auto title_label = std::make_unique<views::Label>(
model()->GetStepTitle(), views::style::CONTEXT_DIALOG_TITLE,
views::style::STYLE_PRIMARY);
title_label->SetMultiLine(true);
title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
title_label->SetAllowCharacterBreak(true);
label_container->AddChildView(title_label.release());
base::string16 description = model()->GetStepDescription();
if (!description.empty()) {
auto description_label = std::make_unique<views::Label>(
std::move(description), views::style::CONTEXT_MESSAGE_BOX_BODY_TEXT);
description_label->SetMultiLine(true);
description_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
description_label->SetAllowCharacterBreak(true);
label_container->AddChildView(description_label.release());
}
base::Optional<base::string16> additional_desciption =
model()->GetAdditionalDescription();
if (additional_desciption) {
auto label = std::make_unique<views::Label>(
std::move(*additional_desciption),
views::style::CONTEXT_MESSAGE_BOX_BODY_TEXT);
label->SetMultiLine(true);
label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
label->SetAllowCharacterBreak(true);
label_container->AddChildView(label.release());
}
contents->AddChildView(label_container.release());
std::unique_ptr<views::View> step_specific_content =
BuildStepSpecificContent();
if (step_specific_content) {
step_specific_content_ = step_specific_content.get();
contents->AddChildView(step_specific_content.release());
contents_layout->SetFlexForView(step_specific_content_, 1);
}
return contents;
}
void AuthenticatorRequestSheetView::OnThemeChanged() {
ui::NativeTheme* theme = GetNativeTheme();
if (theme != ui::NativeTheme::GetInstanceForNativeUi())
return;
bool in_dark_mode = theme->SystemDarkModeEnabled();
if (in_dark_mode == in_dark_mode_)
return;
in_dark_mode_ = in_dark_mode;
UpdateIconImageFromModel();
}
void AuthenticatorRequestSheetView::UpdateIconImageFromModel() {
gfx::IconDescription icon_description(
model()->GetStepIllustration(in_dark_mode_ ? ImageColorScheme::kDark
: ImageColorScheme::kLight),
0 /* automatic dip_size */, SK_ColorBLACK, base::TimeDelta(),
gfx::kNoneIcon);
step_illustration_->SetImage(gfx::CreateVectorIcon(icon_description));
}