blob: 479b38d8ca1dd13d87fb3a78429b29f7cdcc59d7 [file] [log] [blame]
// 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