#include "ash/system/holding_space/holding_space_item_chip_view.h"
#include <algorithm>
#include <optional>
#include <variant>
#include "ash/bubble/bubble_utils.h"
#include "ash/public/cpp/holding_space/holding_space_client.h"
#include "ash/public/cpp/holding_space/holding_space_colors.h"
#include "ash/public/cpp/holding_space/holding_space_constants.h"
#include "ash/public/cpp/holding_space/holding_space_controller.h"
#include "ash/public/cpp/holding_space/holding_space_image.h"
#include "ash/public/cpp/holding_space/holding_space_item.h"
#include "ash/public/cpp/holding_space/holding_space_metrics.h"
#include "ash/public/cpp/holding_space/holding_space_progress.h"
#include "ash/public/cpp/holding_space/holding_space_util.h"
#include "ash/public/cpp/rounded_image_view.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_id.h"
#include "ash/style/dark_light_mode_controller_impl.h"
#include "ash/style/typography.h"
#include "ash/system/holding_space/holding_space_item_view.h"
#include "ash/system/holding_space/holding_space_progress_indicator_util.h"
#include "ash/system/holding_space/holding_space_view_delegate.h"
#include "ash/system/progress_indicator/progress_indicator.h"
#include "ash/system/progress_indicator/progress_indicator_animation_registry.h"
#include "ash/system/progress_indicator/progress_ring_animation.h"
#include "base/check.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "third_party/abseil-cpp/absl/functional/overload.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/chromeos/styles/cros_styles.h"
#include "ui/color/color_id.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_owner.h"
#include "ui/compositor/paint_recorder.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/gfx/geometry/transform_util.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/gfx/scoped_canvas.h"
#include "ui/gfx/skia_paint_util.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/border.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/box_layout_view.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/layout/flex_layout.h"
namespace ash {
namespace {
constexpr int kChildSpacing = 8;
constexpr int kLabelMaskGradientWidth = 16;
constexpr auto kLabelMargins = gfx::Insets::TLBR(4, 0, 4, 2);
constexpr auto kPadding = gfx::Insets::TLBR(0, 8, 0, 10);
constexpr int kPreferredHeight = 40;
constexpr int kPreferredWidth = 160;
constexpr int kProgressIndicatorSize = 26;
constexpr int kSecondaryActionIconSize = 16;
constexpr base::TimeDelta kInProgressImageScaleDuration =
base::Milliseconds(150);
constexpr float kInProgressImageScaleFactor = 0.7f;
void ToCenteredSize(gfx::Rect* rect, const gfx::Size& size) {
rect->Outset(gfx::Outsets::VH(size.height(), size.width()));
rect->ClampToCenteredSize(size);
}
class ObservableRoundedImageView : public RoundedImageView {
METADATA_HEADER(ObservableRoundedImageView, RoundedImageView)
public:
ObservableRoundedImageView() = default;
ObservableRoundedImageView(const ObservableRoundedImageView&) = delete;
ObservableRoundedImageView& operator=(const ObservableRoundedImageView&) =
delete;
~ObservableRoundedImageView() override = default;
using BoundsChangedCallback = base::RepeatingCallback<void()>;
void SetBoundsChangedCallback(BoundsChangedCallback bounds_changed_callback) {
bounds_changed_callback_ = std::move(bounds_changed_callback);
}
private:
void OnBoundsChanged(const gfx::Rect& previous_bounds) override {
RoundedImageView::OnBoundsChanged(previous_bounds);
if (!bounds_changed_callback_.is_null())
bounds_changed_callback_.Run();
}
BoundsChangedCallback bounds_changed_callback_;
};
BEGIN_METADATA(ObservableRoundedImageView)
END_METADATA
BEGIN_VIEW_BUILDER(, ObservableRoundedImageView, RoundedImageView)
VIEW_BUILDER_PROPERTY(ObservableRoundedImageView::BoundsChangedCallback,
BoundsChangedCallback)
END_VIEW_BUILDER
class PaintCallbackLabel : public views::Label {
METADATA_HEADER(PaintCallbackLabel, views::Label)
public:
PaintCallbackLabel() = default;
PaintCallbackLabel(const PaintCallbackLabel&) = delete;
PaintCallbackLabel& operator=(const PaintCallbackLabel&) = delete;
~PaintCallbackLabel() override = default;
using Callback = base::RepeatingCallback<void(views::Label*, gfx::Canvas*)>;
void SetCallback(Callback callback) { callback_ = std::move(callback); }
void SetPaintToLayer(bool fills_bounds_opaquely) {
views::Label::SetPaintToLayer();
layer()->SetFillsBoundsOpaquely(fills_bounds_opaquely);
}
void SetStyle(TypographyToken style) {
bubble_utils::ApplyStyle(this, style);
}
void SetViewAccessibilityIsIgnored(bool is_ignored) {
GetViewAccessibility().SetIsIgnored(is_ignored);
}
private:
void OnPaint(gfx::Canvas* canvas) override {
views::Label::OnPaint(canvas);
if (!callback_.is_null())
callback_.Run(this, canvas);
}
Callback callback_;
};
BEGIN_METADATA(PaintCallbackLabel)
END_METADATA
BEGIN_VIEW_BUILDER(, PaintCallbackLabel, views::Label)
VIEW_BUILDER_PROPERTY(PaintCallbackLabel::Callback, Callback)
VIEW_BUILDER_PROPERTY(TypographyToken, Style)
VIEW_BUILDER_PROPERTY(bool, PaintToLayer)
VIEW_BUILDER_PROPERTY(bool, ViewAccessibilityIsIgnored)
END_VIEW_BUILDER
class ProgressIndicatorView : public views::View {
METADATA_HEADER(ProgressIndicatorView, views::View)
public:
ProgressIndicatorView() = default;
ProgressIndicatorView(const ProgressIndicatorView&) = delete;
ProgressIndicatorView& operator=(const ProgressIndicatorView&) = delete;
~ProgressIndicatorView() override = default;
void CopyProgressIndicatorAddressTo(raw_ptr<ProgressIndicator>* ptr) {
DCHECK(progress_indicator_);
*ptr = progress_indicator_.get();
}
void SetHoldingSpaceItem(const HoldingSpaceItem* item) {
DCHECK(!progress_indicator_);
progress_indicator_ =
holding_space_util::CreateProgressIndicatorForItem(item);
SetPaintToLayer();
layer()->SetFillsBoundsOpaquely(false);
layer()->Add(progress_indicator_->CreateLayer(base::BindRepeating(
[](const ProgressIndicatorView* self, ui::ColorId color_id) {
return self->GetColorProvider()->GetColor(color_id);
},
base::Unretained(this))));
}
private:
void OnBoundsChanged(const gfx::Rect& previous_bounds) override {
if (progress_indicator_) {
gfx::Rect bounds(GetLocalBounds());
ToCenteredSize(&bounds,
gfx::Size(kProgressIndicatorSize, kProgressIndicatorSize));
progress_indicator_->layer()->SetBounds(bounds);
}
}
void OnThemeChanged() override {
views::View::OnThemeChanged();
if (progress_indicator_)
progress_indicator_->InvalidateLayer();
}
std::unique_ptr<ProgressIndicator> progress_indicator_;
};
BEGIN_METADATA(ProgressIndicatorView)
END_METADATA
BEGIN_VIEW_BUILDER(, ProgressIndicatorView, views::View)
VIEW_BUILDER_METHOD(CopyProgressIndicatorAddressTo, raw_ptr<ProgressIndicator>*)
VIEW_BUILDER_PROPERTY(const HoldingSpaceItem*, HoldingSpaceItem)
END_VIEW_BUILDER
}
}
DEFINE_VIEW_BUILDER(, ash::ObservableRoundedImageView)
DEFINE_VIEW_BUILDER(, ash::PaintCallbackLabel)
DEFINE_VIEW_BUILDER(, ash::ProgressIndicatorView)
namespace ash {
namespace {
views::Builder<PaintCallbackLabel> CreateLabelBuilder() {
auto label = views::Builder<PaintCallbackLabel>();
label.SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT)
.SetPaintToLayer(false)
.SetViewAccessibilityIsIgnored(true);
return label;
}
views::Builder<views::ImageButton> CreateSecondaryActionBuilder() {
using HorizontalAlignment = views::ImageButton::HorizontalAlignment;
using VerticalAlignment = views::ImageButton::VerticalAlignment;
auto secondary_action = views::Builder<views::ImageButton>();
secondary_action.SetFocusBehavior(views::View::FocusBehavior::NEVER)
.SetImageHorizontalAlignment(HorizontalAlignment::ALIGN_CENTER)
.SetImageVerticalAlignment(VerticalAlignment::ALIGN_MIDDLE);
return secondary_action;
}
}
HoldingSpaceItemChipView::HoldingSpaceItemChipView(
HoldingSpaceViewDelegate* delegate,
const HoldingSpaceItem* item)
: HoldingSpaceItemView(delegate, item) {
using CrossAxisAlignment = views::BoxLayout::CrossAxisAlignment;
using MainAxisAlignment = views::BoxLayout::MainAxisAlignment;
using Orientation = views::BoxLayout::Orientation;
auto layout_manager = std::make_unique<views::FlexLayout>();
layout_manager->SetOrientation(views::LayoutOrientation::kHorizontal)
.SetCrossAxisAlignment(views::LayoutAlignment::kCenter)
.SetCollapseMargins(true)
.SetIgnoreDefaultMainAxisMargins(true)
.SetInteriorMargin(gfx::Insets(kPadding))
.SetDefault(views::kMarginsKey, gfx::Insets::VH(0, kChildSpacing));
auto paint_label_mask_callback = base::BindRepeating(
&HoldingSpaceItemChipView::OnPaintLabelMask, base::Unretained(this));
auto secondary_action_callback =
base::BindRepeating(&HoldingSpaceItemChipView::OnSecondaryActionPressed,
base::Unretained(this));
views::Builder<HoldingSpaceItemChipView>(this)
.SetPreferredSize(gfx::Size(kPreferredWidth, kPreferredHeight))
.SetLayoutManager(std::move(layout_manager))
.AddChild(
views::Builder<ProgressIndicatorView>()
.SetHoldingSpaceItem(item)
.CopyProgressIndicatorAddressTo(&progress_indicator_)
.SetUseDefaultFillLayout(true)
.AddChild(views::Builder<ObservableRoundedImageView>()
.SetCornerRadius(kHoldingSpaceChipIconSize / 2)
.SetBoundsChangedCallback(base::BindRepeating(
&HoldingSpaceItemChipView::UpdateImageTransform,
base::Unretained(this)))
.CopyAddressTo(&image_)
.SetID(kHoldingSpaceItemImageId))
.AddChild(CreateCheckmarkBuilder())
.AddChild(
views::Builder<views::View>()
.CopyAddressTo(&secondary_action_container_)
.SetID(kHoldingSpaceItemSecondaryActionContainerId)
.SetUseDefaultFillLayout(true)
.SetVisible(false)
.AddChild(
CreateSecondaryActionBuilder()
.CopyAddressTo(&secondary_action_pause_)
.SetID(kHoldingSpaceItemPauseButtonId)
.SetCallback(secondary_action_callback)
.SetVisible(false)
.SetImageModel(
views::Button::STATE_NORMAL,
ui::ImageModel::FromVectorIcon(
kPauseIcon, kColorAshButtonIconColor,
kSecondaryActionIconSize)))
.AddChild(
CreateSecondaryActionBuilder()
.CopyAddressTo(&secondary_action_resume_)
.SetID(kHoldingSpaceItemResumeButtonId)
.SetCallback(secondary_action_callback)
.SetFlipCanvasOnPaintForRTLUI(false)
.SetVisible(false)
.SetImageModel(
views::Button::STATE_NORMAL,
ui::ImageModel::FromVectorIcon(
kResumeIcon, kColorAshButtonIconColor,
kSecondaryActionIconSize)))))
.AddChild(
views::Builder<views::View>()
.SetUseDefaultFillLayout(true)
.SetProperty(views::kFlexBehaviorKey,
views::FlexSpecification(
views::MinimumFlexSizeRule::kScaleToZero,
views::MaximumFlexSizeRule::kUnbounded))
.AddChild(
views::Builder<views::BoxLayoutView>()
.SetOrientation(Orientation::kVertical)
.SetMainAxisAlignment(MainAxisAlignment::kCenter)
.SetCrossAxisAlignment(CrossAxisAlignment::kStretch)
.SetInsideBorderInsets(kLabelMargins)
.AddChild(CreateLabelBuilder()
.CopyAddressTo(&primary_label_)
.SetID(kHoldingSpaceItemPrimaryChipLabelId)
.SetStyle(TypographyToken::kCrosBody2)
.SetElideBehavior(gfx::ELIDE_MIDDLE)
.SetCallback(paint_label_mask_callback))
.AddChild(
CreateLabelBuilder()
.CopyAddressTo(&secondary_label_)
.SetID(kHoldingSpaceItemSecondaryChipLabelId)
.SetStyle(TypographyToken::kCrosLabel1)
.SetElideBehavior(gfx::FADE_TAIL)
.SetCallback(paint_label_mask_callback))
.AfterBuild(base::BindOnce(
[](HoldingSpaceItemChipView* self,
views::BoxLayoutView* box_layout_view) {
self->secondary_label_->SetLineHeight(
self->primary_label_->GetLineHeight());
},
base::Unretained(this))))
.AddChild(views::Builder<views::BoxLayoutView>()
.SetOrientation(Orientation::kHorizontal)
.SetMainAxisAlignment(MainAxisAlignment::kEnd)
.SetCrossAxisAlignment(CrossAxisAlignment::kCenter)
.AddChild(CreatePrimaryActionBuilder())))
.BuildChildren();
image_skia_changed_subscription_ =
item->image().AddImageSkiaChangedCallback(base::BindRepeating(
&HoldingSpaceItemChipView::UpdateImage, base::Unretained(this)));
progress_ring_animation_changed_subscription_ =
HoldingSpaceAnimationRegistry::GetInstance()
->AddProgressRingAnimationChangedCallbackForKey(
ProgressIndicatorAnimationRegistry::AsAnimationKey(item),
base::IgnoreArgs<ProgressRingAnimation*>(base::BindRepeating(
&HoldingSpaceItemChipView::UpdateImageTransform,
base::Unretained(this))));
tooltip_text_dependency_changed_subscriptions_.reserve(4);
tooltip_text_dependency_changed_subscriptions_.push_back(
primary_label_->AddTextChangedCallback(base::BindRepeating(
&HoldingSpaceItemChipView::ScheduleUpdateTooltipText,
base::Unretained(this))));
tooltip_text_dependency_changed_subscriptions_.push_back(
primary_label_->AddTooltipTextChangedCallback(base::BindRepeating(
&HoldingSpaceItemChipView::ScheduleUpdateTooltipText,
base::Unretained(this))));
tooltip_text_dependency_changed_subscriptions_.push_back(
secondary_label_->AddTextChangedCallback(base::BindRepeating(
&HoldingSpaceItemChipView::ScheduleUpdateTooltipText,
base::Unretained(this))));
tooltip_text_dependency_changed_subscriptions_.push_back(
secondary_label_->AddTooltipTextChangedCallback(base::BindRepeating(
&HoldingSpaceItemChipView::ScheduleUpdateTooltipText,
base::Unretained(this))));
UpdateImage();
UpdateImageAndProgressIndicatorVisibility();
UpdateLabels();
}
HoldingSpaceItemChipView::~HoldingSpaceItemChipView() = default;
views::View* HoldingSpaceItemChipView::GetTooltipHandlerForPoint(
const gfx::Point& point) {
return HitTestPoint(point) ? this : nullptr;
}
void HoldingSpaceItemChipView::OnHoldingSpaceItemUpdated(
const HoldingSpaceItem* item,
const HoldingSpaceItemUpdatedFields& updated_fields) {
HoldingSpaceItemView::OnHoldingSpaceItemUpdated(item, updated_fields);
if (this->item() == item) {
UpdateImage();
UpdateLabels();
UpdateSecondaryAction();
}
}
void HoldingSpaceItemChipView::OnPrimaryActionVisibilityChanged(bool visible) {
primary_label_->SchedulePaint();
secondary_label_->SchedulePaint();
}
void HoldingSpaceItemChipView::OnSelectionUiChanged() {
HoldingSpaceItemView::OnSelectionUiChanged();
UpdateLabels();
UpdateSecondaryAction();
}
void HoldingSpaceItemChipView::OnMouseEvent(ui::MouseEvent* event) {
HoldingSpaceItemView::OnMouseEvent(event);
switch (event->type()) {
case ui::EventType::kMouseEntered:
case ui::EventType::kMouseExited:
UpdateSecondaryAction();
break;
default:
break;
}
}
void HoldingSpaceItemChipView::OnThemeChanged() {
HoldingSpaceItemView::OnThemeChanged();
UpdateImage();
UpdateLabels();
}
void HoldingSpaceItemChipView::OnPaintLabelMask(views::Label* label,
gfx::Canvas* canvas) {
if (!primary_action_container()->GetVisible())
return;
gfx::Point gradient_start, gradient_end;
if (base::i18n::IsRTL()) {
gradient_end.set_x(primary_action_container()->width());
gradient_start.set_x(gradient_end.x() + kLabelMaskGradientWidth);
} else {
gradient_end.set_x(label->width() - primary_action_container()->width());
gradient_start.set_x(gradient_end.x() - kLabelMaskGradientWidth);
}
cc::PaintFlags flags;
flags.setAntiAlias(true);
flags.setBlendMode(SkBlendMode::kDstIn);
flags.setShader(gfx::CreateGradientShader(
gradient_start, gradient_end, SK_ColorBLACK, SK_ColorTRANSPARENT));
canvas->DrawRect(label->GetLocalBounds(), flags);
}
void HoldingSpaceItemChipView::OnSecondaryActionPressed() {
if (!item())
return;
DCHECK_NE(secondary_action_pause_->GetVisible(),
secondary_action_resume_->GetVisible());
if (delegate())
delegate()->OnHoldingSpaceItemViewSecondaryActionPressed(this);
const HoldingSpaceCommandId command_id =
secondary_action_pause_->GetVisible()
? HoldingSpaceCommandId::kPauseItem
: HoldingSpaceCommandId::kResumeItem;
const bool success =
holding_space_util::ExecuteInProgressCommand(item(), command_id);
CHECK(success);
}
void HoldingSpaceItemChipView::ScheduleUpdateTooltipText() {
if (!update_tooltip_text_scheduler_.IsRunning()) {
update_tooltip_text_scheduler_.Start(
FROM_HERE, base::TimeDelta(),
base::BindOnce(&HoldingSpaceItemChipView::UpdateTooltipText,
base::Unretained(this)));
}
}
void HoldingSpaceItemChipView::UpdateImage() {
if (!item())
return;
image_->SetImage(item()->image().GetImageSkia(
gfx::Size(kHoldingSpaceChipIconSize, kHoldingSpaceChipIconSize),
DarkLightModeControllerImpl::Get()
->IsDarkModeEnabled()));
SchedulePaint();
UpdateImageTransform();
}
void HoldingSpaceItemChipView::UpdateImageAndProgressIndicatorVisibility() {
if (!item())
return;
const bool is_secondary_action_visible =
secondary_action_container_->GetVisible();
const bool is_progress_indicator_inner_icon_visible =
!is_secondary_action_visible && !checkmark()->GetVisible();
bool is_image_visible = is_progress_indicator_inner_icon_visible;
const HoldingSpaceProgress& progress = item()->progress();
is_image_visible &= progress.IsHidden() || progress.IsComplete();
image_->SetVisible(is_image_visible);
progress_indicator_->SetInnerIconVisible(
is_progress_indicator_inner_icon_visible);
}
void HoldingSpaceItemChipView::UpdateImageTransform() {
if (!item())
return;
if (image_->bounds().IsEmpty())
return;
const bool is_item_visibly_in_progress =
!item()->progress().IsHidden() && !item()->progress().IsComplete();
const ProgressRingAnimation* progress_ring_animation =
HoldingSpaceAnimationRegistry::GetInstance()
->GetProgressRingAnimationForKey(
ProgressIndicatorAnimationRegistry::AsAnimationKey(item()));
gfx::Transform transform;
if (is_item_visibly_in_progress || progress_ring_animation) {
transform = gfx::GetScaleTransform(image_->bounds().CenterPoint(),
kInProgressImageScaleFactor);
}
if (!image_->layer()) {
image_->SetPaintToLayer();
image_->layer()->SetFillsBoundsOpaquely(false);
}
if (image_->layer()->GetTargetTransform() == transform)
return;
if (!transform.IsIdentity()) {
image_->layer()->SetTransform(transform);
return;
}
ui::ScopedLayerAnimationSettings settings(image_->layer()->GetAnimator());
settings.SetTransitionDuration(kInProgressImageScaleDuration);
settings.SetTweenType(gfx::Tween::Type::LINEAR_OUT_SLOW_IN);
image_->layer()->SetTransform(transform);
}
void HoldingSpaceItemChipView::UpdateLabels() {
if (!item())
return;
const bool multiselect =
delegate() && delegate()->selection_ui() ==
HoldingSpaceViewDelegate::SelectionUi::kMultiSelect;
primary_label_->SetText(item()->GetText());
primary_label_->SetEnabledColor(selected() && multiselect
? kColorAshMultiSelectTextColor
: kColorAshTextColorPrimary);
secondary_label_->SetText(
item()->secondary_text().value_or(std::u16string()));
if (selected() && multiselect) {
secondary_label_->SetEnabledColor(kColorAshMultiSelectTextColor);
} else if (const std::optional<HoldingSpaceColorVariant>& color_variant =
item()->secondary_text_color_variant()) {
std::visit(absl::Overload{
[&](const ui::ColorId& color_id) {
secondary_label_->SetEnabledColor(color_id);
},
[&](const HoldingSpaceColors& colors) {
secondary_label_->SetEnabledColor(
DarkLightModeControllerImpl::Get()->IsDarkModeEnabled()
? colors.dark_mode()
: colors.light_mode());
},
},
*color_variant);
} else {
secondary_label_->SetEnabledColor(kColorAshTextColorSecondary);
}
secondary_label_->SetVisible(!secondary_label_->GetText().empty());
}
void HoldingSpaceItemChipView::UpdateSecondaryAction() {
if (!item())
return;
const bool has_secondary_action =
!checkmark()->GetVisible() && !item()->progress().IsComplete() &&
(holding_space_util::SupportsInProgressCommand(
item(), HoldingSpaceCommandId::kPauseItem) ||
holding_space_util::SupportsInProgressCommand(
item(), HoldingSpaceCommandId::kResumeItem)) &&
IsMouseHovered();
if (!has_secondary_action) {
secondary_action_container_->SetVisible(false);
UpdateImageAndProgressIndicatorVisibility();
return;
}
const bool is_item_paused = holding_space_util::SupportsInProgressCommand(
item(), HoldingSpaceCommandId::kResumeItem);
secondary_action_pause_->SetVisible(!is_item_paused);
secondary_action_resume_->SetVisible(is_item_paused);
secondary_action_container_->SetVisible(true);
UpdateImageAndProgressIndicatorVisibility();
}
void HoldingSpaceItemChipView::UpdateTooltipText() {
std::u16string primary_tooltip = primary_label_->GetTooltipText();
std::u16string secondary_tooltip = secondary_label_->GetTooltipText();
if (primary_tooltip.empty() && secondary_tooltip.empty()) {
SetTooltipText(std::u16string());
return;
}
if (primary_tooltip.empty()) {
primary_tooltip = primary_label_->GetText();
}
if (secondary_tooltip.empty()) {
secondary_tooltip = secondary_label_->GetText();
}
if (secondary_tooltip.empty()) {
SetTooltipText(primary_tooltip);
return;
}
SetTooltipText(l10n_util::GetStringFUTF16(
IDS_ASH_HOLDING_SPACE_ITEM_A11Y_NAME_AND_TOOLTIP, primary_tooltip,
secondary_tooltip));
}
BEGIN_METADATA(HoldingSpaceItemChipView)
END_METADATA
}