blob: 4c23de01c2a1a6ba2ecbf77a7cbe64b78ac7226c [file] [log] [blame]
// Copyright 2019 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/rich_hover_button.h"
#include "base/strings/string_util.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 "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/models/image_model.h"
#include "ui/compositor/layer.h"
#include "ui/views/animation/ink_drop.h"
#include "ui/views/border.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/table_layout.h"
#include "ui/views/style/typography.h"
#include "ui/views/view_class_properties.h"
namespace {
std::unique_ptr<views::View> CreateIconView(const ui::ImageModel& icon_image) {
auto icon = std::make_unique<NonAccessibleImageView>();
icon->SetImage(icon_image);
// Make sure hovering over the icon also hovers the `RichHoverButton`.
icon->SetCanProcessEventsWithinSubtree(false);
// Don't cover |icon| when the ink drops are being painted.
icon->SetPaintToLayer();
icon->layer()->SetFillsBoundsOpaquely(false);
return icon;
}
} // namespace
RichHoverButton::RichHoverButton(
views::Button::PressedCallback callback,
const ui::ImageModel& main_image_icon,
const std::u16string& title_text,
const std::u16string& secondary_text,
const std::u16string& tooltip_text,
const std::u16string& subtitle_text,
absl::optional<ui::ImageModel> action_image_icon,
absl::optional<ui::ImageModel> state_icon)
: HoverButton(std::move(callback), std::u16string()) {
label()->SetHandlesTooltips(false);
ChromeLayoutProvider* layout_provider = ChromeLayoutProvider::Get();
const int icon_label_spacing = layout_provider->GetDistanceMetric(
views::DISTANCE_RELATED_LABEL_HORIZONTAL);
views::style::TextContext text_context =
views::style::CONTEXT_DIALOG_BODY_TEXT;
views::TableLayout* table_layout =
SetLayoutManager(std::make_unique<views::TableLayout>());
table_layout
// Column for |main_image_icon|.
->AddColumn(views::LayoutAlignment::kCenter,
views::LayoutAlignment::kCenter,
views::TableLayout::kFixedSize,
views::TableLayout::ColumnSize::kUsePreferred, 0, 0)
.AddPaddingColumn(views::TableLayout::kFixedSize, icon_label_spacing)
// Column for title.
.AddColumn(views::LayoutAlignment::kStretch,
views::LayoutAlignment::kCenter, 1.0f,
views::TableLayout::ColumnSize::kUsePreferred, 0, 0)
.AddPaddingColumn(views::TableLayout::kFixedSize, icon_label_spacing)
// Column for |secondary_text|.
.AddColumn(views::LayoutAlignment::kEnd, views::LayoutAlignment::kStretch,
views::TableLayout::kFixedSize,
views::TableLayout::ColumnSize::kUsePreferred, 0, 0);
if (state_icon.has_value()) {
table_layout
// Column for |state_icon|.
->AddPaddingColumn(views::TableLayout::kFixedSize, icon_label_spacing)
.AddColumn(views::LayoutAlignment::kCenter,
views::LayoutAlignment::kCenter,
views::TableLayout::kFixedSize,
views::TableLayout::ColumnSize::kFixed, 16, 0);
}
table_layout
// Column for |action_icon|.
->AddPaddingColumn(views::TableLayout::kFixedSize, icon_label_spacing)
.AddColumn(views::LayoutAlignment::kCenter,
views::LayoutAlignment::kCenter,
views::TableLayout::kFixedSize,
views::TableLayout::ColumnSize::kFixed, 16, 0)
.AddRows(1, views::TableLayout::kFixedSize,
// Force row to have sufficient height for full line-height of
// the title.
views::style::GetLineHeight(text_context,
views::style::STYLE_PRIMARY));
// TODO(pkasting): This class should subclass Button, not HoverButton.
table_layout->SetChildViewIgnoredByLayout(image(), true);
table_layout->SetChildViewIgnoredByLayout(label(), true);
table_layout->SetChildViewIgnoredByLayout(ink_drop_container(), true);
AddChildView(CreateIconView(main_image_icon));
auto title_label = std::make_unique<views::Label>();
title_label->SetTextContext(text_context);
title_ = AddChildView(std::move(title_label));
title_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
title_->SetCanProcessEventsWithinSubtree(false);
auto secondary_label = std::make_unique<views::Label>(
std::u16string(), views::style::CONTEXT_LABEL,
views::style::STYLE_SECONDARY);
secondary_label->SetHorizontalAlignment(gfx::ALIGN_RIGHT);
secondary_label_ = AddChildView(std::move(secondary_label));
// State icon is optional and column is created only when it is set.
if (state_icon.has_value()) {
AddChildView(CreateIconView(state_icon.value()));
}
if (action_image_icon.has_value()) {
AddChildView(CreateIconView(action_image_icon.value()));
} else {
// Fill the cell with an empty view at column 5.
AddChildView(std::make_unique<views::View>());
}
if (!title_text.empty()) {
SetTitleText(title_text);
if (!secondary_text.empty())
SetSecondaryText(secondary_text);
}
if (!subtitle_text.empty()) {
table_layout->AddRows(1, views::TableLayout::kFixedSize);
AddChildView(std::make_unique<views::View>());
subtitle_ = AddChildView(std::make_unique<views::Label>(
subtitle_text, views::style::CONTEXT_LABEL,
views::style::STYLE_SECONDARY));
subtitle_->SetMultiLine(true);
subtitle_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
subtitle_->SetAutoColorReadabilityEnabled(false);
subtitle_->SizeToFit(title_->GetPreferredSize().width());
AddChildView(std::make_unique<views::View>());
AddChildView(std::make_unique<views::View>());
}
SetBorder(views::CreateEmptyBorder(layout_provider->GetInsetsMetric(
ChromeInsetsMetric::INSETS_PAGE_INFO_HOVER_BUTTON)));
SetTooltipText(tooltip_text);
UpdateAccessibleName();
Layout();
}
void RichHoverButton::SetTitleText(const std::u16string& title_text) {
DCHECK(title_);
title_->SetText(title_text);
UpdateAccessibleName();
}
void RichHoverButton::SetSecondaryText(const std::u16string& secondary_text) {
DCHECK(secondary_label_);
secondary_label_->SetText(secondary_text);
UpdateAccessibleName();
}
void RichHoverButton::SetSubtitleText(const std::u16string& subtitle_text) {
DCHECK(subtitle_);
subtitle_->SetText(subtitle_text);
UpdateAccessibleName();
}
void RichHoverButton::SetSubtitleMultiline(bool is_multiline) {
subtitle_->SetMultiLine(is_multiline);
}
const views::Label* RichHoverButton::GetTitleViewForTesting() const {
return title_;
}
const views::Label* RichHoverButton::GetSubTitleViewForTesting() const {
return subtitle_;
}
void RichHoverButton::UpdateAccessibleName() {
const std::u16string title_text =
secondary_label_ == nullptr
? title_->GetText()
: base::JoinString({title_->GetText(), secondary_label_->GetText()},
u" ");
const std::u16string accessible_name =
subtitle_ == nullptr
? title_text
: base::JoinString({title_text, subtitle_->GetText()}, u"\n");
HoverButton::SetAccessibleName(accessible_name);
}
gfx::Size RichHoverButton::CalculatePreferredSize() const {
return Button::CalculatePreferredSize();
}
int RichHoverButton::GetHeightForWidth(int w) const {
return Button::GetHeightForWidth(w);
}
void RichHoverButton::OnBoundsChanged(const gfx::Rect& previous_bounds) {
return Button::OnBoundsChanged(previous_bounds);
}
views::View* RichHoverButton::GetTooltipHandlerForPoint(
const gfx::Point& point) {
return Button::GetTooltipHandlerForPoint(point);
}
BEGIN_METADATA(RichHoverButton, HoverButton)
END_METADATA