910e62b5创建于 1月15日历史提交
// 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 <optional>

#include "base/numerics/safe_conversions.h"
#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 "chrome/browser/ui/views/location_bar/omnibox_chip_theme.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/vector_icon_types.h"
#include "ui/views/background.h"
#include "ui/views/controls/highlight_path_generator.h"
#include "ui/views/interaction/element_tracker_views.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;

// These chrome refresh layout constants are not shared with other views.
constexpr int kChipVerticalPadding = 4;
constexpr int kChipHorizontalPadding = 6;

}  // namespace

DEFINE_CUSTOM_ELEMENT_EVENT_TYPE(kOmniboxChipButtonExpanded);

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(kChipVerticalPadding, kChipHorizontalPadding));
  label()->SetTextStyle(views::style::STYLE_BODY_4_EMPHASIS);
  SetCornerRadius(GetLayoutConstant(LOCATION_BAR_CHILD_CORNER_RADIUS));
  animation_ = std::make_unique<gfx::SlideAnimation>(this);

  UpdateIconAndColors();
}

OmniboxChipButton::~OmniboxChipButton() = default;

void OmniboxChipButton::VisibilityChanged(views::View* starting_from,
                                          bool is_visible) {
  observers_.Notify(&Observer::OnChipVisibilityChanged, is_visible);
}

void OmniboxChipButton::AnimateCollapse(base::TimeDelta duration) {
  animation_->SetSlideDuration(duration);
  ForceAnimateCollapse();
}

void OmniboxChipButton::AnimateExpand(base::TimeDelta duration) {
  animation_->SetSlideDuration(duration);
  ForceAnimateExpand();
}

void OmniboxChipButton::ResetAnimation(double value) {
  animation_->Reset(value);
  OnAnimationValueMaybeChanged();
}

// TODO(crbug.com/40232718): Respect `available_size`.
gfx::Size OmniboxChipButton::CalculatePreferredSize(
    const views::SizeBounds& available_size) const {
  const int fixed_width = GetIconSize() + GetInsets().width();
  constexpr int extra_width = kChipImagePadding + kExtraRightPadding;
  views::SizeBound available_width = std::max<views::SizeBound>(
      0, available_size.width() - fixed_width - extra_width);
  const int label_width =
      label()->GetPreferredSize(views::SizeBounds(available_width, {})).width();
  const int collapsable_width = label_width + extra_width;

  const int width =
      base::ClampRound(collapsable_width * animation_->GetCurrentValue()) +
      fixed_width;
  return gfx::Size(width, views::LabelButton::CalculatePreferredSize(
                              views::SizeBounds(width, {}))
                              .height());
}

void OmniboxChipButton::OnThemeChanged() {
  MdTextButton::OnThemeChanged();
  UpdateIconAndColors();
}

void OmniboxChipButton::UpdateBackgroundColor() {
  if (theme_ == OmniboxChipTheme::kIconStyle) {
    SetBackground(nullptr);
  } else {
    SkColor color = GetColorProvider()->GetColor(GetBackgroundColorId());
    SetBackground(CreateBackgroundFromPainter(
        views::Painter::CreateSolidRoundRectPainterWithVariableRadius(
            color, GetCornerRadii())));
  }
}

void OmniboxChipButton::AnimationEnded(const gfx::Animation* animation) {
  if (animation != animation_.get()) {
    return;
  }

  OnAnimationValueMaybeChanged();

  views::ElementTrackerViews::GetInstance()->NotifyCustomEvent(
      kOmniboxChipButtonExpanded, this);
}

void OmniboxChipButton::AnimationProgressed(const gfx::Animation* animation) {
  if (animation != animation_.get()) {
    return;
  }

  OnAnimationValueMaybeChanged();
  PreferredSizeChanged();
}

void OmniboxChipButton::SetTheme(OmniboxChipTheme theme) {
  theme_ = theme;
  UpdateIconAndColors();
}

void OmniboxChipButton::SetIcon(const gfx::VectorIcon& icon) {
  icon_ = &icon;
  UpdateIconAndColors();
}

ui::ImageModel OmniboxChipButton::GetIconImageModel() const {
  return ui::ImageModel::FromVectorIcon(GetIcon(), GetForegroundColorId(),
                                        GetIconSize(), nullptr);
}

const gfx::VectorIcon& OmniboxChipButton::GetIcon() const {
  if (icon_) {
    return const_cast<decltype(*icon_)>(*icon_);
  }

  return gfx::VectorIcon::EmptyIcon();
}

ui::ColorId OmniboxChipButton::GetForegroundColorId() const {
  return kColorOmniboxChipForegroundNormalVisibility;
}

ui::ColorId OmniboxChipButton::GetBackgroundColorId() const {
  DCHECK(theme_ != OmniboxChipTheme::kIconStyle);
  return kColorOmniboxChipBackground;
}

void OmniboxChipButton::UpdateIconAndColors() {
  if (!GetWidget()) {
    return;
  }
  SetEnabledTextColors(GetForegroundColorId());
  SetImageModel(views::Button::STATE_NORMAL, GetIconImageModel());
  ConfigureInkDropForRefresh2023(this, kColorOmniboxChipInkDropHover,
                                 kColorOmniboxChipInkDropRipple);
}

void OmniboxChipButton::ForceAnimateExpand() {
  ResetAnimation(0.0);
  animation_->Show();
}

void OmniboxChipButton::ForceAnimateCollapse() {
  ResetAnimation(1.0);
  animation_->Hide();
}

void OmniboxChipButton::OnAnimationValueMaybeChanged() {
  fully_collapsed_ = animation_->GetCurrentValue() == 0.0;
}

int OmniboxChipButton::GetIconSize() const {
  // Mimic the sizing for other trailing icons.
  return GetLayoutConstant((theme_ == OmniboxChipTheme::kIconStyle)
                               ? LOCATION_BAR_TRAILING_ICON_SIZE
                               : LOCATION_BAR_CHIP_ICON_SIZE);
}

void OmniboxChipButton::AddObserver(Observer* observer) {
  observers_.AddObserver(observer);
}

void OmniboxChipButton::RemoveObserver(Observer* observer) {
  observers_.RemoveObserver(observer);
}

BEGIN_METADATA(OmniboxChipButton)
ADD_READONLY_PROPERTY_METADATA(int, IconSize)
END_METADATA