910e62b5创建于 1月15日历史提交
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/capture_mode/action_button_view.h"

#include <memory>
#include <string>
#include <utility>

#include "ash/capture_mode/capture_mode_session_focus_cycler.h"
#include "ash/capture_mode/capture_mode_types.h"
#include "ash/capture_mode/capture_mode_util.h"
#include "ash/style/ash_color_id.h"
#include "ash/style/style_util.h"
#include "ash/style/system_shadow.h"
#include "ash/style/typography.h"
#include "base/time/time.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/models/image_model.h"
#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animator.h"
#include "ui/gfx/animation/tween.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/vector_icon_types.h"
#include "ui/views/animation/animation_builder.h"
#include "ui/views/animation/ink_drop.h"
#include "ui/views/background.h"
#include "ui/views/border.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/highlight_path_generator.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"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"

namespace ash {

namespace {

// Action button insets when it is shown in full (text and icon).
constexpr auto kFullActionButtonInsets = gfx::Insets::TLBR(8, 12, 8, 16);

// Action button insets when it only has a text label (no icon).
constexpr auto kTextOnlyActionButtonInsets = gfx::Insets::VH(8, 16);

// Action button insets when it is collapsed (icon only).
constexpr auto kCollapsedActionButtonInsets = gfx::Insets(8);

// The horizontal spacing between the icon and label in an action button.
constexpr int kActionButtonIconLabelSpacing = 8;

// The corner radius for an action button.
constexpr int kActionButtonRadius = 18;

// The size of the icon in an action button.
constexpr int kActionButtonIconSize = 20;

}  // namespace

ActionButtonView::ActionButtonView(views::Button::PressedCallback callback,
                                   std::u16string text,
                                   const gfx::VectorIcon* icon,
                                   ActionButtonRank rank)
    : views::Button(std::move(callback)),
      rank_(rank),
      // Since this view has fully circular rounded corners, we can't use a
      // nine patch layer for the shadow. We have to use the
      // `ShadowOnTextureLayer`. For more info, see https://crbug.com/1308800.
      shadow_(SystemShadow::CreateShadowOnTextureLayer(
          SystemShadow::Type::kElevation12)) {
  box_layout_ = SetLayoutManager(
      icon ? std::make_unique<views::BoxLayout>(
                 views::BoxLayout::Orientation::kHorizontal,
                 kFullActionButtonInsets, kActionButtonIconLabelSpacing)
           : std::make_unique<views::BoxLayout>(
                 views::BoxLayout::Orientation::kHorizontal,
                 kTextOnlyActionButtonInsets));
  SetPaintToLayer();
  layer()->SetFillsBoundsOpaquely(false);

  SetBackground(views::CreateRoundedRectBackground(
      cros_tokens::kCrosSysSystemBaseElevated, kActionButtonRadius));
  shadow_->SetRoundedCornerRadius(kActionButtonRadius);
  capture_mode_util::SetHighlightBorder(
      this, kActionButtonRadius,
      views::HighlightBorder::Type::kHighlightBorderNoShadow);
  views::InstallRoundRectHighlightPathGenerator(this, gfx::Insets(),
                                                kActionButtonRadius);

  StyleUtil::ConfigureInkDropAttributes(
      this, StyleUtil::kBaseColor | StyleUtil::kInkDropOpacity);
  StyleUtil::SetUpInkDropForButton(this);
  ink_drop_container_ =
      AddChildView(std::make_unique<views::InkDropContainerView>());
  // The container should adjust its bounds if we collapse to an icon button.
  ink_drop_container_->SetAutoMatchParentBounds(true);

  if (icon) {
    image_view_ = AddChildView(
        std::make_unique<views::ImageView>(ui::ImageModel::FromVectorIcon(
            *icon, kColorAshButtonIconColor, kActionButtonIconSize)));
  }
  label_ = AddChildView(std::make_unique<views::Label>(text));
  TypographyProvider::Get()->StyleLabel(TypographyToken::kCrosButton2, *label_);

  CaptureModeSessionFocusCycler::HighlightHelper::Install(this);
  SetAccessibleName(text);
}

ActionButtonView::~ActionButtonView() {
  views::InkDrop::Remove(this);
}

void ActionButtonView::AddedToWidget() {
  views::Button::AddedToWidget();

  // Since the layer of the shadow has to be added as a sibling to this view's
  // layer, we need to wait until the view is added to the widget.
  auto* parent = layer()->parent();
  ui::Layer* shadow_layer = shadow_->GetLayer();
  parent->Add(shadow_layer);
  parent->StackAtBottom(shadow_layer);

  // Make the shadow observe the color provider source change to update the
  // colors.
  shadow_->ObserveColorProviderSource(GetWidget());
}

void ActionButtonView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
  // The shadow layer is a sibling of this view's layer, and should have the
  // same bounds.
  shadow_->SetContentBounds(layer()->bounds());
}

void ActionButtonView::AddLayerToRegion(ui::Layer* layer,
                                        views::LayerRegion region) {
  // This routes background layers to `ink_drop_container_` instead of `this` to
  // avoid painting effects underneath our background.
  ink_drop_container_->AddLayerToRegion(layer, region);
}

void ActionButtonView::RemoveLayerFromRegions(ui::Layer* layer) {
  ink_drop_container_->RemoveLayerFromRegions(layer);
}

void ActionButtonView::CollapseToIconButton() {
  if (!label_->GetVisible()) {
    return;
  }
  label_->SetVisible(false);
  const std::u16string label_text(label_->GetText());
  label_->SetTooltipText(label_text);
  box_layout_->set_inside_border_insets(kCollapsedActionButtonInsets);
}

void ActionButtonView::PerformFadeInAnimation(
    base::TimeDelta fade_in_duration) {
  CHECK(layer());
  layer()->SetOpacity(0.0f);
  shadow_->GetLayer()->SetOpacity(0.0f);
  views::AnimationBuilder()
      .SetPreemptionStrategy(
          ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
      .Once()
      .SetDuration(fade_in_duration)
      .SetOpacity(layer(), 1.0f, gfx::Tween::LINEAR)
      .SetOpacity(shadow_->GetLayer(), 1.0f, gfx::Tween::LINEAR);
}

BEGIN_METADATA(ActionButtonView)
END_METADATA

}  // namespace ash