blob: c0a73c96d14b7108ed5e2db72ddcc74264f5a3b8 [file] [log] [blame]
// Copyright 2020 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/location_bar/omnibox_chip_button.h"
#include <cstddef>
#include "base/time/time.h"
#include "chrome/browser/themes/theme_properties.h"
#include "chrome/browser/ui/color/chrome_color_id.h"
#include "chrome/browser/ui/layout_constants.h"
#include "chrome/browser/ui/views/location_bar/location_bar_util.h"
#include "components/vector_icons/vector_icons.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/theme_provider.h"
#include "ui/base/ui_base_features.h"
#include "ui/color/color_id.h"
#include "ui/color/color_provider.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/views/background.h"
#include "ui/views/controls/highlight_path_generator.h"
#include "ui/views/painter.h"
#include "ui/views/view_class_properties.h"
namespace {
// Padding between chip's icon and label.
constexpr int kChipImagePadding = 4;
// An extra space between chip's label and right edge.
constexpr int kExtraRightPadding = 4;
} // namespace
DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(OmniboxChipButton, kChipElementId);
OmniboxChipButton::OmniboxChipButton(PressedCallback callback)
: MdTextButton(std::move(callback),
std::u16string(),
views::style::CONTEXT_BUTTON_MD) {
SetProperty(views::kElementIdentifierKey, kChipElementId);
views::InstallPillHighlightPathGenerator(this);
SetHorizontalAlignment(gfx::ALIGN_LEFT);
SetElideBehavior(gfx::ElideBehavior::FADE_TAIL);
SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
// Equalizing padding on the left, right and between icon and label.
SetImageLabelSpacing(kChipImagePadding);
SetCustomPadding(gfx::Insets::VH(
GetLayoutConstant(LOCATION_BAR_CHILD_INTERIOR_PADDING),
GetLayoutInsets(LOCATION_BAR_ICON_INTERIOR_PADDING).left()));
SetCornerRadius(GetIconSize());
constexpr auto kAnimationDuration = base::Milliseconds(350);
animation_ = std::make_unique<gfx::SlideAnimation>(this);
animation_->SetSlideDuration(kAnimationDuration);
UpdateIconAndColors();
}
OmniboxChipButton::~OmniboxChipButton() = default;
void OmniboxChipButton::VisibilityChanged(views::View* starting_from,
bool is_visible) {
for (Observer& observer : observers_) {
observer.OnChipVisibilityChanged(is_visible);
}
}
void OmniboxChipButton::AnimateCollapse(base::TimeDelta kAnimationDuration) {
base_width_ = 0;
animation_->SetSlideDuration(kAnimationDuration);
ForceAnimateCollapse();
}
void OmniboxChipButton::AnimateExpand(base::TimeDelta kAnimationDuration) {
base_width_ = 0;
animation_->SetSlideDuration(kAnimationDuration);
ForceAnimateExpand();
}
void OmniboxChipButton::AnimateToFit(base::TimeDelta kAnimationDuration) {
animation_->SetSlideDuration(kAnimationDuration);
base_width_ = label()->width();
if (label()->GetPreferredSize().width() < width()) {
// As we're collapsing, we need to make sure that the padding is not
// animated away.
base_width_ += kChipImagePadding + kExtraRightPadding;
ForceAnimateCollapse();
} else {
ForceAnimateExpand();
}
}
void OmniboxChipButton::ResetAnimation(double value) {
fully_collapsed_ = value == 0.0;
animation_->Reset(value);
}
gfx::Size OmniboxChipButton::CalculatePreferredSize() const {
const int fixed_width = GetIconSize() + GetInsets().width();
const int collapsable_width = label()->GetPreferredSize().width() +
kChipImagePadding + kExtraRightPadding;
const double animation_value =
force_expanded_for_testing_ ? 1.0 : animation_->GetCurrentValue();
const int width = base_width_ +
std::round(collapsable_width * animation_value) +
fixed_width;
return gfx::Size(width, GetHeightForWidth(width));
}
void OmniboxChipButton::OnThemeChanged() {
MdTextButton::OnThemeChanged();
UpdateIconAndColors();
}
void OmniboxChipButton::UpdateBackgroundColor() {
if (theme_ == OmniboxChipTheme::kIconStyle) {
SetBackground(nullptr);
} else {
SetBackground(
CreateBackgroundFromPainter(views::Painter::CreateSolidRoundRectPainter(
GetBackgroundColor(), GetIconSize())));
}
}
void OmniboxChipButton::AnimationEnded(const gfx::Animation* animation) {
if (animation != animation_.get())
return;
fully_collapsed_ = animation->GetCurrentValue() != 1.0;
if (animation->GetCurrentValue() == 1.0) {
for (Observer& observer : observers_) {
observer.OnExpandAnimationEnded();
}
}
if (animation->GetCurrentValue() == 0.0) {
for (Observer& observer : observers_) {
observer.OnCollapseAnimationEnded();
}
}
}
void OmniboxChipButton::AnimationProgressed(const gfx::Animation* animation) {
if (animation == animation_.get())
PreferredSizeChanged();
}
void OmniboxChipButton::SetTheme(OmniboxChipTheme theme) {
theme_ = theme;
UpdateIconAndColors();
}
void OmniboxChipButton::SetMessage(std::u16string message) {
SetText(message);
UpdateIconAndColors();
}
ui::ImageModel OmniboxChipButton::GetIconImageModel() const {
return ui::ImageModel::FromVectorIcon(GetIcon(), GetTextAndIconColor(),
GetIconSize(), nullptr);
}
const gfx::VectorIcon& OmniboxChipButton::GetIcon() const {
if (icon_) {
return const_cast<decltype(*icon_)>(*icon_);
}
return gfx::kNoneIcon;
}
void OmniboxChipButton::ForceAnimateExpand() {
ResetAnimation(0.0);
animation_->Show();
}
void OmniboxChipButton::ForceAnimateCollapse() {
ResetAnimation(1.0);
animation_->Hide();
}
int OmniboxChipButton::GetIconSize() const {
return GetLayoutConstant(LOCATION_BAR_ICON_SIZE);
}
void OmniboxChipButton::UpdateIconAndColors() {
if (!GetWidget())
return;
SetEnabledTextColors(GetTextAndIconColor());
SetImageModel(views::Button::STATE_NORMAL, GetIconImageModel());
if (features::IsChromeRefresh2023()) {
ConfigureInkDropForRefresh2023(this, kColorOmniboxChipInkDropHover,
kColorOmniboxChipInkDropRipple);
}
}
SkColor OmniboxChipButton::GetTextAndIconColor() const {
if (theme_ == OmniboxChipTheme::kIconStyle)
return GetColorProvider()->GetColor(kColorOmniboxResultsIcon);
return GetColorProvider()->GetColor(
theme_ == OmniboxChipTheme::kLowVisibility
? kColorOmniboxChipForegroundLowVisibility
: kColorOmniboxChipForegroundNormalVisibility);
}
SkColor OmniboxChipButton::GetBackgroundColor() const {
DCHECK(theme_ != OmniboxChipTheme::kIconStyle);
return GetColorProvider()->GetColor(kColorOmniboxChipBackground);
}
void OmniboxChipButton::SetForceExpandedForTesting(
bool force_expanded_for_testing) {
force_expanded_for_testing_ = force_expanded_for_testing;
}
void OmniboxChipButton::SetChipIcon(const gfx::VectorIcon& icon) {
icon_ = &icon;
UpdateIconAndColors();
}
void OmniboxChipButton::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void OmniboxChipButton::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
BEGIN_METADATA(OmniboxChipButton, views::MdTextButton)
ADD_READONLY_PROPERTY_METADATA(int, IconSize)
END_METADATA