blob: affe2c7e2579345b591b3ab70d30ce4fb96a5760 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/views/controls/button/checkbox.h"
#include <stddef.h>
#include <utility>
#include "ui/accessibility/ax_node_data.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/color_palette.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/native_theme/native_theme.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/animation/ink_drop_impl.h"
#include "ui/views/animation/ink_drop_mask.h"
#include "ui/views/animation/ink_drop_ripple.h"
#include "ui/views/controls/button/label_button_border.h"
#include "ui/views/controls/focus_ring.h"
#include "ui/views/layout/layout_provider.h"
#include "ui/views/metadata/metadata_impl_macros.h"
#include "ui/views/painter.h"
#include "ui/views/resources/grit/views_resources.h"
#include "ui/views/style/platform_style.h"
#include "ui/views/style/typography.h"
#include "ui/views/vector_icons.h"
namespace views {
Checkbox::Checkbox(const base::string16& label, ButtonListener* listener)
: LabelButton(listener, label), checked_(false), label_ax_id_(0) {
SetHorizontalAlignment(gfx::ALIGN_LEFT);
SetFocusForPlatform();
set_request_focus_on_press(false);
SetInkDropMode(InkDropMode::ON);
set_has_ink_drop_action_on_click(true);
// Limit the checkbox height to match the legacy appearance.
const gfx::Size preferred_size(LabelButton::CalculatePreferredSize());
SetMinSize(gfx::Size(0, preferred_size.height() + 4));
// Checkboxes always have a focus ring, even when the platform otherwise
// doesn't generally use them for buttons.
SetInstallFocusRingOnFocus(true);
}
Checkbox::~Checkbox() = default;
void Checkbox::SetChecked(bool checked) {
if (GetChecked() == checked)
return;
checked_ = checked;
NotifyAccessibilityEvent(ax::mojom::Event::kCheckedStateChanged, true);
UpdateImage();
OnPropertyChanged(&checked_, kPropertyEffectsNone);
}
bool Checkbox::GetChecked() const {
return checked_;
}
void Checkbox::SetMultiLine(bool multi_line) {
if (GetMultiLine() == multi_line)
return;
label()->SetMultiLine(multi_line);
// TODO(pkasting): Remove this and forward callback subscriptions to the
// underlying label property when Label is converted to properties.
OnPropertyChanged(this, kPropertyEffectsNone);
}
bool Checkbox::GetMultiLine() const {
return label()->GetMultiLine();
}
void Checkbox::SetAssociatedLabel(View* labelling_view) {
DCHECK(labelling_view);
label_ax_id_ = labelling_view->GetViewAccessibility().GetUniqueId().Get();
ui::AXNodeData node_data;
labelling_view->GetAccessibleNodeData(&node_data);
// TODO(aleventhal) automatically handle setting the name from the related
// label in ViewAccessibility and have it update the name if the text of the
// associated label changes.
SetAccessibleName(
node_data.GetString16Attribute(ax::mojom::StringAttribute::kName));
}
void Checkbox::GetAccessibleNodeData(ui::AXNodeData* node_data) {
LabelButton::GetAccessibleNodeData(node_data);
node_data->role = ax::mojom::Role::kCheckBox;
const ax::mojom::CheckedState checked_state =
GetChecked() ? ax::mojom::CheckedState::kTrue
: ax::mojom::CheckedState::kFalse;
node_data->SetCheckedState(checked_state);
if (GetEnabled()) {
node_data->SetDefaultActionVerb(GetChecked()
? ax::mojom::DefaultActionVerb::kUncheck
: ax::mojom::DefaultActionVerb::kCheck);
}
if (label_ax_id_) {
node_data->AddIntListAttribute(ax::mojom::IntListAttribute::kLabelledbyIds,
{label_ax_id_});
}
}
void Checkbox::OnThemeChanged() {
LabelButton::OnThemeChanged();
UpdateImage();
}
std::unique_ptr<InkDrop> Checkbox::CreateInkDrop() {
std::unique_ptr<InkDropImpl> ink_drop = CreateDefaultInkDropImpl();
ink_drop->SetShowHighlightOnHover(false);
ink_drop->SetAutoHighlightMode(InkDropImpl::AutoHighlightMode::NONE);
return ink_drop;
}
std::unique_ptr<InkDropRipple> Checkbox::CreateInkDropRipple() const {
// The "small" size is 21dp, the large size is 1.33 * 21dp = 28dp.
return CreateSquareInkDropRipple(image()->GetMirroredBounds().CenterPoint(),
gfx::Size(21, 21));
}
std::unique_ptr<InkDropMask> Checkbox::CreateInkDropMask() const {
// Avoid the default ink-drop mask to allow the ripple effect to extend beyond
// the checkbox view (otherwise it gets clipped which looks weird).
return nullptr;
}
SkColor Checkbox::GetInkDropBaseColor() const {
// Usually ink-drop ripples match the text color. Checkboxes use the color of
// the unchecked, enabled icon.
return GetIconImageColor(IconState::ENABLED);
}
gfx::ImageSkia Checkbox::GetImage(ButtonState for_state) const {
int icon_state = 0;
if (GetChecked())
icon_state |= IconState::CHECKED;
if (for_state != STATE_DISABLED)
icon_state |= IconState::ENABLED;
return gfx::CreateVectorIcon(GetVectorIcon(), 16,
GetIconImageColor(icon_state));
}
std::unique_ptr<LabelButtonBorder> Checkbox::CreateDefaultBorder() const {
std::unique_ptr<LabelButtonBorder> border =
LabelButton::CreateDefaultBorder();
border->set_insets(
LayoutProvider::Get()->GetInsetsMetric(INSETS_CHECKBOX_RADIO_BUTTON));
return border;
}
void Checkbox::Layout() {
LabelButton::Layout();
if (focus_ring() && !image()->bounds().IsEmpty())
focus_ring()->SetPath(GetFocusRingPath());
}
SkPath Checkbox::GetFocusRingPath() const {
SkPath path;
gfx::Rect bounds = image()->GetMirroredBounds();
bounds.Inset(1, 1);
path.addRect(RectToSkRect(bounds));
return path;
}
const gfx::VectorIcon& Checkbox::GetVectorIcon() const {
return GetChecked() ? kCheckboxActiveIcon : kCheckboxNormalIcon;
}
SkColor Checkbox::GetIconImageColor(int icon_state) const {
const SkColor active_color = GetNativeTheme()->GetSystemColor(
(icon_state & IconState::CHECKED)
? ui::NativeTheme::kColorId_ProminentButtonColor
: ui::NativeTheme::kColorId_ButtonEnabledColor);
return (icon_state & IconState::ENABLED)
? active_color
: color_utils::BlendTowardMaxContrast(active_color,
gfx::kDisabledControlAlpha);
}
void Checkbox::NotifyClick(const ui::Event& event) {
SetChecked(!GetChecked());
LabelButton::NotifyClick(event);
}
ui::NativeTheme::Part Checkbox::GetThemePart() const {
return ui::NativeTheme::kCheckbox;
}
void Checkbox::GetExtraParams(ui::NativeTheme::ExtraParams* params) const {
LabelButton::GetExtraParams(params);
params->button.checked = GetChecked();
}
BEGIN_METADATA(Checkbox)
METADATA_PARENT_CLASS(LabelButton)
ADD_PROPERTY_METADATA(Checkbox, bool, Checked)
ADD_PROPERTY_METADATA(Checkbox, bool, MultiLine)
END_METADATA()
} // namespace views