blob: d293d3325c9c230db998625aa8443594c4714041 [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/tabs/fade_footer_view.h"
#include "base/byte_count.h"
#include "base/check.h"
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/ui/color/chrome_color_id.h"
#include "chrome/browser/ui/layout_constants.h"
#include "chrome/browser/ui/tabs/alert/tab_alert.h"
#include "chrome/browser/ui/tabs/alert/tab_alert_controller.h"
#include "chrome/browser/ui/tabs/alert/tab_alert_icon.h"
#include "chrome/browser/ui/views/tabs/alert_indicator_button.h"
#include "chrome/grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/models/image_model.h"
#include "ui/base/text/bytes_formatting.h"
#include "ui/base/ui_base_features.h"
#include "ui/color/color_id.h"
#include "ui/compositor/layer.h"
#include "ui/gfx/color_palette.h"
#include "ui/views/border.h"
#include "ui/views/layout/flex_layout_types.h"
#include "ui/views/layout/layout_provider.h"
#include "ui/views/view.h"
#include "ui/views/view_class_properties.h"
namespace {
// Spacing to separate the icon from its corresponding label.
constexpr int kIconLabelSpacing = 8;
// Margins used to surround the entire footer contents.
constexpr auto kFooterMargins = gfx::Insets::VH(12, 12);
// Spacing used to separate two footer rows.
constexpr int kFooterRowSpacing = 8;
ui::ColorId GetTabAlertColor(tabs::TabAlert alert_state) {
// Hover card background color isn't affected by third party themes so icons
// need to use a different color id from those used in the
// AlertIndicatorButton.
ui::ColorId icon_color = gfx::kPlaceholderColor;
switch (alert_state) {
case tabs::TabAlert::kMediaRecording:
case tabs::TabAlert::kAudioRecording:
case tabs::TabAlert::kVideoRecording:
case tabs::TabAlert::kDesktopCapturing:
icon_color = kColorHoverCardTabAlertMediaRecordingIcon;
break;
case tabs::TabAlert::kTabCapturing:
case tabs::TabAlert::kPipPlaying:
case tabs::TabAlert::kActorAccessing:
case tabs::TabAlert::kGlicAccessing:
case tabs::TabAlert::kGlicSharing:
icon_color = kColorHoverCardTabAlertPipPlayingIcon;
break;
case tabs::TabAlert::kAudioPlaying:
case tabs::TabAlert::kAudioMuting:
case tabs::TabAlert::kBluetoothConnected:
case tabs::TabAlert::kBluetoothScanActive:
case tabs::TabAlert::kUsbConnected:
case tabs::TabAlert::kHidConnected:
case tabs::TabAlert::kSerialConnected:
case tabs::TabAlert::kVrPresentingInHeadset:
icon_color = kColorHoverCardTabAlertAudioPlayingIcon;
break;
}
return icon_color;
}
} // namespace
template <typename T>
FooterRow<T>::FooterRow(bool is_fade_out_view)
: is_fade_out_view_(is_fade_out_view) {
views::FlexLayout* flex_layout =
views::View::SetLayoutManager(std::make_unique<views::FlexLayout>());
flex_layout->SetOrientation(views::LayoutOrientation::kHorizontal)
.SetCrossAxisAlignment(views::LayoutAlignment::kStart);
icon_ = views::View::AddChildView(std::make_unique<views::ImageView>());
if (is_fade_out_view) {
icon_->SetPaintToLayer();
icon_->layer()->SetOpacity(0.0f);
}
icon_->SetVerticalAlignment(views::ImageView::Alignment::kLeading);
footer_label_ = views::View::AddChildView(std::make_unique<views::Label>(
std::u16string(), views::style::CONTEXT_DIALOG_BODY_TEXT));
icon_->SetBackground(
views::CreateSolidBackground(ui::kColorBubbleFooterBackground));
footer_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
footer_label_->SetMultiLine(true);
footer_label_->SetProperty(
views::kFlexBehaviorKey,
views::FlexSpecification(views::LayoutOrientation::kHorizontal,
views::MinimumFlexSizeRule::kScaleToZero,
views::MaximumFlexSizeRule::kUnbounded, true));
footer_label_->SetEnabledColor(kColorTabHoverCardSecondaryText);
footer_label_->SetTextStyle(views::style::STYLE_BODY_4);
// Vertically align the icon to the top line of the label
const int offset = (footer_label_->GetLineHeight() -
GetLayoutConstant(TAB_ALERT_INDICATOR_ICON_WIDTH)) /
2;
icon_->SetProperty(views::kMarginsKey,
gfx::Insets::TLBR(offset, 0, 0, kIconLabelSpacing));
}
template <typename T>
void FooterRow<T>::SetContent(const ui::ImageModel& icon_image_model,
std::u16string label_text) {
footer_label_->SetText(label_text);
icon_->SetImage(icon_image_model);
}
template <typename T>
gfx::Size FooterRow<T>::CalculatePreferredSize(
const views::SizeBounds& available_size) const {
return footer_label_->GetText().empty()
? gfx::Size()
: views::View::CalculatePreferredSize(available_size);
}
template <typename T>
gfx::Size FooterRow<T>::GetMinimumSize() const {
return gfx::Size();
}
template <typename T>
void FooterRow<T>::SetFade(double percent) {
CHECK(is_fade_out_view_);
percent = std::min(1.0, percent);
icon_->layer()->SetOpacity(1.0 - percent);
const SkAlpha alpha = base::saturated_cast<SkAlpha>(
std::numeric_limits<SkAlpha>::max() * (1.0 - percent));
footer_label_->SetBackgroundColor(
SkColorSetA(footer_label_->GetBackgroundColor(), alpha));
footer_label_->SetEnabledColor(
SkColorSetA(footer_label_->GetEnabledColor(), alpha));
}
using FooterRow_AlertFooterRowData = FooterRow<AlertFooterRowData>;
BEGIN_TEMPLATE_METADATA(FooterRow_AlertFooterRowData, FooterRow)
END_METADATA
using FooterRow_PerformanceRowData = FooterRow<PerformanceRowData>;
BEGIN_TEMPLATE_METADATA(FooterRow_PerformanceRowData, FooterRow)
END_METADATA
using FooterRow_CollaborationMessagingRowData =
FooterRow<CollaborationMessagingRowData>;
BEGIN_TEMPLATE_METADATA(FooterRow_CollaborationMessagingRowData, FooterRow)
END_METADATA
template class FooterRow<AlertFooterRowData>;
template class FooterRow<PerformanceRowData>;
template class FooterRow<CollaborationMessagingRowData>;
// FadeAlertFooterRow
// -----------------------------------------------------------------------
void FadeAlertFooterRow::SetData(const AlertFooterRowData& data) {
std::optional<tabs::TabAlert> alert_state = data.alert_state;
if (data.should_show_discard_status) {
std::u16string row_text;
if (data.memory_savings_in_bytes > base::ByteCount(0)) {
const std::u16string formatted_memory_usage =
ui::FormatBytes(base::ByteCount(data.memory_savings_in_bytes));
row_text = l10n_util::GetStringFUTF16(
IDS_HOVERCARD_INACTIVE_TAB_MEMORY_SAVINGS, formatted_memory_usage);
} else {
row_text = l10n_util::GetStringUTF16(IDS_HOVERCARD_INACTIVE_TAB);
}
SetContent(ui::ImageModel::FromVectorIcon(
kPerformanceSpeedometerIcon,
kColorHoverCardTabAlertAudioPlayingIcon,
GetLayoutConstant(TAB_ALERT_INDICATOR_ICON_WIDTH)),
row_text);
} else if (alert_state.has_value()) {
const tabs::TabAlert alert = alert_state.value();
SetContent(tabs::GetAlertImageModel(alert, GetTabAlertColor(alert)),
tabs::TabAlertController::GetTabAlertStateText(alert));
} else {
SetContent(ui::ImageModel(), std::u16string());
}
data_ = data;
}
BEGIN_METADATA(FadeAlertFooterRow)
END_METADATA
// FadePerformanceFooterRow
// -----------------------------------------------------------------------
void FadePerformanceFooterRow::SetData(const PerformanceRowData& data) {
if (data.show_memory_usage) {
const std::u16string formatted_memory_usage =
ui::FormatBytes(data.memory_usage_in_bytes);
const std::u16string row_text = l10n_util::GetStringFUTF16(
data.is_high_memory_usage ? IDS_HOVERCARD_TAB_HIGH_MEMORY_USAGE
: IDS_HOVERCARD_TAB_MEMORY_USAGE,
formatted_memory_usage);
const ui::ImageModel icon_image_model = ui::ImageModel::FromVectorIcon(
kPerformanceSpeedometerIcon, kColorHoverCardTabAlertAudioPlayingIcon,
GetLayoutConstant(TAB_ALERT_INDICATOR_ICON_WIDTH));
SetContent(icon_image_model, row_text);
} else {
SetContent(ui::ImageModel(), std::u16string());
}
data_ = data;
}
BEGIN_METADATA(FadePerformanceFooterRow)
END_METADATA
// FadeCollaborationMessagingFooterRow
// -----------------------------------------------------------------------
void FadeCollaborationMessagingFooterRow::SetData(
const CollaborationMessagingRowData& data) {
data_ = data;
if (!data_.should_show_collaboration_messaging) {
// Empty section if collaboration messaging should be hidden.
SetContent(ui::ImageModel(), std::u16string());
return;
}
SetContent(data_.avatar, data_.text);
}
CollaborationMessagingRowData::CollaborationMessagingRowData() = default;
CollaborationMessagingRowData::~CollaborationMessagingRowData() = default;
CollaborationMessagingRowData::CollaborationMessagingRowData(
const CollaborationMessagingRowData& other) = default;
CollaborationMessagingRowData& CollaborationMessagingRowData::operator=(
const CollaborationMessagingRowData& other) = default;
BEGIN_METADATA(FadeCollaborationMessagingFooterRow)
END_METADATA
// FooterView
// -----------------------------------------------------------------------
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(FooterView, kHoverCardFooterElementId);
FooterView::FooterView() {
SetProperty(views::kElementIdentifierKey, kHoverCardFooterElementId);
flex_layout_ =
views::View::SetLayoutManager(std::make_unique<views::FlexLayout>());
flex_layout_->SetOrientation(views::LayoutOrientation::kVertical)
.SetMainAxisAlignment(views::LayoutAlignment::kStart)
.SetCollapseMargins(true)
.SetInteriorMargin(kFooterMargins)
.SetDefault(views::kMarginsKey, gfx::Insets::VH(kFooterRowSpacing, 0));
alert_row_ = AddChildView(std::make_unique<AlertFadeView>(
std::make_unique<FadeAlertFooterRow>(/* is_fade_out_view =*/false),
std::make_unique<FadeAlertFooterRow>(/* is_fade_out_view =*/true)));
performance_row_ = AddChildView(std::make_unique<PerformanceFadeView>(
std::make_unique<FadePerformanceFooterRow>(/* is_fade_out_view =*/false),
std::make_unique<FadePerformanceFooterRow>(/* is_fade_out_view =*/true)));
collaboration_messaging_row_ =
AddChildView(std::make_unique<CollaborationMessagingFadeView>(
std::make_unique<FadeCollaborationMessagingFooterRow>(
/* is_fade_out_view =*/false),
std::make_unique<FadeCollaborationMessagingFooterRow>(
/* is_fade_out_view =*/true)));
alert_row_->SetProperty(
views::kFlexBehaviorKey,
views::FlexSpecification(views::LayoutOrientation::kHorizontal,
views::MinimumFlexSizeRule::kScaleToMinimum,
views::MaximumFlexSizeRule::kUnbounded, true));
performance_row_->SetProperty(
views::kFlexBehaviorKey,
views::FlexSpecification(views::LayoutOrientation::kHorizontal,
views::MinimumFlexSizeRule::kScaleToMinimum,
views::MaximumFlexSizeRule::kUnbounded, true));
collaboration_messaging_row_->SetProperty(
views::kFlexBehaviorKey,
views::FlexSpecification(views::LayoutOrientation::kHorizontal,
views::MinimumFlexSizeRule::kScaleToMinimum,
views::MaximumFlexSizeRule::kUnbounded, true));
SetBackground(views::CreateSolidBackground(ui::kColorBubbleFooterBackground));
}
void FooterView::SetAlertData(const AlertFooterRowData& data) {
alert_row_->SetData(data);
UpdateVisibility();
}
void FooterView::SetPerformanceData(const PerformanceRowData& data) {
performance_row_->SetData(data);
UpdateVisibility();
}
void FooterView::SetCollaborationMessagingData(
const CollaborationMessagingRowData& data) {
collaboration_messaging_row_->SetData(data);
UpdateVisibility();
}
void FooterView::SetFade(double percent) {
alert_row_->SetFade(percent);
performance_row_->SetFade(percent);
collaboration_messaging_row_->SetFade(percent);
}
void FooterView::UpdateVisibility() {
SetVisible(performance_row_->CalculatePreferredSize({}).height() > 0 ||
alert_row_->CalculatePreferredSize({}).height() > 0 ||
collaboration_messaging_row_->CalculatePreferredSize({}).height() >
0);
}
using FadeWrapper_View_PerformanceRowData =
FadeWrapper<views::View, PerformanceRowData>;
BEGIN_TEMPLATE_METADATA(FadeWrapper_View_PerformanceRowData, FadeWrapper)
END_METADATA
using FadeWrapper_View_AlertFooterRowData =
FadeWrapper<views::View, AlertFooterRowData>;
BEGIN_TEMPLATE_METADATA(FadeWrapper_View_AlertFooterRowData, FadeWrapper)
END_METADATA
using FadeWrapper_View_CollaborationMessagingRowData =
FadeWrapper<views::View, CollaborationMessagingRowData>;
BEGIN_TEMPLATE_METADATA(FadeWrapper_View_CollaborationMessagingRowData,
FadeWrapper)
END_METADATA
using FadeView_FadeAlertFooterRow_FadeAlertFooterRow_AlertFooterRowData =
FadeView<FadeAlertFooterRow, FadeAlertFooterRow, AlertFooterRowData>;
BEGIN_TEMPLATE_METADATA(
FadeView_FadeAlertFooterRow_FadeAlertFooterRow_AlertFooterRowData,
FadeView)
END_METADATA
using FadeView_FadePerformanceFooterRow_FadePerformanceFooterRow_PerformanceRowData =
FadeView<FadePerformanceFooterRow,
FadePerformanceFooterRow,
PerformanceRowData>;
BEGIN_TEMPLATE_METADATA(
FadeView_FadePerformanceFooterRow_FadePerformanceFooterRow_PerformanceRowData,
FadeView)
END_METADATA
using FadeView_FadeCollaborationMessagingFooterRow_FadeCollaborationMessagingFooterRow_CollaborationMessagingRowData =
FadeView<FadeCollaborationMessagingFooterRow,
FadeCollaborationMessagingFooterRow,
CollaborationMessagingRowData>;
BEGIN_TEMPLATE_METADATA(
FadeView_FadeCollaborationMessagingFooterRow_FadeCollaborationMessagingFooterRow_CollaborationMessagingRowData,
FadeView)
END_METADATA
BEGIN_METADATA(FooterView)
END_METADATA