#include "ash/style/tab_slider_button.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/style_util.h"
#include "ash/style/typography.h"
#include "ui/accessibility/ax_enums.mojom-shared.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
#include "ui/color/color_id.h"
#include "ui/compositor/layer.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/border.h"
#include "ui/views/controls/focus_ring.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/layout/box_layout.h"
namespace ash {
namespace {
constexpr ui::ColorId kSelectedColorId =
cros_tokens::kCrosSysSystemOnPrimaryContainer;
constexpr ui::ColorId kDisabledSelectedColorId =
cros_tokens::kCrosSysSystemOnPrimaryContainerDisabled;
constexpr ui::ColorId kUnselectedColorId = cros_tokens::kCrosSysOnSurface;
constexpr ui::ColorId kDisabledUnselectedColorId =
cros_tokens::kCrosSysDisabled;
constexpr int kFocusRingPadding = 2;
constexpr int kIconButtonSize = 32;
constexpr int kIconSize = 20;
constexpr int kLabelButtonHeight = 32;
constexpr int kLabelButtonMinWidth = 80;
constexpr gfx::Insets kLabelButtonBorderInsets = gfx::Insets::VH(6, 16);
constexpr gfx::Insets kIconLabelButtonVerticalMargins = gfx::Insets(8);
constexpr gfx::Insets kIconLabelButtonHorizontalMargins =
gfx::Insets::VH(8, 16);
constexpr int kIconLabelSpacing = 6;
}
TabSliderButton::TabSliderButton(PressedCallback callback,
const std::u16string& tooltip_text)
: views::Button(std::move(callback)) {
SetPaintToLayer();
layer()->SetFillsBoundsOpaquely(false);
auto* focus_ring = views::FocusRing::Get(this);
focus_ring->SetOutsetFocusRingDisabled(true);
focus_ring->SetColorId(cros_tokens::kCrosSysFocusRing);
const float halo_inset =
focus_ring->GetHaloThickness() / 2.f + kFocusRingPadding;
focus_ring->SetHaloInset(-halo_inset);
focus_ring->SetPathGenerator(
std::make_unique<views::PillHighlightPathGenerator>());
views::InstallPillHighlightPathGenerator(this);
SetTooltipText(tooltip_text);
GetViewAccessibility().SetRole(ax::mojom::Role::kToggleButton);
GetViewAccessibility().SetCheckedState(selected_
? ax::mojom::CheckedState::kTrue
: ax::mojom::CheckedState::kFalse);
}
TabSliderButton::~TabSliderButton() = default;
void TabSliderButton::AddedToSlider(TabSlider* tab_slider) {
DCHECK(tab_slider);
tab_slider_ = tab_slider;
}
void TabSliderButton::SetSelected(bool selected) {
if (selected_ == selected) {
return;
}
selected_ = selected;
GetViewAccessibility().SetCheckedState(selected_
? ax::mojom::CheckedState::kTrue
: ax::mojom::CheckedState::kFalse);
if (selected_ && tab_slider_) {
tab_slider_->OnButtonSelected(this);
}
OnSelectedChanged();
}
ui::ColorId TabSliderButton::GetColorIdOnButtonState() {
const bool enabled = GetEnabled();
return selected()
? (enabled ? kSelectedColorId : kDisabledSelectedColorId)
: (enabled ? kUnselectedColorId : kDisabledUnselectedColorId);
}
void TabSliderButton::NotifyClick(const ui::Event& event) {
SetSelected(true);
views::Button::NotifyClick(event);
}
BEGIN_METADATA(TabSliderButton)
END_METADATA
IconSliderButton::IconSliderButton(PressedCallback callback,
const gfx::VectorIcon* icon,
const std::u16string& tooltip_text)
: TabSliderButton(std::move(callback), tooltip_text), icon_(icon) {
SetPreferredSize(gfx::Size(kIconButtonSize, kIconButtonSize));
auto* focus_ring = views::FocusRing::Get(this);
focus_ring->SetOutsetFocusRingDisabled(true);
focus_ring->SetPathGenerator(
std::make_unique<views::CircleHighlightPathGenerator>(-gfx::Insets(
focus_ring->GetHaloThickness() / 2 + kFocusRingPadding)));
views::InstallCircleHighlightPathGenerator(this);
}
IconSliderButton::~IconSliderButton() = default;
void IconSliderButton::OnSelectedChanged() {
SchedulePaint();
}
void IconSliderButton::OnThemeChanged() {
views::View::OnThemeChanged();
SchedulePaint();
}
void IconSliderButton::PaintButtonContents(gfx::Canvas* canvas) {
DCHECK(GetWidget());
const gfx::ImageSkia img = gfx::CreateVectorIcon(
*icon_, kIconSize,
GetColorProvider()->GetColor(GetColorIdOnButtonState()));
const int origin_offset = (kIconButtonSize - kIconSize) / 2;
canvas->DrawImageInt(img, origin_offset, origin_offset);
}
BEGIN_METADATA(IconSliderButton)
END_METADATA
LabelSliderButton::LabelSliderButton(PressedCallback callback,
const std::u16string& text,
const std::u16string& tooltip_text)
: TabSliderButton(std::move(callback), tooltip_text),
label_(AddChildView(std::make_unique<views::Label>(text))) {
SetBorder(views::CreateEmptyBorder(kLabelButtonBorderInsets));
SetUseDefaultFillLayout(true);
label_->SetAutoColorReadabilityEnabled(false);
TypographyProvider::Get()->StyleLabel(TypographyToken::kCrosButton2, *label_);
if (tooltip_text.empty()) {
SetTooltipText(text);
}
}
LabelSliderButton::~LabelSliderButton() = default;
void LabelSliderButton::UpdateLabelColor() {
label_->SetEnabledColor(GetColorIdOnButtonState());
SchedulePaint();
}
void LabelSliderButton::OnSelectedChanged() {
UpdateLabelColor();
}
gfx::Size LabelSliderButton::CalculatePreferredSize(
const views::SizeBounds& available_size) const {
gfx::Insets insets = GetInsets();
views::SizeBound label_available_width = std::max<views::SizeBound>(
kLabelButtonMinWidth, available_size.width() - insets.width());
return gfx::Size(
std::max(
label_->GetPreferredSize(views::SizeBounds(label_available_width, {}))
.width() +
GetInsets().width(),
kLabelButtonMinWidth),
kLabelButtonHeight);
}
void LabelSliderButton::StateChanged(ButtonState old_state) {
if (old_state != ButtonState::STATE_DISABLED &&
GetState() != ButtonState::STATE_DISABLED) {
return;
}
UpdateLabelColor();
}
BEGIN_METADATA(LabelSliderButton)
END_METADATA
IconLabelSliderButton::IconLabelSliderButton(PressedCallback callback,
const gfx::VectorIcon* icon,
const std::u16string& text,
const std::u16string& tooltip_text,
bool horizontal)
: TabSliderButton(std::move(callback),
tooltip_text.empty() ? text : tooltip_text),
image_view_(AddChildView(std::make_unique<views::ImageView>())),
label_(AddChildView(std::make_unique<views::Label>(text))) {
auto* layout_manager = SetLayoutManager(std::make_unique<views::BoxLayout>(
horizontal ? views::BoxLayout::Orientation::kHorizontal
: views::BoxLayout::Orientation::kVertical,
(horizontal ? kIconLabelButtonHorizontalMargins
: kIconLabelButtonVerticalMargins),
kIconLabelSpacing));
DCHECK(icon);
image_view_->SetImage(ui::ImageModel::FromImageGenerator(
base::BindRepeating(
[](TabSliderButton* tab_slider_button,
const gfx::VectorIcon* vector_icon, const ui::ColorProvider*) {
return gfx::CreateVectorIcon(
*vector_icon, kIconSize,
tab_slider_button->GetColorProvider()->GetColor(
tab_slider_button->GetColorIdOnButtonState()));
},
this, icon),
gfx::Size(kIconSize, kIconSize)));
label_->SetAutoColorReadabilityEnabled(false);
TypographyProvider::Get()->StyleLabel(TypographyToken::kCrosButton2, *label_);
if (horizontal) {
layout_manager->set_main_axis_alignment(
views::BoxLayout::MainAxisAlignment::kCenter);
if (base::i18n::IsRTL()) {
ReorderChildView(label_, 0);
}
}
}
IconLabelSliderButton::~IconLabelSliderButton() = default;
gfx::Size IconLabelSliderButton::CalculatePreferredSize(
const views::SizeBounds& available_size) const {
return {0, GetMinimumSize().height()};
}
void IconLabelSliderButton::UpdateColors() {
label_->SetEnabledColor(GetColorIdOnButtonState());
SchedulePaint();
}
void IconLabelSliderButton::OnSelectedChanged() {
UpdateColors();
}
BEGIN_METADATA(IconLabelSliderButton)
END_METADATA
}