blob: 9df39e1535f9ecf71f638b465cfdf39c6858c698 [file] [log] [blame]
// Copyright 2021 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 "ash/style/system_toast_style.h"
#include <string>
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_provider.h"
#include "ash/style/pill_button.h"
#include "ash/system/toast/toast_overlay.h"
#include "ash/wm/work_area_insets.h"
#include "base/strings/strcat.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/compositor/layer.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/gfx/text_elider.h"
#include "ui/gfx/text_utils.h"
#include "ui/views/background.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/highlight_border.h"
#include "ui/views/layout/box_layout.h"
namespace ash {
namespace {
// UI constants in DIP (Density Independent Pixel).
constexpr int kToastTextMaximumWidth = 512;
constexpr int kOneLineHorizontalSpacing = 16;
constexpr int kTwoLineHorizontalSpacing = 24;
constexpr int kSpacingBetweenLabelAndButton = 16;
constexpr int kOnelineButtonRightSpacing = 2;
constexpr int kTwolineButtonRightSpacing = 12;
constexpr int kToastLabelVerticalSpacing = 8;
constexpr int kManagedIconSize = 32;
// The label inside SystemToastStyle, which allows two lines at maximum.
class SystemToastInnerLabel : public views::Label {
public:
METADATA_HEADER(SystemToastInnerLabel);
explicit SystemToastInnerLabel(const std::u16string& text)
: views::Label(text) {
SetHorizontalAlignment(gfx::ALIGN_LEFT);
SetAutoColorReadabilityEnabled(false);
SetMultiLine(true);
SetMaximumWidth(kToastTextMaximumWidth);
SetMaxLines(2);
SetSubpixelRenderingEnabled(false);
SetFontList(views::Label::GetDefaultFontList().Derive(
2, gfx::Font::FontStyle::NORMAL, gfx::Font::Weight::NORMAL));
}
SystemToastInnerLabel(const SystemToastInnerLabel&) = delete;
SystemToastInnerLabel& operator=(const SystemToastInnerLabel&) = delete;
~SystemToastInnerLabel() override = default;
private:
// views::Label:
void OnThemeChanged() override {
views::Label::OnThemeChanged();
SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
AshColorProvider::ContentLayerType::kTextColorPrimary));
}
};
BEGIN_METADATA(SystemToastInnerLabel, views::Label)
END_METADATA
SkColor GetBackgroundColor() {
return AshColorProvider::Get()->GetBaseLayerColor(
AshColorProvider::BaseLayerType::kTransparent80);
}
// TODO(crbug/1294449): Handle the case when a word can't be fitted into
// one-line where additional spaces need to be padded.
bool FormatDisplayLabelText(views::Label* label,
std::u16string& out_display_text) {
const gfx::FontList& font_list = label->font_list();
const std::u16string& text = label->GetText();
const int label_text_width = gfx::GetStringWidth(text, font_list);
out_display_text = text;
const bool two_line = label_text_width > kToastTextMaximumWidth;
if (two_line) {
// Find the index to split string into multiple lines so that the width of
// each row is within `kToastTextMaximumWidth` limit.
const int split_index =
text.length() * kToastTextMaximumWidth / label_text_width;
out_display_text = base::StrCat(
{text.substr(0, split_index), u"\n" + text.substr(split_index)});
}
out_display_text = gfx::ElideText(
out_display_text, font_list, kToastTextMaximumWidth * 2, gfx::ELIDE_TAIL);
return two_line;
}
} // namespace
SystemToastStyle::SystemToastStyle(base::RepeatingClosure dismiss_callback,
const std::u16string& text,
const std::u16string& dismiss_text,
const bool is_managed) {
SetPaintToLayer();
layer()->SetFillsBoundsOpaquely(false);
layer()->SetBackgroundBlur(ColorProvider::kBackgroundBlurSigma);
if (is_managed) {
managed_icon_ = AddChildView(std::make_unique<views::ImageView>());
managed_icon_->SetPreferredSize(
gfx::Size(kManagedIconSize, kManagedIconSize));
}
label_ = AddChildView(std::make_unique<SystemToastInnerLabel>(text));
std::u16string display_text;
const bool two_line = FormatDisplayLabelText(label_, display_text);
label_->SetText(display_text);
if (!dismiss_text.empty()) {
button_ = AddChildView(
std::make_unique<PillButton>(std::move(dismiss_callback), dismiss_text,
PillButton::Type::kIconlessAccentFloating,
/*icon=*/nullptr));
button_->SetFocusBehavior(views::View::FocusBehavior::ACCESSIBLE_ONLY);
}
int toast_height = kToastLabelVerticalSpacing * 2 + label_->GetLineHeight();
if (two_line)
toast_height += label_->GetLineHeight();
const int vertical_spacing =
button_
? std::min(kToastLabelVerticalSpacing,
(toast_height - button_->GetPreferredSize().height()) / 2)
: kToastLabelVerticalSpacing;
auto insets =
gfx::Insets::VH(vertical_spacing, two_line ? kTwoLineHorizontalSpacing
: kOneLineHorizontalSpacing);
if (button_) {
insets.set_right(two_line ? kTwolineButtonRightSpacing
: kOnelineButtonRightSpacing);
}
auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal, insets,
button_ ? kSpacingBetweenLabelAndButton : 0));
layout->set_cross_axis_alignment(
views::BoxLayout::CrossAxisAlignment::kCenter);
layout->SetFlexForView(label_, 1);
const int toast_corner_radius = toast_height / 2.f;
layer()->SetRoundedCornerRadius(gfx::RoundedCornersF(toast_corner_radius));
SetBackground(views::CreateRoundedRectBackground(GetBackgroundColor(),
toast_corner_radius));
if (features::IsDarkLightModeEnabled()) {
SetBorder(std::make_unique<views::HighlightBorder>(
toast_corner_radius, views::HighlightBorder::Type::kHighlightBorder1,
/*use_light_colors=*/false));
}
}
SystemToastStyle::~SystemToastStyle() = default;
void SystemToastStyle::SetText(const std::u16string& text) {
label_->SetText(text);
}
void SystemToastStyle::OnThemeChanged() {
views::View::OnThemeChanged();
background()->SetNativeControlColor(GetBackgroundColor());
if (managed_icon_) {
managed_icon_->SetImage(gfx::CreateVectorIcon(
kSystemMenuBusinessIcon,
AshColorProvider::Get()->GetContentLayerColor(
AshColorProvider::ContentLayerType::kIconColorPrimary)));
}
SchedulePaint();
}
BEGIN_METADATA(SystemToastStyle, views::View)
END_METADATA
} // namespace ash